From bcecde140a561c64e297225904afebebd62336ce Mon Sep 17 00:00:00 2001 From: jljusten Date: Mon, 27 Jun 2011 23:32:56 +0000 Subject: [PATCH] IntelFrameworkModulePkg: Add Compatibility Support Module (CSM) drivers Added these drivers: * LegacyBiosDxe * BlockIoDxe * KeyboardDxe * Snp16Dxe * VideoDxe Signed-off-by: jljusten Reviewed-by: mdkinney Reviewed-by: geekboy15a git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11905 6f19259b-4bc3-4df7-8a09-765794883524 --- .../Csm/BiosThunk/BlockIoDxe/BiosBlkIo.c | 782 ++++ .../Csm/BiosThunk/BlockIoDxe/BiosBlkIo.h | 439 +++ .../Csm/BiosThunk/BlockIoDxe/BiosInt13.c | 1485 +++++++ .../Csm/BiosThunk/BlockIoDxe/BlockIoDxe.inf | 58 + .../Csm/BiosThunk/BlockIoDxe/ComponentName.c | 309 ++ .../Csm/BiosThunk/BlockIoDxe/Edd.h | 209 + .../Csm/BiosThunk/KeyboardDxe/BiosKeyboard.c | 2365 +++++++++++ .../Csm/BiosThunk/KeyboardDxe/BiosKeyboard.h | 744 ++++ .../Csm/BiosThunk/KeyboardDxe/ComponentName.c | 183 + .../Csm/BiosThunk/KeyboardDxe/ComponentName.h | 153 + .../Csm/BiosThunk/KeyboardDxe/KeyboardDxe.inf | 68 + .../Csm/BiosThunk/Snp16Dxe/BiosSnp16.c | 3507 +++++++++++++++++ .../Csm/BiosThunk/Snp16Dxe/BiosSnp16.h | 1655 ++++++++ .../Csm/BiosThunk/Snp16Dxe/ComponentName.c | 309 ++ .../Csm/BiosThunk/Snp16Dxe/Misc.c | 956 +++++ .../Csm/BiosThunk/Snp16Dxe/Pxe.h | 613 +++ .../Csm/BiosThunk/Snp16Dxe/PxeUndi.c | 1254 ++++++ .../Csm/BiosThunk/Snp16Dxe/Snp16Dxe.inf | 67 + .../Csm/BiosThunk/VideoDxe/BiosVideo.c | 3164 +++++++++++++++ .../Csm/BiosThunk/VideoDxe/BiosVideo.h | 532 +++ .../Csm/BiosThunk/VideoDxe/ComponentName.c | 313 ++ .../BiosThunk/VideoDxe/VesaBiosExtensions.h | 451 +++ .../Csm/BiosThunk/VideoDxe/VideoDxe.inf | 80 + .../Csm/LegacyBiosDxe/IA32/InterruptTable.S | 67 + .../Csm/LegacyBiosDxe/IA32/InterruptTable.asm | 73 + .../Csm/LegacyBiosDxe/Ipf/IpfBootSupport.c | 277 ++ .../Csm/LegacyBiosDxe/Ipf/IpfThunk.h | 102 + .../Csm/LegacyBiosDxe/Ipf/IpfThunk.i | 89 + .../Csm/LegacyBiosDxe/Ipf/IpfThunk.s | 524 +++ .../Csm/LegacyBiosDxe/Ipf/Thunk.c | 550 +++ .../Csm/LegacyBiosDxe/LegacyBbs.c | 384 ++ .../Csm/LegacyBiosDxe/LegacyBda.c | 66 + .../Csm/LegacyBiosDxe/LegacyBios.c | 1007 +++++ .../Csm/LegacyBiosDxe/LegacyBiosDxe.inf | 143 + .../Csm/LegacyBiosDxe/LegacyBiosInterface.h | 1501 +++++++ .../Csm/LegacyBiosDxe/LegacyBootSupport.c | 2032 ++++++++++ .../Csm/LegacyBiosDxe/LegacyCmos.c | 124 + .../Csm/LegacyBiosDxe/LegacyIde.c | 300 ++ .../Csm/LegacyBiosDxe/LegacyPci.c | 2901 ++++++++++++++ .../Csm/LegacyBiosDxe/LegacySio.c | 234 ++ .../Csm/LegacyBiosDxe/Thunk.c | 284 ++ .../Csm/LegacyBiosDxe/X64/InterruptTable.S | 72 + .../Csm/LegacyBiosDxe/X64/InterruptTable.asm | 71 + .../Include/Guid/LegacyBios.h | 36 + .../IntelFrameworkModulePkg.dec | 26 +- .../IntelFrameworkModulePkg.dsc | 13 + 46 files changed, 30571 insertions(+), 1 deletion(-) create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosBlkIo.c create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosBlkIo.h create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosInt13.c create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BlockIoDxe.inf create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/ComponentName.c create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/Edd.h create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/BiosKeyboard.c create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/BiosKeyboard.h create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/ComponentName.c create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/ComponentName.h create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/KeyboardDxe.inf create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/BiosSnp16.c create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/BiosSnp16.h create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/ComponentName.c create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Misc.c create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Pxe.h create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/PxeUndi.c create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Snp16Dxe.inf create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/BiosVideo.c create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/BiosVideo.h create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/ComponentName.c create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/VesaBiosExtensions.h create mode 100644 IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/VideoDxe.inf create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/IA32/InterruptTable.S create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/IA32/InterruptTable.asm create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfBootSupport.c create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.h create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.i create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.s create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/Thunk.c create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBbs.c create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBda.c create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBios.c create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBootSupport.c create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyCmos.c create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyIde.c create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyPci.c create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacySio.c create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Thunk.c create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/X64/InterruptTable.S create mode 100644 IntelFrameworkModulePkg/Csm/LegacyBiosDxe/X64/InterruptTable.asm create mode 100644 IntelFrameworkModulePkg/Include/Guid/LegacyBios.h diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosBlkIo.c b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosBlkIo.c new file mode 100644 index 0000000000..309cf1a9ed --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosBlkIo.c @@ -0,0 +1,782 @@ +/** @file + EFI glue for BIOS INT 13h block devices. + + This file is coded to EDD 3.0 as defined by T13 D1386 Revision 4 + Availible on http://www.t13.org/#Project drafts + Currently at ftp://fission.dt.wdc.com/pub/standards/x3t13/project/d1386r4.pdf + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "BiosBlkIo.h" + +// +// Global data declaration +// +// +// EFI Driver Binding Protocol Instance +// +EFI_DRIVER_BINDING_PROTOCOL gBiosBlockIoDriverBinding = { + BiosBlockIoDriverBindingSupported, + BiosBlockIoDriverBindingStart, + BiosBlockIoDriverBindingStop, + 0x3, + NULL, + NULL +}; + +// +// Semaphore to control access to global variables mActiveInstances and mBufferUnder1Mb +// +EFI_LOCK mGlobalDataLock = EFI_INITIALIZE_LOCK_VARIABLE(TPL_APPLICATION); + +// +// Number of active instances of this protocol. This is used to allocate/free +// the shared buffer. You must acquire the semaphore to modify. +// +UINTN mActiveInstances = 0; + +// +// Pointer to the beginning of the buffer used for real mode thunk +// You must acquire the semaphore to modify. +// +EFI_PHYSICAL_ADDRESS mBufferUnder1Mb = 0; + +// +// Address packet is a buffer under 1 MB for all version EDD calls +// +EDD_DEVICE_ADDRESS_PACKET *mEddBufferUnder1Mb; + +// +// This is a buffer for INT 13h func 48 information +// +BIOS_LEGACY_DRIVE *mLegacyDriverUnder1Mb; + +// +// Buffer of 0xFE00 bytes for EDD 1.1 transfer must be under 1 MB +// 0xFE00 bytes is the max transfer size supported. +// +VOID *mEdd11Buffer; + +EFI_GUID mUnknownDevGuid = UNKNOWN_DEVICE_GUID; + +/** + Driver entry point. + + @param ImageHandle Handle of driver image. + @param SystemTable Pointer to system table. + + @retval EFI_SUCCESS Entrypoint successfully executed. + @retval Others Fail to execute entrypoint. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install protocols + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gBiosBlockIoDriverBinding, + ImageHandle, + &gBiosBlockIoComponentName, + &gBiosBlockIoComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Install Legacy BIOS GUID to mark this driver as a BIOS Thunk Driver + // + return gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiLegacyBiosGuid, + NULL, + NULL + ); +} + +/** + Check whether the driver supports this device. + + @param This The Udriver binding protocol. + @param Controller The controller handle to check. + @param RemainingDevicePath The remaining device path. + + @retval EFI_SUCCESS The driver supports this controller. + @retval other This device isn't supported. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + PCI_TYPE00 Pci; + + // + // See if the Legacy BIOS Protocol is available + // + Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // See if this is a PCI VGA Controller by looking at the Command register and + // Class Code Register + // + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0, sizeof (Pci) / sizeof (UINT32), &Pci); + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + Status = EFI_UNSUPPORTED; + if (Pci.Hdr.ClassCode[2] == PCI_CLASS_MASS_STORAGE || + (Pci.Hdr.ClassCode[2] == PCI_BASE_CLASS_INTELLIGENT && Pci.Hdr.ClassCode[1] == PCI_SUB_CLASS_INTELLIGENT) + ) { + Status = EFI_SUCCESS; + } + +Done: + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + +/** + Starts the device with this driver. + + @param This The driver binding instance. + @param Controller Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The controller is controlled by the driver. + @retval Other This controller cannot be started. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT8 DiskStart; + UINT8 DiskEnd; + BIOS_BLOCK_IO_DEV *BiosBlockIoPrivate; + EFI_DEVICE_PATH_PROTOCOL *PciDevPath; + UINTN Index; + UINTN Flags; + UINTN TmpAddress; + BOOLEAN DeviceEnable; + + // + // Initialize variables + // + PciIo = NULL; + PciDevPath = NULL; + + DeviceEnable = FALSE; + + // + // See if the Legacy BIOS Protocol is available + // + Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); + if (EFI_ERROR (Status)) { + goto Error; + } + // + // Open the IO Abstraction(s) needed + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &PciDevPath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto Error; + } + // + // Enable the device and make sure VGA cycles are being forwarded to this VGA device + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_DEVICE_ENABLE, + NULL + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + DeviceEnable = TRUE; + + // + // Check to see if there is a legacy option ROM image associated with this PCI device + // + Status = LegacyBios->CheckPciRom ( + LegacyBios, + Controller, + NULL, + NULL, + &Flags + ); + if (EFI_ERROR (Status)) { + goto Error; + } + // + // Post the legacy option ROM if it is available. + // + Status = LegacyBios->InstallPciRom ( + LegacyBios, + Controller, + NULL, + &Flags, + &DiskStart, + &DiskEnd, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + goto Error; + } + // + // All instances share a buffer under 1MB to put real mode thunk code in + // If it has not been allocated, then we allocate it. + // + if (mBufferUnder1Mb == 0) { + // + // Should only be here if there are no active instances + // + ASSERT (mActiveInstances == 0); + + // + // Acquire the lock + // + EfiAcquireLock (&mGlobalDataLock); + + // + // Allocate below 1MB + // + mBufferUnder1Mb = 0x00000000000FFFFF; + Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData, BLOCK_IO_BUFFER_PAGE_SIZE, &mBufferUnder1Mb); + + // + // Release the lock + // + EfiReleaseLock (&mGlobalDataLock); + + // + // Check memory allocation success + // + if (EFI_ERROR (Status)) { + // + // In checked builds we want to assert if the allocate failed. + // + ASSERT_EFI_ERROR (Status); + Status = EFI_OUT_OF_RESOURCES; + mBufferUnder1Mb = 0; + goto Error; + } + + TmpAddress = (UINTN) mBufferUnder1Mb; + // + // Adjusting the value to be on proper boundary + // + mEdd11Buffer = (VOID *) ALIGN_VARIABLE (TmpAddress); + + TmpAddress = (UINTN) mEdd11Buffer + MAX_EDD11_XFER; + // + // Adjusting the value to be on proper boundary + // + mLegacyDriverUnder1Mb = (BIOS_LEGACY_DRIVE *) ALIGN_VARIABLE (TmpAddress); + + TmpAddress = (UINTN) mLegacyDriverUnder1Mb + sizeof (BIOS_LEGACY_DRIVE); + // + // Adjusting the value to be on proper boundary + // + mEddBufferUnder1Mb = (EDD_DEVICE_ADDRESS_PACKET *) ALIGN_VARIABLE (TmpAddress); + } + // + // Allocate the private device structure for each disk + // + for (Index = DiskStart; Index < DiskEnd; Index++) { + + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (BIOS_BLOCK_IO_DEV), + (VOID **) &BiosBlockIoPrivate + ); + if (EFI_ERROR (Status)) { + goto Error; + } + // + // Zero the private device structure + // + ZeroMem (BiosBlockIoPrivate, sizeof (BIOS_BLOCK_IO_DEV)); + + // + // Initialize the private device structure + // + BiosBlockIoPrivate->Signature = BIOS_CONSOLE_BLOCK_IO_DEV_SIGNATURE; + BiosBlockIoPrivate->ControllerHandle = Controller; + BiosBlockIoPrivate->LegacyBios = LegacyBios; + BiosBlockIoPrivate->PciIo = PciIo; + + BiosBlockIoPrivate->Bios.Floppy = FALSE; + BiosBlockIoPrivate->Bios.Number = (UINT8) Index; + BiosBlockIoPrivate->Bios.Letter = (UINT8) (Index - 0x80 + 'C'); + BiosBlockIoPrivate->BlockMedia.RemovableMedia = FALSE; + + if (BiosInitBlockIo (BiosBlockIoPrivate)) { + SetBiosInitBlockIoDevicePath (PciDevPath, &BiosBlockIoPrivate->Bios, &BiosBlockIoPrivate->DevicePath); + + // + // Install the Block Io Protocol onto a new child handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &BiosBlockIoPrivate->Handle, + &gEfiBlockIoProtocolGuid, + &BiosBlockIoPrivate->BlockIo, + &gEfiDevicePathProtocolGuid, + BiosBlockIoPrivate->DevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + gBS->FreePool (BiosBlockIoPrivate); + } + // + // Open For Child Device + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &BiosBlockIoPrivate->PciIo, + This->DriverBindingHandle, + BiosBlockIoPrivate->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + } else { + gBS->FreePool (BiosBlockIoPrivate); + } + } + +Error: + if (EFI_ERROR (Status)) { + if (PciIo != NULL) { + if (DeviceEnable) { + PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationDisable, + EFI_PCI_DEVICE_ENABLE, + NULL + ); + } + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + if (PciDevPath != NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + if (mBufferUnder1Mb != 0 && mActiveInstances == 0) { + gBS->FreePages (mBufferUnder1Mb, BLOCK_IO_BUFFER_PAGE_SIZE); + + // + // Clear the buffer back to 0 + // + EfiAcquireLock (&mGlobalDataLock); + mBufferUnder1Mb = 0; + EfiReleaseLock (&mGlobalDataLock); + } + } + } else { + // + // Successfully installed, so increment the number of active instances + // + EfiAcquireLock (&mGlobalDataLock); + mActiveInstances++; + EfiReleaseLock (&mGlobalDataLock); + } + + return Status; +} + +/** + Stop the device handled by this driver. + + @param This The driver binding protocol. + @param Controller The controller to release. + @param NumberOfChildren The number of handles in ChildHandleBuffer. + @param ChildHandleBuffer The array of child handle. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + @retval Others Fail to uninstall protocols attached on the device. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + BOOLEAN AllChildrenStopped; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + BIOS_BLOCK_IO_DEV *BiosBlockIoPrivate; + UINTN Index; + + // + // Decrement the number of active instances + // + if (mActiveInstances != 0) { + // + // Add a check since the stop function will be called 2 times for each handle + // + EfiAcquireLock (&mGlobalDataLock); + mActiveInstances--; + EfiReleaseLock (&mGlobalDataLock); + } + + if ((mActiveInstances == 0) && (mBufferUnder1Mb != 0)) { + // + // Free our global buffer + // + Status = gBS->FreePages (mBufferUnder1Mb, BLOCK_IO_BUFFER_PAGE_SIZE); + ASSERT_EFI_ERROR (Status); + + EfiAcquireLock (&mGlobalDataLock); + mBufferUnder1Mb = 0; + EfiReleaseLock (&mGlobalDataLock); + } + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + BiosBlockIoPrivate = BIOS_BLOCK_IO_FROM_THIS (BlockIo); + + // + // Release PCI I/O and Block IO Protocols on the clild handle. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiBlockIoProtocolGuid, + &BiosBlockIoPrivate->BlockIo, + &gEfiDevicePathProtocolGuid, + BiosBlockIoPrivate->DevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + // + // Shutdown the hardware + // + BiosBlockIoPrivate->PciIo->Attributes ( + BiosBlockIoPrivate->PciIo, + EfiPciIoAttributeOperationDisable, + EFI_PCI_DEVICE_ENABLE, + NULL + ); + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + gBS->FreePool (BiosBlockIoPrivate); + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + Status = gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; +} + +/** + Build device path for device. + + @param BaseDevicePath Base device path. + @param Drive Legacy drive. + @param DevicePath Device path for output. + +**/ +VOID +SetBiosInitBlockIoDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *BaseDevicePath, + IN BIOS_LEGACY_DRIVE *Drive, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + EFI_STATUS Status; + UNKNOWN_DEVICE_VENDOR_DEVICE_PATH VendorNode; + + Status = EFI_UNSUPPORTED; + + // + // BugBug: Check for memory leaks! + // + if (Drive->EddVersion == EDD_VERSION_30) { + // + // EDD 3.0 case. + // + Status = BuildEdd30DevicePath (BaseDevicePath, Drive, DevicePath); + } + + if (EFI_ERROR (Status)) { + // + // EDD 1.1 device case or it is unrecognized EDD 3.0 device + // + ZeroMem (&VendorNode, sizeof (VendorNode)); + VendorNode.DevicePath.Header.Type = HARDWARE_DEVICE_PATH; + VendorNode.DevicePath.Header.SubType = HW_VENDOR_DP; + SetDevicePathNodeLength (&VendorNode.DevicePath.Header, sizeof (VendorNode)); + CopyMem (&VendorNode.DevicePath.Guid, &mUnknownDevGuid, sizeof (EFI_GUID)); + VendorNode.LegacyDriveLetter = Drive->Number; + *DevicePath = AppendDevicePathNode (BaseDevicePath, &VendorNode.DevicePath.Header); + } +} + +/** + Build device path for EDD 3.0. + + @param BaseDevicePath Base device path. + @param Drive Legacy drive. + @param DevicePath Device path for output. + + @retval EFI_SUCCESS The device path is built successfully. + @retval EFI_UNSUPPORTED It is failed to built device path. + +**/ +EFI_STATUS +BuildEdd30DevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *BaseDevicePath, + IN BIOS_LEGACY_DRIVE *Drive, + IN EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + // + // AVL UINT64 Address; + // AVL EFI_HANDLE Handle; + // + EFI_DEV_PATH Node; + UINT32 Controller; + + Controller = (UINT32) Drive->Parameters.InterfacePath.Pci.Controller; + + ZeroMem (&Node, sizeof (Node)); + if ((AsciiStrnCmp ("ATAPI", Drive->Parameters.InterfaceType, 5) == 0) || + (AsciiStrnCmp ("ATA", Drive->Parameters.InterfaceType, 3) == 0) + ) { + // + // ATA or ATAPI drive found + // + Node.Atapi.Header.Type = MESSAGING_DEVICE_PATH; + Node.Atapi.Header.SubType = MSG_ATAPI_DP; + SetDevicePathNodeLength (&Node.Atapi.Header, sizeof (ATAPI_DEVICE_PATH)); + Node.Atapi.SlaveMaster = Drive->Parameters.DevicePath.Atapi.Master; + Node.Atapi.Lun = Drive->Parameters.DevicePath.Atapi.Lun; + Node.Atapi.PrimarySecondary = (UINT8) Controller; + } else { + // + // Not an ATA/ATAPI drive + // + if (Controller != 0) { + ZeroMem (&Node, sizeof (Node)); + Node.Controller.Header.Type = HARDWARE_DEVICE_PATH; + Node.Controller.Header.SubType = HW_CONTROLLER_DP; + SetDevicePathNodeLength (&Node.Controller.Header, sizeof (CONTROLLER_DEVICE_PATH)); + Node.Controller.ControllerNumber = Controller; + *DevicePath = AppendDevicePathNode (*DevicePath, &Node.DevPath); + } + + ZeroMem (&Node, sizeof (Node)); + + if (AsciiStrnCmp ("SCSI", Drive->Parameters.InterfaceType, 4) == 0) { + // + // SCSI drive + // + Node.Scsi.Header.Type = MESSAGING_DEVICE_PATH; + Node.Scsi.Header.SubType = MSG_SCSI_DP; + SetDevicePathNodeLength (&Node.Scsi.Header, sizeof (SCSI_DEVICE_PATH)); + + // + // Lun is miss aligned in both EDD and Device Path data structures. + // thus we do a byte copy, to prevent alignment traps on IA-64. + // + CopyMem (&Node.Scsi.Lun, &Drive->Parameters.DevicePath.Scsi.Lun, sizeof (UINT16)); + Node.Scsi.Pun = Drive->Parameters.DevicePath.Scsi.Pun; + + } else if (AsciiStrnCmp ("USB", Drive->Parameters.InterfaceType, 3) == 0) { + // + // USB drive + // + Node.Usb.Header.Type = MESSAGING_DEVICE_PATH; + Node.Usb.Header.SubType = MSG_USB_DP; + SetDevicePathNodeLength (&Node.Usb.Header, sizeof (USB_DEVICE_PATH)); + Node.Usb.ParentPortNumber = (UINT8) Drive->Parameters.DevicePath.Usb.Reserved; + + } else if (AsciiStrnCmp ("1394", Drive->Parameters.InterfaceType, 4) == 0) { + // + // 1394 drive + // + Node.F1394.Header.Type = MESSAGING_DEVICE_PATH; + Node.F1394.Header.SubType = MSG_1394_DP; + SetDevicePathNodeLength (&Node.F1394.Header, sizeof (F1394_DEVICE_PATH)); + Node.F1394.Guid = Drive->Parameters.DevicePath.FireWire.Guid; + + } else if (AsciiStrnCmp ("FIBRE", Drive->Parameters.InterfaceType, 5) == 0) { + // + // Fibre drive + // + Node.FibreChannel.Header.Type = MESSAGING_DEVICE_PATH; + Node.FibreChannel.Header.SubType = MSG_FIBRECHANNEL_DP; + SetDevicePathNodeLength (&Node.FibreChannel.Header, sizeof (FIBRECHANNEL_DEVICE_PATH)); + Node.FibreChannel.WWN = Drive->Parameters.DevicePath.FibreChannel.Wwn; + Node.FibreChannel.Lun = Drive->Parameters.DevicePath.FibreChannel.Lun; + + } else { + DEBUG ( + ( + DEBUG_BLKIO, "It is unrecognized EDD 3.0 device, Drive Number = %x, InterfaceType = %s\n", + Drive->Number, + Drive->Parameters.InterfaceType + ) + ); + } + } + + if (Node.DevPath.Type == 0) { + return EFI_UNSUPPORTED; + } + + *DevicePath = AppendDevicePathNode (BaseDevicePath, &Node.DevPath); + return EFI_SUCCESS; +} diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosBlkIo.h b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosBlkIo.h new file mode 100644 index 0000000000..de3c8d3518 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosBlkIo.h @@ -0,0 +1,439 @@ +/** @file + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _BIOS_BLOCK_IO_H_ +#define _BIOS_BLOCK_IO_H_ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Edd.h" + +#define UNKNOWN_DEVICE_GUID \ + { 0xcf31fac5, 0xc24e, 0x11d2, {0x85, 0xf3, 0x0, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b} } + +typedef struct { + VENDOR_DEVICE_PATH DevicePath; + UINT8 LegacyDriveLetter; +} UNKNOWN_DEVICE_VENDOR_DEVICE_PATH; + +// +// Global Variables +// +extern EFI_COMPONENT_NAME_PROTOCOL gBiosBlockIoComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gBiosBlockIoComponentName2; + + +// +// Define the I2O class code +// +#define PCI_BASE_CLASS_INTELLIGENT 0x0e +#define PCI_SUB_CLASS_INTELLIGENT 0x00 + +// +// Number of pages needed for our buffer under 1MB +// +#define BLOCK_IO_BUFFER_PAGE_SIZE (((sizeof (EDD_DEVICE_ADDRESS_PACKET) + sizeof (BIOS_LEGACY_DRIVE) + MAX_EDD11_XFER) / EFI_PAGE_SIZE) + 1 \ + ) + +// +// Driver Binding Protocol functions +// + +/** + Check whether the driver supports this device. + + @param This The Udriver binding protocol. + @param Controller The controller handle to check. + @param RemainingDevicePath The remaining device path. + + @retval EFI_SUCCESS The driver supports this controller. + @retval other This device isn't supported. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + + +/** + Starts the device with this driver. + + @param This The driver binding instance. + @param Controller Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The controller is controlled by the driver. + @retval Other This controller cannot be started. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop the device handled by this driver. + + @param This The driver binding protocol. + @param Controller The controller to release. + @param NumberOfChildren The number of handles in ChildHandleBuffer. + @param ChildHandleBuffer The array of child handle. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + @retval Others Fail to uninstall protocols attached on the device. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// Other internal functions +// + +/** + Build device path for EDD 3.0. + + @param BaseDevicePath Base device path. + @param Drive Legacy drive. + @param DevicePath Device path for output. + + @retval EFI_SUCCESS The device path is built successfully. + @retval EFI_UNSUPPORTED It is failed to built device path. + +**/ +EFI_STATUS +BuildEdd30DevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *BaseDevicePath, + IN BIOS_LEGACY_DRIVE *Drive, + IN EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +/** + Initialize block I/O device instance + + @param Dev Instance of block I/O device instance + + @retval TRUE Initialization succeeds. + @retval FALSE Initialization fails. + +**/ +BOOLEAN +BiosInitBlockIo ( + IN BIOS_BLOCK_IO_DEV *Dev + ); + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +Edd30BiosReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +Edd30BiosWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writting back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ); + +/** + Reset the Block Device. + + @param This Indicates a pointer to the calling context. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +Edd11BiosReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +Edd11BiosWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +BiosReadLegacyDrive ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +BiosWriteLegacyDrive ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Gets parameters of block I/O device. + + @param BiosBlockIoDev Instance of block I/O device. + @param Drive Legacy drive. + + @return Result of device parameter retrieval. + +**/ +UINTN +Int13GetDeviceParameters ( + IN BIOS_BLOCK_IO_DEV *BiosBlockIoDev, + IN BIOS_LEGACY_DRIVE *Drive + ); + +/** + Extension of INT13 call. + + @param BiosBlockIoDev Instance of block I/O device. + @param Drive Legacy drive. + + @return Result of this extension. + +**/ +UINTN +Int13Extensions ( + IN BIOS_BLOCK_IO_DEV *BiosBlockIoDev, + IN BIOS_LEGACY_DRIVE *Drive + ); + +/** + Gets parameters of legacy drive. + + @param BiosBlockIoDev Instance of block I/O device. + @param Drive Legacy drive. + + @return Result of drive parameter retrieval. + +**/ +UINTN +GetDriveParameters ( + IN BIOS_BLOCK_IO_DEV *BiosBlockIoDev, + IN BIOS_LEGACY_DRIVE *Drive + ); + +/** + Build device path for device. + + @param BaseDevicePath Base device path. + @param Drive Legacy drive. + @param DevicePath Device path for output. + +**/ +VOID +SetBiosInitBlockIoDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *BaseDevicePath, + IN BIOS_LEGACY_DRIVE *Drive, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +#endif diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosInt13.c b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosInt13.c new file mode 100644 index 0000000000..c53490b0e3 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosInt13.c @@ -0,0 +1,1485 @@ +/** @file + Routines that use BIOS to support INT 13 devices. + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "BiosBlkIo.h" + +// +// Module global variables +// +// +// Address packet is a buffer under 1 MB for all version EDD calls +// +extern EDD_DEVICE_ADDRESS_PACKET *mEddBufferUnder1Mb; + +// +// This is a buffer for INT 13h func 48 information +// +extern BIOS_LEGACY_DRIVE *mLegacyDriverUnder1Mb; + +// +// Buffer of 0xFE00 bytes for EDD 1.1 transfer must be under 1 MB +// 0xFE00 bytes is the max transfer size supported. +// +extern VOID *mEdd11Buffer; + + +/** + Initialize block I/O device instance + + @param Dev Instance of block I/O device instance + + @retval TRUE Initialization succeeds. + @retval FALSE Initialization fails. + +**/ +BOOLEAN +BiosInitBlockIo ( + IN BIOS_BLOCK_IO_DEV *Dev + ) +{ + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_BLOCK_IO_MEDIA *BlockMedia; + BIOS_LEGACY_DRIVE *Bios; + + BlockIo = &Dev->BlockIo; + BlockIo->Media = &Dev->BlockMedia; + BlockMedia = BlockIo->Media; + Bios = &Dev->Bios; + + if (Int13GetDeviceParameters (Dev, Bios) != 0) { + if (Int13Extensions (Dev, Bios) != 0) { + BlockMedia->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; + BlockMedia->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; + + if ((Bios->Parameters.Flags & EDD_DEVICE_REMOVABLE) == EDD_DEVICE_REMOVABLE) { + BlockMedia->RemovableMedia = TRUE; + } + + } else { + // + // Legacy Interfaces + // + BlockMedia->BlockSize = 512; + BlockMedia->LastBlock = (Bios->MaxHead + 1) * Bios->MaxSector * (Bios->MaxCylinder + 1) - 1; + } + + DEBUG ((DEBUG_INIT, "BlockSize = %d LastBlock = %d\n", BlockMedia->BlockSize, BlockMedia->LastBlock)); + + BlockMedia->LogicalPartition = FALSE; + BlockMedia->WriteCaching = FALSE; + + // + // BugBug: Need to set this for removable media devices if they do not + // have media present + // + BlockMedia->ReadOnly = FALSE; + BlockMedia->MediaPresent = TRUE; + + BlockIo->Reset = BiosBlockIoReset; + BlockIo->FlushBlocks = BiosBlockIoFlushBlocks; + + if (!Bios->ExtendedInt13) { + // + // Legacy interfaces + // + BlockIo->ReadBlocks = BiosReadLegacyDrive; + BlockIo->WriteBlocks = BiosWriteLegacyDrive; + } else if ((Bios->EddVersion == EDD_VERSION_30) && (Bios->Extensions64Bit)) { + // + // EDD 3.0 Required for Device path, but extended reads are not required. + // + BlockIo->ReadBlocks = Edd30BiosReadBlocks; + BlockIo->WriteBlocks = Edd30BiosWriteBlocks; + } else { + // + // Assume EDD 1.1 - Read and Write functions. + // This could be EDD 3.0 without Extensions64Bit being set. + // If it's EDD 1.1 this will work, but the device path will not + // be correct. This will cause confusion to EFI OS installation. + // + BlockIo->ReadBlocks = Edd11BiosReadBlocks; + BlockIo->WriteBlocks = Edd11BiosWriteBlocks; + } + + BlockMedia->LogicalPartition = FALSE; + BlockMedia->WriteCaching = FALSE; + + return TRUE; + } + + return FALSE; +} + +/** + Gets parameters of block I/O device. + + @param BiosBlockIoDev Instance of block I/O device. + @param Drive Legacy drive. + + @return Result of device parameter retrieval. + +**/ +UINTN +Int13GetDeviceParameters ( + IN BIOS_BLOCK_IO_DEV *BiosBlockIoDev, + IN BIOS_LEGACY_DRIVE *Drive + ) +{ + UINTN CarryFlag; + UINT16 Cylinder; + EFI_IA32_REGISTER_SET Regs; + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + + Regs.H.AH = 0x08; + Regs.H.DL = Drive->Number; + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ((DEBUG_INIT, "Int13GetDeviceParameters: INT 13 08 DL=%02x : CF=%d AH=%02x\n", Drive->Number, CarryFlag, Regs.H.AH)); + if (CarryFlag != 0 || Regs.H.AH != 0x00) { + Drive->ErrorCode = Regs.H.AH; + return FALSE; + } + + if (Drive->Floppy) { + if (Regs.H.BL == 0x10) { + Drive->AtapiFloppy = TRUE; + } else { + Drive->MaxHead = Regs.H.DH; + Drive->MaxSector = Regs.H.CL; + Drive->MaxCylinder = Regs.H.CH; + if (Drive->MaxSector == 0) { + // + // BugBug: You can not trust the Carry flag. + // + return FALSE; + } + } + } else { + Drive->MaxHead = (UINT8) (Regs.H.DH & 0x3f); + Cylinder = (UINT16) (((UINT16) Regs.H.DH & 0xc0) << 4); + Cylinder = (UINT16) (Cylinder | ((UINT16) Regs.H.CL & 0xc0) << 2); + Drive->MaxCylinder = (UINT16) (Cylinder + Regs.H.CH); + Drive->MaxSector = (UINT8) (Regs.H.CL & 0x3f); + } + + return TRUE; +} + +/** + Extension of INT13 call. + + @param BiosBlockIoDev Instance of block I/O device. + @param Drive Legacy drive. + + @return Result of this extension. + +**/ +UINTN +Int13Extensions ( + IN BIOS_BLOCK_IO_DEV *BiosBlockIoDev, + IN BIOS_LEGACY_DRIVE *Drive + ) +{ + INTN CarryFlag; + EFI_IA32_REGISTER_SET Regs; + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + + Regs.H.AH = 0x41; + Regs.X.BX = 0x55aa; + Regs.H.DL = Drive->Number; + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ((DEBUG_INIT, "Int13Extensions: INT 13 41 DL=%02x : CF=%d BX=%04x\n", Drive->Number, CarryFlag, Regs.X.BX)); + if (CarryFlag != 0 || Regs.X.BX != 0xaa55) { + Drive->ExtendedInt13 = FALSE; + Drive->DriveLockingAndEjecting = FALSE; + Drive->Edd = FALSE; + return FALSE; + } + + Drive->EddVersion = Regs.H.AH; + Drive->ExtendedInt13 = (BOOLEAN) ((Regs.X.CX & 0x01) == 0x01); + Drive->DriveLockingAndEjecting = (BOOLEAN) ((Regs.X.CX & 0x02) == 0x02); + Drive->Edd = (BOOLEAN) ((Regs.X.CX & 0x04) == 0x04); + Drive->Extensions64Bit = (BOOLEAN) (Regs.X.CX & 0x08); + + Drive->ParametersValid = (UINT8) GetDriveParameters (BiosBlockIoDev, Drive); + return TRUE; +} + +/** + Gets parameters of legacy drive. + + @param BiosBlockIoDev Instance of block I/O device. + @param Drive Legacy drive. + + @return Result of drive parameter retrieval. + +**/ +UINTN +GetDriveParameters ( + IN BIOS_BLOCK_IO_DEV *BiosBlockIoDev, + IN BIOS_LEGACY_DRIVE *Drive + ) +{ + INTN CarryFlag; + EFI_IA32_REGISTER_SET Regs; + UINTN PointerMath; + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + + Regs.H.AH = 0x48; + Regs.H.DL = Drive->Number; + + // + // EDD Buffer must be passed in with max buffer size as first entry in the buffer + // + mLegacyDriverUnder1Mb->Parameters.StructureSize = (UINT16) sizeof (EDD_DRIVE_PARAMETERS); + Regs.X.DS = EFI_SEGMENT ((UINTN)(&mLegacyDriverUnder1Mb->Parameters)); + Regs.X.SI = EFI_OFFSET ((UINTN)(&mLegacyDriverUnder1Mb->Parameters)); + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ((DEBUG_INIT, "GetDriveParameters: INT 13 48 DL=%02x : CF=%d AH=%02x\n", Drive->Number, CarryFlag, Regs.H.AH)); + if (CarryFlag != 0 || Regs.H.AH != 0x00) { + Drive->ErrorCode = Regs.H.AH; + SetMem (&Drive->Parameters, sizeof (Drive->Parameters), 0xaf); + return FALSE; + } + // + // We only have one buffer < 1MB, so copy into our instance data + // + CopyMem ( + &Drive->Parameters, + &mLegacyDriverUnder1Mb->Parameters, + sizeof (Drive->Parameters) + ); + + if (Drive->AtapiFloppy) { + // + // Sense Media Type + // + Regs.H.AH = 0x20; + Regs.H.DL = Drive->Number; + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ((DEBUG_INIT, "GetDriveParameters: INT 13 20 DL=%02x : CF=%d AL=%02x\n", Drive->Number, CarryFlag, Regs.H.AL)); + if (CarryFlag != 0) { + // + // Media not present or unknown media present + // + if ((Drive->Parameters.Flags & EDD_GEOMETRY_VALID) == EDD_GEOMETRY_VALID) { + Drive->MaxHead = (UINT8) (Drive->Parameters.MaxHeads - 1); + Drive->MaxSector = (UINT8) Drive->Parameters.SectorsPerTrack; + ASSERT (Drive->MaxSector != 0); + Drive->MaxCylinder = (UINT16) (Drive->Parameters.MaxCylinders - 1); + } else { + Drive->MaxHead = 0; + Drive->MaxSector = 1; + Drive->MaxCylinder = 0; + } + + } else { + // + // Media Present + // + switch (Regs.H.AL) { + case 0x03: + // + // 720 KB + // + Drive->MaxHead = 1; + Drive->MaxSector = 9; + Drive->MaxCylinder = 79; + break; + + case 0x04: + // + // 1.44MB + // + Drive->MaxHead = 1; + Drive->MaxSector = 18; + Drive->MaxCylinder = 79; + break; + + case 0x06: + // + // 2.88MB + // + Drive->MaxHead = 1; + Drive->MaxSector = 36; + Drive->MaxCylinder = 79; + break; + + case 0x0C: + // + // 360 KB + // + Drive->MaxHead = 1; + Drive->MaxSector = 9; + Drive->MaxCylinder = 39; + break; + + case 0x0D: + // + // 1.2 MB + // + Drive->MaxHead = 1; + Drive->MaxSector = 15; + Drive->MaxCylinder = 79; + break; + + case 0x0E: + // + // Toshiba 3 mode + // + case 0x0F: + // + // NEC 3 mode + // + case 0x10: + // + // Default Media + // + if ((Drive->Parameters.Flags & EDD_GEOMETRY_VALID) == EDD_GEOMETRY_VALID) { + Drive->MaxHead = (UINT8) (Drive->Parameters.MaxHeads - 1); + Drive->MaxSector = (UINT8) Drive->Parameters.SectorsPerTrack; + ASSERT (Drive->MaxSector != 0); + Drive->MaxCylinder = (UINT16) (Drive->Parameters.MaxCylinders - 1); + } else { + Drive->MaxHead = 0; + Drive->MaxSector = 1; + Drive->MaxCylinder = 0; + } + break; + + default: + // + // Unknown media type. + // + Drive->MaxHead = 0; + Drive->MaxSector = 1; + Drive->MaxCylinder = 0; + break; + } + } + + Drive->Parameters.PhysicalSectors = (Drive->MaxHead + 1) * Drive->MaxSector * (Drive->MaxCylinder + 1); + Drive->Parameters.BytesPerSector = 512; + } + // + // This data comes from the BIOS so it may not allways be valid + // since the BIOS may reuse this buffer for future accesses + // + PointerMath = EFI_SEGMENT (Drive->Parameters.Fdpt) << 4; + PointerMath += EFI_OFFSET (Drive->Parameters.Fdpt); + Drive->FdptPointer = (VOID *) PointerMath; + + return TRUE; +} +// +// Block IO Routines +// + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +Edd30BiosReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_BLOCK_IO_MEDIA *Media; + BIOS_BLOCK_IO_DEV *BiosBlockIoDev; + EDD_DEVICE_ADDRESS_PACKET *AddressPacket; + // + // I exist only for readability + // + EFI_IA32_REGISTER_SET Regs; + UINT64 TransferBuffer; + UINTN NumberOfBlocks; + UINTN TransferByteSize; + UINTN BlockSize; + BIOS_LEGACY_DRIVE *Bios; + UINTN CarryFlag; + UINTN MaxTransferBlocks; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + Media = This->Media; + BlockSize = Media->BlockSize; + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Lba > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize % BlockSize != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); + AddressPacket = mEddBufferUnder1Mb; + + MaxTransferBlocks = MAX_EDD11_XFER / BlockSize; + + TransferBuffer = (UINT64)(UINTN) Buffer; + for (; BufferSize > 0;) { + NumberOfBlocks = BufferSize / BlockSize; + NumberOfBlocks = NumberOfBlocks > MaxTransferBlocks ? MaxTransferBlocks : NumberOfBlocks; + // + // Max transfer MaxTransferBlocks + // + AddressPacket->PacketSizeInBytes = (UINT8) sizeof (EDD_DEVICE_ADDRESS_PACKET); + AddressPacket->Zero = 0; + AddressPacket->NumberOfBlocks = (UINT8) NumberOfBlocks; + AddressPacket->Zero2 = 0; + AddressPacket->SegOffset = 0xffffffff; + AddressPacket->Lba = (UINT64) Lba; + AddressPacket->TransferBuffer = TransferBuffer; + + Regs.H.AH = 0x42; + Regs.H.DL = BiosBlockIoDev->Bios.Number; + Regs.X.SI = EFI_OFFSET (AddressPacket); + Regs.X.DS = EFI_SEGMENT (AddressPacket); + + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ( + ( + DEBUG_BLKIO, "Edd30BiosReadBlocks: INT 13 42 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, + CarryFlag, Regs.H.AH + ) + ); + + Media->MediaPresent = TRUE; + if (CarryFlag != 0) { + // + // Return Error Status + // + BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; + if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { + Media->MediaId++; + Bios = &BiosBlockIoDev->Bios; + if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { + if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { + Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; + Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; + } else { + ASSERT (FALSE); + } + + Media->ReadOnly = FALSE; + gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); + gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); + return EFI_MEDIA_CHANGED; + } + } + + if (Media->RemovableMedia) { + Media->MediaPresent = FALSE; + } + + return EFI_DEVICE_ERROR; + } + + TransferByteSize = NumberOfBlocks * BlockSize; + BufferSize = BufferSize - TransferByteSize; + TransferBuffer += TransferByteSize; + Lba += NumberOfBlocks; + } + + return EFI_SUCCESS; +} + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +Edd30BiosWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_BLOCK_IO_MEDIA *Media; + BIOS_BLOCK_IO_DEV *BiosBlockIoDev; + EDD_DEVICE_ADDRESS_PACKET *AddressPacket; + // + // I exist only for readability + // + EFI_IA32_REGISTER_SET Regs; + UINT64 TransferBuffer; + UINTN NumberOfBlocks; + UINTN TransferByteSize; + UINTN BlockSize; + BIOS_LEGACY_DRIVE *Bios; + UINTN CarryFlag; + UINTN MaxTransferBlocks; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + Media = This->Media; + BlockSize = Media->BlockSize; + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Lba > Media->LastBlock) { + return EFI_DEVICE_ERROR; + } + + if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize % BlockSize != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); + AddressPacket = mEddBufferUnder1Mb; + + MaxTransferBlocks = MAX_EDD11_XFER / BlockSize; + + TransferBuffer = (UINT64)(UINTN) Buffer; + for (; BufferSize > 0;) { + NumberOfBlocks = BufferSize / BlockSize; + NumberOfBlocks = NumberOfBlocks > MaxTransferBlocks ? MaxTransferBlocks : NumberOfBlocks; + // + // Max transfer MaxTransferBlocks + // + AddressPacket->PacketSizeInBytes = (UINT8) sizeof (EDD_DEVICE_ADDRESS_PACKET); + AddressPacket->Zero = 0; + AddressPacket->NumberOfBlocks = (UINT8) NumberOfBlocks; + AddressPacket->Zero2 = 0; + AddressPacket->SegOffset = 0xffffffff; + AddressPacket->Lba = (UINT64) Lba; + AddressPacket->TransferBuffer = TransferBuffer; + + Regs.H.AH = 0x43; + Regs.H.AL = 0x00; + // + // Write Verify Off + // + Regs.H.DL = (UINT8) (BiosBlockIoDev->Bios.Number); + Regs.X.SI = EFI_OFFSET (AddressPacket); + Regs.X.DS = EFI_SEGMENT (AddressPacket); + + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ( + ( + DEBUG_BLKIO, "Edd30BiosWriteBlocks: INT 13 43 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, + CarryFlag, Regs.H.AH + ) + ); + + Media->MediaPresent = TRUE; + if (CarryFlag != 0) { + // + // Return Error Status + // + BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; + if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { + Media->MediaId++; + Bios = &BiosBlockIoDev->Bios; + if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { + if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { + Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; + Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; + } else { + ASSERT (FALSE); + } + + Media->ReadOnly = FALSE; + gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); + gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); + return EFI_MEDIA_CHANGED; + } + } else if (BiosBlockIoDev->Bios.ErrorCode == BIOS_WRITE_PROTECTED) { + Media->ReadOnly = TRUE; + return EFI_WRITE_PROTECTED; + } + + if (Media->RemovableMedia) { + Media->MediaPresent = FALSE; + } + + return EFI_DEVICE_ERROR; + } + + Media->ReadOnly = FALSE; + TransferByteSize = NumberOfBlocks * BlockSize; + BufferSize = BufferSize - TransferByteSize; + TransferBuffer += TransferByteSize; + Lba += NumberOfBlocks; + } + + return EFI_SUCCESS; +} + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writting back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + return EFI_SUCCESS; +} + +/** + Reset the Block Device. + + @param This Indicates a pointer to the calling context. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + BIOS_BLOCK_IO_DEV *BiosBlockIoDev; + EFI_IA32_REGISTER_SET Regs; + UINTN CarryFlag; + + BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + + Regs.H.AH = 0x00; + Regs.H.DL = BiosBlockIoDev->Bios.Number; + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ( + ( + DEBUG_INIT, "BiosBlockIoReset: INT 13 00 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, CarryFlag, + Regs.H.AH + ) + ); + if (CarryFlag != 0) { + if (Regs.H.AL == BIOS_RESET_FAILED) { + Regs.H.AH = 0x00; + Regs.H.DL = BiosBlockIoDev->Bios.Number; + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ( + ( + DEBUG_INIT, "BiosBlockIoReset: INT 13 00 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, CarryFlag, + Regs.H.AH + ) + ); + if (CarryFlag != 0) { + BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; + return EFI_DEVICE_ERROR; + } + } + } + + return EFI_SUCCESS; +} +// +// +// These functions need to double buffer all data under 1MB! +// +// + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +Edd11BiosReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_BLOCK_IO_MEDIA *Media; + BIOS_BLOCK_IO_DEV *BiosBlockIoDev; + EDD_DEVICE_ADDRESS_PACKET *AddressPacket; + // + // I exist only for readability + // + EFI_IA32_REGISTER_SET Regs; + UINT64 TransferBuffer; + UINTN NumberOfBlocks; + UINTN TransferByteSize; + UINTN BlockSize; + BIOS_LEGACY_DRIVE *Bios; + UINTN CarryFlag; + UINTN MaxTransferBlocks; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + Media = This->Media; + BlockSize = Media->BlockSize; + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Lba > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize % BlockSize != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); + AddressPacket = mEddBufferUnder1Mb; + + MaxTransferBlocks = MAX_EDD11_XFER / BlockSize; + + TransferBuffer = (UINT64)(UINTN) mEdd11Buffer; + for (; BufferSize > 0;) { + NumberOfBlocks = BufferSize / BlockSize; + NumberOfBlocks = NumberOfBlocks > MaxTransferBlocks ? MaxTransferBlocks : NumberOfBlocks; + // + // Max transfer MaxTransferBlocks + // + AddressPacket->PacketSizeInBytes = (UINT8) sizeof (EDD_DEVICE_ADDRESS_PACKET); + AddressPacket->Zero = 0; + AddressPacket->NumberOfBlocks = (UINT8) NumberOfBlocks; + AddressPacket->Zero2 = 0; + AddressPacket->SegOffset = EFI_SEGMENT (TransferBuffer) << 16; + AddressPacket->SegOffset |= EFI_OFFSET (TransferBuffer); + AddressPacket->Lba = (UINT64) Lba; + + Regs.H.AH = 0x42; + Regs.H.DL = BiosBlockIoDev->Bios.Number; + Regs.X.SI = EFI_OFFSET (AddressPacket); + Regs.X.DS = EFI_SEGMENT (AddressPacket); + + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ( + ( + DEBUG_BLKIO, "Edd11BiosReadBlocks: INT 13 42 DL=%02x : CF=%d AH=%02x : LBA 0x%lx Block(s) %0d \n", + BiosBlockIoDev->Bios.Number, CarryFlag, Regs.H.AH, Lba, NumberOfBlocks + ) + ); + Media->MediaPresent = TRUE; + if (CarryFlag != 0) { + // + // Return Error Status + // + BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; + if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { + Media->MediaId++; + Bios = &BiosBlockIoDev->Bios; + if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { + if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { + Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; + Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; + } else { + ASSERT (FALSE); + } + + Media->ReadOnly = FALSE; + gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); + gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); + return EFI_MEDIA_CHANGED; + } + } + + if (Media->RemovableMedia) { + Media->MediaPresent = FALSE; + } + + return EFI_DEVICE_ERROR; + } + + TransferByteSize = NumberOfBlocks * BlockSize; + CopyMem (Buffer, (VOID *) (UINTN) TransferBuffer, TransferByteSize); + BufferSize = BufferSize - TransferByteSize; + Buffer = (VOID *) ((UINT8 *) Buffer + TransferByteSize); + Lba += NumberOfBlocks; + } + + return EFI_SUCCESS; +} + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +Edd11BiosWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_BLOCK_IO_MEDIA *Media; + BIOS_BLOCK_IO_DEV *BiosBlockIoDev; + EDD_DEVICE_ADDRESS_PACKET *AddressPacket; + // + // I exist only for readability + // + EFI_IA32_REGISTER_SET Regs; + UINT64 TransferBuffer; + UINTN NumberOfBlocks; + UINTN TransferByteSize; + UINTN BlockSize; + BIOS_LEGACY_DRIVE *Bios; + UINTN CarryFlag; + UINTN MaxTransferBlocks; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + Media = This->Media; + BlockSize = Media->BlockSize; + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Lba > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize % BlockSize != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); + AddressPacket = mEddBufferUnder1Mb; + + MaxTransferBlocks = MAX_EDD11_XFER / BlockSize; + + TransferBuffer = (UINT64)(UINTN) mEdd11Buffer; + for (; BufferSize > 0;) { + NumberOfBlocks = BufferSize / BlockSize; + NumberOfBlocks = NumberOfBlocks > MaxTransferBlocks ? MaxTransferBlocks : NumberOfBlocks; + // + // Max transfer MaxTransferBlocks + // + AddressPacket->PacketSizeInBytes = (UINT8) sizeof (EDD_DEVICE_ADDRESS_PACKET); + AddressPacket->Zero = 0; + AddressPacket->NumberOfBlocks = (UINT8) NumberOfBlocks; + AddressPacket->Zero2 = 0; + AddressPacket->SegOffset = EFI_SEGMENT (TransferBuffer) << 16; + AddressPacket->SegOffset |= EFI_OFFSET (TransferBuffer); + AddressPacket->Lba = (UINT64) Lba; + + Regs.H.AH = 0x43; + Regs.H.AL = 0x00; + // + // Write Verify disable + // + Regs.H.DL = BiosBlockIoDev->Bios.Number; + Regs.X.SI = EFI_OFFSET (AddressPacket); + Regs.X.DS = EFI_SEGMENT (AddressPacket); + + TransferByteSize = NumberOfBlocks * BlockSize; + CopyMem ((VOID *) (UINTN) TransferBuffer, Buffer, TransferByteSize); + + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ( + ( + DEBUG_BLKIO, "Edd11BiosWriteBlocks: INT 13 43 DL=%02x : CF=%d AH=%02x\n: LBA 0x%lx Block(s) %0d \n", + BiosBlockIoDev->Bios.Number, CarryFlag, Regs.H.AH, Lba, NumberOfBlocks + ) + ); + Media->MediaPresent = TRUE; + if (CarryFlag != 0) { + // + // Return Error Status + // + BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; + if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { + Media->MediaId++; + Bios = &BiosBlockIoDev->Bios; + if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { + if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { + Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; + Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; + } else { + ASSERT (FALSE); + } + + Media->ReadOnly = FALSE; + gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); + gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); + return EFI_MEDIA_CHANGED; + } + } else if (BiosBlockIoDev->Bios.ErrorCode == BIOS_WRITE_PROTECTED) { + Media->ReadOnly = TRUE; + return EFI_WRITE_PROTECTED; + } + + if (Media->RemovableMedia) { + Media->MediaPresent = FALSE; + } + + return EFI_DEVICE_ERROR; + } + + Media->ReadOnly = FALSE; + BufferSize = BufferSize - TransferByteSize; + Buffer = (VOID *) ((UINT8 *) Buffer + TransferByteSize); + Lba += NumberOfBlocks; + } + + return EFI_SUCCESS; +} + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +BiosReadLegacyDrive ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_BLOCK_IO_MEDIA *Media; + BIOS_BLOCK_IO_DEV *BiosBlockIoDev; + EFI_IA32_REGISTER_SET Regs; + UINTN UpperCylinder; + UINTN Temp; + UINTN Cylinder; + UINTN Head; + UINTN Sector; + UINTN NumberOfBlocks; + UINTN TransferByteSize; + UINTN ShortLba; + UINTN CheckLba; + UINTN BlockSize; + BIOS_LEGACY_DRIVE *Bios; + UINTN CarryFlag; + UINTN Retry; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + Media = This->Media; + BlockSize = Media->BlockSize; + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Lba > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize % BlockSize != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); + ShortLba = (UINTN) Lba; + + while (BufferSize != 0) { + // + // Compute I/O location in Sector, Head, Cylinder format + // + Sector = (ShortLba % BiosBlockIoDev->Bios.MaxSector) + 1; + Temp = ShortLba / BiosBlockIoDev->Bios.MaxSector; + Head = Temp % (BiosBlockIoDev->Bios.MaxHead + 1); + Cylinder = Temp / (BiosBlockIoDev->Bios.MaxHead + 1); + + // + // Limit transfer to this Head & Cylinder + // + NumberOfBlocks = BufferSize / BlockSize; + Temp = BiosBlockIoDev->Bios.MaxSector - Sector + 1; + NumberOfBlocks = NumberOfBlocks > Temp ? Temp : NumberOfBlocks; + + Retry = 3; + do { + // + // Perform the IO + // + Regs.H.AH = 2; + Regs.H.AL = (UINT8) NumberOfBlocks; + Regs.H.DL = BiosBlockIoDev->Bios.Number; + + UpperCylinder = (Cylinder & 0x0f00) >> 2; + + CheckLba = Cylinder * (BiosBlockIoDev->Bios.MaxHead + 1) + Head; + CheckLba = CheckLba * BiosBlockIoDev->Bios.MaxSector + Sector - 1; + + DEBUG ( + (DEBUG_BLKIO, + "RLD: LBA %x (%x), Sector %x (%x), Head %x (%x), Cyl %x, UCyl %x\n", + ShortLba, + CheckLba, + Sector, + BiosBlockIoDev->Bios.MaxSector, + Head, + BiosBlockIoDev->Bios.MaxHead, + Cylinder, + UpperCylinder) + ); + ASSERT (CheckLba == ShortLba); + + Regs.H.CL = (UINT8) ((Sector & 0x3f) + (UpperCylinder & 0xff)); + Regs.H.DH = (UINT8) (Head & 0x3f); + Regs.H.CH = (UINT8) (Cylinder & 0xff); + + Regs.X.BX = EFI_OFFSET (mEdd11Buffer); + Regs.X.ES = EFI_SEGMENT (mEdd11Buffer); + + DEBUG ( + (DEBUG_BLKIO, + "INT 13h: AX:(02%02x) DX:(%02x%02x) CX:(%02x%02x) BX:(%04x) ES:(%04x)\n", + Regs.H.AL, + (UINT8) (Head & 0x3f), + Regs.H.DL, + (UINT8) (Cylinder & 0xff), + (UINT8) ((Sector & 0x3f) + (UpperCylinder & 0xff)), + EFI_OFFSET (mEdd11Buffer), + EFI_SEGMENT (mEdd11Buffer)) + ); + + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ( + ( + DEBUG_BLKIO, "BiosReadLegacyDrive: INT 13 02 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, + CarryFlag, Regs.H.AH + ) + ); + Retry--; + } while (CarryFlag != 0 && Retry != 0 && Regs.H.AH != BIOS_DISK_CHANGED); + + Media->MediaPresent = TRUE; + if (CarryFlag != 0) { + // + // Return Error Status + // + BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; + if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { + Media->MediaId++; + Bios = &BiosBlockIoDev->Bios; + if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { + // + // If the size of the media changed we need to reset the disk geometry + // + if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { + Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; + Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; + } else { + // + // Legacy Interfaces + // + Media->LastBlock = (Bios->MaxHead + 1) * Bios->MaxSector * (Bios->MaxCylinder + 1) - 1; + Media->BlockSize = 512; + } + + Media->ReadOnly = FALSE; + gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); + gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); + return EFI_MEDIA_CHANGED; + } + } + + if (Media->RemovableMedia) { + Media->MediaPresent = FALSE; + } + + return EFI_DEVICE_ERROR; + } + + TransferByteSize = NumberOfBlocks * BlockSize; + CopyMem (Buffer, mEdd11Buffer, TransferByteSize); + + ShortLba = ShortLba + NumberOfBlocks; + BufferSize = BufferSize - TransferByteSize; + Buffer = (VOID *) ((UINT8 *) Buffer + TransferByteSize); + } + + return EFI_SUCCESS; +} + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +BiosWriteLegacyDrive ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_BLOCK_IO_MEDIA *Media; + BIOS_BLOCK_IO_DEV *BiosBlockIoDev; + EFI_IA32_REGISTER_SET Regs; + UINTN UpperCylinder; + UINTN Temp; + UINTN Cylinder; + UINTN Head; + UINTN Sector; + UINTN NumberOfBlocks; + UINTN TransferByteSize; + UINTN ShortLba; + UINTN CheckLba; + UINTN BlockSize; + BIOS_LEGACY_DRIVE *Bios; + UINTN CarryFlag; + UINTN Retry; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + Media = This->Media; + BlockSize = Media->BlockSize; + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Lba > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize % BlockSize != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); + ShortLba = (UINTN) Lba; + + while (BufferSize != 0) { + // + // Compute I/O location in Sector, Head, Cylinder format + // + Sector = (ShortLba % BiosBlockIoDev->Bios.MaxSector) + 1; + Temp = ShortLba / BiosBlockIoDev->Bios.MaxSector; + Head = Temp % (BiosBlockIoDev->Bios.MaxHead + 1); + Cylinder = Temp / (BiosBlockIoDev->Bios.MaxHead + 1); + + // + // Limit transfer to this Head & Cylinder + // + NumberOfBlocks = BufferSize / BlockSize; + Temp = BiosBlockIoDev->Bios.MaxSector - Sector + 1; + NumberOfBlocks = NumberOfBlocks > Temp ? Temp : NumberOfBlocks; + + Retry = 3; + do { + // + // Perform the IO + // + Regs.H.AH = 3; + Regs.H.AL = (UINT8) NumberOfBlocks; + Regs.H.DL = BiosBlockIoDev->Bios.Number; + + UpperCylinder = (Cylinder & 0x0f00) >> 2; + + CheckLba = Cylinder * (BiosBlockIoDev->Bios.MaxHead + 1) + Head; + CheckLba = CheckLba * BiosBlockIoDev->Bios.MaxSector + Sector - 1; + + DEBUG ( + (DEBUG_BLKIO, + "RLD: LBA %x (%x), Sector %x (%x), Head %x (%x), Cyl %x, UCyl %x\n", + ShortLba, + CheckLba, + Sector, + BiosBlockIoDev->Bios.MaxSector, + Head, + BiosBlockIoDev->Bios.MaxHead, + Cylinder, + UpperCylinder) + ); + ASSERT (CheckLba == ShortLba); + + Regs.H.CL = (UINT8) ((Sector & 0x3f) + (UpperCylinder & 0xff)); + Regs.H.DH = (UINT8) (Head & 0x3f); + Regs.H.CH = (UINT8) (Cylinder & 0xff); + + Regs.X.BX = EFI_OFFSET (mEdd11Buffer); + Regs.X.ES = EFI_SEGMENT (mEdd11Buffer); + + TransferByteSize = NumberOfBlocks * BlockSize; + CopyMem (mEdd11Buffer, Buffer, TransferByteSize); + + DEBUG ( + (DEBUG_BLKIO, + "INT 13h: AX:(03%02x) DX:(%02x%02x) CX:(%02x%02x) BX:(%04x) ES:(%04x)\n", + Regs.H.AL, + (UINT8) (Head & 0x3f), + Regs.H.DL, + (UINT8) (Cylinder & 0xff), + (UINT8) ((Sector & 0x3f) + (UpperCylinder & 0xff)), + EFI_OFFSET (mEdd11Buffer), + EFI_SEGMENT (mEdd11Buffer)) + ); + + CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); + DEBUG ( + ( + DEBUG_BLKIO, "BiosWriteLegacyDrive: INT 13 03 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, + CarryFlag, Regs.H.AH + ) + ); + Retry--; + } while (CarryFlag != 0 && Retry != 0 && Regs.H.AH != BIOS_DISK_CHANGED); + + Media->MediaPresent = TRUE; + if (CarryFlag != 0) { + // + // Return Error Status + // + BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; + if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { + Media->MediaId++; + Bios = &BiosBlockIoDev->Bios; + if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { + if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { + Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; + Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; + } else { + // + // Legacy Interfaces + // + Media->LastBlock = (Bios->MaxHead + 1) * Bios->MaxSector * (Bios->MaxCylinder + 1) - 1; + Media->BlockSize = 512; + } + // + // If the size of the media changed we need to reset the disk geometry + // + Media->ReadOnly = FALSE; + gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); + gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); + return EFI_MEDIA_CHANGED; + } + } else if (BiosBlockIoDev->Bios.ErrorCode == BIOS_WRITE_PROTECTED) { + Media->ReadOnly = TRUE; + return EFI_WRITE_PROTECTED; + } + + if (Media->RemovableMedia) { + Media->MediaPresent = FALSE; + } + + return EFI_DEVICE_ERROR; + } + + Media->ReadOnly = FALSE; + ShortLba = ShortLba + NumberOfBlocks; + BufferSize = BufferSize - TransferByteSize; + Buffer = (VOID *) ((UINT8 *) Buffer + TransferByteSize); + } + + return EFI_SUCCESS; +} diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BlockIoDxe.inf b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BlockIoDxe.inf new file mode 100644 index 0000000000..3fcaa2723c --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BlockIoDxe.inf @@ -0,0 +1,58 @@ +## @file +# Component description file for BIOS Block IO module. +# +# Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions +# of the BSD License which accompanies this distribution. The +# full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BlockIoDxe + FILE_GUID = 4495E47E-42A9-4007-8c17-B6664F909D04 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = BiosBlockIoDriverEntryPoint + +[Sources] + BiosBlkIo.h + Edd.h + BiosBlkIo.c + BiosInt13.c + ComponentName.c + +[LibraryClasses] + UefiDriverEntryPoint + DebugLib + BaseMemoryLib + UefiBootServicesTableLib + UefiLib + DevicePathLib + MemoryAllocationLib + + +[Protocols] + gEfiBlockIoProtocolGuid + gEfiDevicePathProtocolGuid + gEfiPciIoProtocolGuid + gEfiLegacyBiosProtocolGuid + + +[Guids] + gEfiLegacyBiosGuid + + +[Packages] + MdePkg/MdePkg.dec + IntelFrameworkPkg/IntelFrameworkPkg.dec + IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec + diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/ComponentName.c b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/ComponentName.c new file mode 100644 index 0000000000..88bca13d5f --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/ComponentName.c @@ -0,0 +1,309 @@ +/** @file + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "BiosBlkIo.h" + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gBiosBlockIoComponentName = { + BiosBlockIoComponentNameGetDriverName, + BiosBlockIoComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gBiosBlockIoComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) BiosBlockIoComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) BiosBlockIoComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mBiosBlockIoDriverNameTable[] = { + { + "eng;en", + L"BIOS[INT13] Block Io Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mBiosBlockIoDriverNameTable, + DriverName, + (BOOLEAN)(This == &gBiosBlockIoComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosBlockIoComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/Edd.h b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/Edd.h new file mode 100644 index 0000000000..be4d8302cf --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/Edd.h @@ -0,0 +1,209 @@ +/** @file + Include file to suport EDD 3.0. + This file is coded to T13 D1386 Revision 3 + Availible on http://www.t13.org/#Project drafts + Currently at ftp://fission.dt.wdc.com/pub/standards/x3t13/project/d1386r3.pdf + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EDD_H_ +#define _EDD_H_ + +// +// packing with no compiler padding, so that the fields +// of the following architected structures can be +// properly accessed from C code. +// +#pragma pack(1) + +typedef struct { + UINT8 Bus; + UINT8 Device; + UINT8 Function; + UINT8 Controller; + UINT32 Reserved; +} EDD_PCI; + +typedef struct { + UINT16 Base; + UINT16 Reserved; + UINT32 Reserved2; +} EDD_LEGACY; + +typedef union { + EDD_PCI Pci; + EDD_LEGACY Legacy; +} EDD_INTERFACE_PATH; + +typedef struct { + UINT8 Master; + UINT8 Reserved[15]; +} EDD_ATA; + +typedef struct { + UINT8 Master; + UINT8 Lun; + UINT8 Reserved[14]; +} EDD_ATAPI; + +typedef struct { + UINT16 Pun; + UINT64 Lun; + UINT8 Reserved[6]; +} EDD_SCSI; + +typedef struct { + UINT64 SerialNumber; + UINT64 Reserved; +} EDD_USB; + +typedef struct { + UINT64 Guid; + UINT64 Reserved; +} EDD_1394; + +typedef struct { + UINT64 Wwn; + UINT64 Lun; +} EDD_FIBRE; + +typedef union { + EDD_ATA Ata; + EDD_ATAPI Atapi; + EDD_SCSI Scsi; + EDD_USB Usb; + EDD_1394 FireWire; + EDD_FIBRE FibreChannel; +} EDD_DEVICE_PATH; + +typedef struct { + UINT16 StructureSize; + UINT16 Flags; + UINT32 MaxCylinders; + UINT32 MaxHeads; + UINT32 SectorsPerTrack; + UINT64 PhysicalSectors; + UINT16 BytesPerSector; + UINT32 Fdpt; + UINT16 Key; + UINT8 DevicePathLength; + UINT8 Reserved1; + UINT16 Reserved2; + CHAR8 HostBusType[4]; + CHAR8 InterfaceType[8]; + EDD_INTERFACE_PATH InterfacePath; + EDD_DEVICE_PATH DevicePath; + UINT8 Reserved3; + UINT8 Checksum; +} EDD_DRIVE_PARAMETERS; + +// +// EDD_DRIVE_PARAMETERS.Flags defines +// +#define EDD_GEOMETRY_VALID 0x02 +#define EDD_DEVICE_REMOVABLE 0x04 +#define EDD_WRITE_VERIFY_SUPPORTED 0x08 +#define EDD_DEVICE_CHANGE 0x10 +#define EDD_DEVICE_LOCKABLE 0x20 + +// +// BUGBUG: This bit does not follow the spec. It tends to be always set +// to work properly with Win98. +// +#define EDD_DEVICE_GEOMETRY_MAX 0x40 + +typedef struct { + UINT8 PacketSizeInBytes; // 0x18 + UINT8 Zero; + UINT8 NumberOfBlocks; // Max 0x7f + UINT8 Zero2; + UINT32 SegOffset; + UINT64 Lba; + UINT64 TransferBuffer; + UINT32 ExtendedBlockCount; // Max 0xffffffff + UINT32 Zero3; +} EDD_DEVICE_ADDRESS_PACKET; + +#define EDD_VERSION_30 0x30 + +// +// Int 13 BIOS Errors +// +#define BIOS_PASS 0x00 +#define BIOS_WRITE_PROTECTED 0x03 +#define BIOS_SECTOR_NOT_FOUND 0x04 +#define BIOS_RESET_FAILED 0x05 +#define BIOS_DISK_CHANGED 0x06 +#define BIOS_DRIVE_DOES_NOT_EXIST 0x07 +#define BIOS_DMA_ERROR 0x08 +#define BIOS_DATA_BOUNDRY_ERROR 0x09 +#define BIOS_BAD_SECTOR 0x0a +#define BIOS_BAD_TRACK 0x0b +#define BIOS_MEADIA_TYPE_NOT_FOUND 0x0c +#define BIOS_INVALED_FORMAT 0x0d +#define BIOS_ECC_ERROR 0x10 +#define BIOS_ECC_CORRECTED_ERROR 0x11 +#define BIOS_HARD_DRIVE_FAILURE 0x20 +#define BIOS_SEEK_FAILED 0x40 +#define BIOS_DRIVE_TIMEOUT 0x80 +#define BIOS_DRIVE_NOT_READY 0xaa +#define BIOS_UNDEFINED_ERROR 0xbb +#define BIOS_WRITE_FAULT 0xcc +#define BIOS_SENSE_FAILED 0xff + +#define MAX_EDD11_XFER 0xfe00 + +#pragma pack() +// +// Internal Data Structures +// +typedef struct { + CHAR8 Letter; + UINT8 Number; + UINT8 EddVersion; + BOOLEAN ExtendedInt13; + BOOLEAN DriveLockingAndEjecting; + BOOLEAN Edd; + BOOLEAN Extensions64Bit; + BOOLEAN ParametersValid; + UINT8 ErrorCode; + VOID *FdptPointer; + BOOLEAN Floppy; + BOOLEAN AtapiFloppy; + UINT8 MaxHead; + UINT8 MaxSector; + UINT16 MaxCylinder; + UINT16 Pad; + EDD_DRIVE_PARAMETERS Parameters; +} BIOS_LEGACY_DRIVE; + +#define BIOS_CONSOLE_BLOCK_IO_DEV_SIGNATURE SIGNATURE_32 ('b', 'b', 'i', 'o') +typedef struct { + UINTN Signature; + + EFI_HANDLE Handle; + EFI_HANDLE ControllerHandle; + EFI_BLOCK_IO_PROTOCOL BlockIo; + EFI_BLOCK_IO_MEDIA BlockMedia; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + + BIOS_LEGACY_DRIVE Bios; + +} BIOS_BLOCK_IO_DEV; + +#define BIOS_BLOCK_IO_FROM_THIS(a) CR (a, BIOS_BLOCK_IO_DEV, BlockIo, BIOS_CONSOLE_BLOCK_IO_DEV_SIGNATURE) + +#endif diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/BiosKeyboard.c b/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/BiosKeyboard.c new file mode 100644 index 0000000000..06ef9d3345 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/BiosKeyboard.c @@ -0,0 +1,2365 @@ +/** @file + ConsoleOut Routines that speak VGA. + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "BiosKeyboard.h" + +// +// EFI Driver Binding Protocol Instance +// +EFI_DRIVER_BINDING_PROTOCOL gBiosKeyboardDriverBinding = { + BiosKeyboardDriverBindingSupported, + BiosKeyboardDriverBindingStart, + BiosKeyboardDriverBindingStop, + 0x3, + NULL, + NULL +}; + + +/** + Enqueue the key. + + @param Queue The queue to be enqueued. + @param KeyData The key data to be enqueued. + + @retval EFI_NOT_READY The queue is full. + @retval EFI_SUCCESS Successfully enqueued the key data. + +**/ +EFI_STATUS +Enqueue ( + IN SIMPLE_QUEUE *Queue, + IN EFI_KEY_DATA *KeyData + ) +{ + if ((Queue->Rear + 1) % QUEUE_MAX_COUNT == Queue->Front) { + return EFI_NOT_READY; + } + + CopyMem (&Queue->Buffer[Queue->Rear], KeyData, sizeof (EFI_KEY_DATA)); + Queue->Rear = (Queue->Rear + 1) % QUEUE_MAX_COUNT; + + return EFI_SUCCESS; +} + + +/** + Dequeue the key. + + @param Queue The queue to be dequeued. + @param KeyData The key data to be dequeued. + + @retval EFI_NOT_READY The queue is empty. + @retval EFI_SUCCESS Successfully dequeued the key data. + +**/ +EFI_STATUS +Dequeue ( + IN SIMPLE_QUEUE *Queue, + IN EFI_KEY_DATA *KeyData + ) +{ + if (Queue->Front == Queue->Rear) { + return EFI_NOT_READY; + } + + CopyMem (KeyData, &Queue->Buffer[Queue->Front], sizeof (EFI_KEY_DATA)); + Queue->Front = (Queue->Front + 1) % QUEUE_MAX_COUNT; + + return EFI_SUCCESS; +} + + +/** + Check whether the queue is empty. + + @param Queue The queue to be checked. + + @retval EFI_NOT_READY The queue is empty. + @retval EFI_SUCCESS The queue is not empty. + +**/ +EFI_STATUS +CheckQueue ( + IN SIMPLE_QUEUE *Queue + ) +{ + if (Queue->Front == Queue->Rear) { + return EFI_NOT_READY; + } + + return EFI_SUCCESS; +} + +// +// EFI Driver Binding Protocol Functions +// + +/** + Check whether the driver supports this device. + + @param This The Udriver binding protocol. + @param Controller The controller handle to check. + @param RemainingDevicePath The remaining device path. + + @retval EFI_SUCCESS The driver supports this controller. + @retval other This device isn't supported. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + EFI_ISA_IO_PROTOCOL *IsaIo; + + // + // See if the Legacy BIOS Protocol is available + // + Status = gBS->LocateProtocol ( + &gEfiLegacyBiosProtocolGuid, + NULL, + (VOID **) &LegacyBios + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + (VOID **) &IsaIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Use the ISA I/O Protocol to see if Controller is the Keyboard controller + // + if (IsaIo->ResourceList->Device.HID != EISA_PNP_ID (0x303) || IsaIo->ResourceList->Device.UID != 0) { + Status = EFI_UNSUPPORTED; + } + + gBS->CloseProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + +/** + Starts the device with this driver. + + @param This The driver binding instance. + @param Controller Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The controller is controlled by the driver. + @retval Other This controller cannot be started. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + EFI_ISA_IO_PROTOCOL *IsaIo; + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + EFI_IA32_REGISTER_SET Regs; + BOOLEAN CarryFlag; + EFI_PS2_POLICY_PROTOCOL *Ps2Policy; + UINT8 Command; + EFI_STATUS_CODE_VALUE StatusCode; + + BiosKeyboardPrivate = NULL; + IsaIo = NULL; + StatusCode = 0; + + // + // Get Ps2 policy to set. Will be use if present. + // + gBS->LocateProtocol ( + &gEfiPs2PolicyProtocolGuid, + NULL, + (VOID **) &Ps2Policy + ); + + // + // See if the Legacy BIOS Protocol is available + // + Status = gBS->LocateProtocol ( + &gEfiLegacyBiosProtocolGuid, + NULL, + (VOID **) &LegacyBios + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Open the IO Abstraction(s) needed + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + (VOID **) &IsaIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Allocate the private device structure + // + BiosKeyboardPrivate = (BIOS_KEYBOARD_DEV *) AllocateZeroPool (sizeof (BIOS_KEYBOARD_DEV)); + if (NULL == BiosKeyboardPrivate) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // Initialize the private device structure + // + BiosKeyboardPrivate->Signature = BIOS_KEYBOARD_DEV_SIGNATURE; + BiosKeyboardPrivate->Handle = Controller; + BiosKeyboardPrivate->LegacyBios = LegacyBios; + BiosKeyboardPrivate->IsaIo = IsaIo; + + BiosKeyboardPrivate->SimpleTextIn.Reset = BiosKeyboardReset; + BiosKeyboardPrivate->SimpleTextIn.ReadKeyStroke = BiosKeyboardReadKeyStroke; + + BiosKeyboardPrivate->DataRegisterAddress = KEYBOARD_8042_DATA_REGISTER; + BiosKeyboardPrivate->StatusRegisterAddress = KEYBOARD_8042_STATUS_REGISTER; + BiosKeyboardPrivate->CommandRegisterAddress = KEYBOARD_8042_COMMAND_REGISTER; + BiosKeyboardPrivate->ExtendedKeyboard = TRUE; + + BiosKeyboardPrivate->Queue.Front = 0; + BiosKeyboardPrivate->Queue.Rear = 0; + BiosKeyboardPrivate->SimpleTextInputEx.Reset = BiosKeyboardResetEx; + BiosKeyboardPrivate->SimpleTextInputEx.ReadKeyStrokeEx = BiosKeyboardReadKeyStrokeEx; + BiosKeyboardPrivate->SimpleTextInputEx.SetState = BiosKeyboardSetState; + BiosKeyboardPrivate->SimpleTextInputEx.RegisterKeyNotify = BiosKeyboardRegisterKeyNotify; + BiosKeyboardPrivate->SimpleTextInputEx.UnregisterKeyNotify = BiosKeyboardUnregisterKeyNotify; + InitializeListHead (&BiosKeyboardPrivate->NotifyList); + + Status = gBS->HandleProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &BiosKeyboardPrivate->DevicePath + ); + + // + // Report that the keyboard is being enabled + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_ENABLE, + BiosKeyboardPrivate->DevicePath + ); + + // + // Setup the WaitForKey event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + BiosKeyboardWaitForKey, + &(BiosKeyboardPrivate->SimpleTextIn), + &((BiosKeyboardPrivate->SimpleTextIn).WaitForKey) + ); + if (EFI_ERROR (Status)) { + (BiosKeyboardPrivate->SimpleTextIn).WaitForKey = NULL; + goto Done; + } + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + BiosKeyboardWaitForKeyEx, + &(BiosKeyboardPrivate->SimpleTextInputEx), + &(BiosKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx) + ); + if (EFI_ERROR (Status)) { + BiosKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx = NULL; + goto Done; + } + + // + // Setup a periodic timer, used for reading keystrokes at a fixed interval + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + BiosKeyboardTimerHandler, + BiosKeyboardPrivate, + &BiosKeyboardPrivate->TimerEvent + ); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; + goto Done; + } + + Status = gBS->SetTimer ( + BiosKeyboardPrivate->TimerEvent, + TimerPeriodic, + KEYBOARD_TIMER_INTERVAL + ); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; + goto Done; + } + + // + // Report a Progress Code for an attempt to detect the precense of the keyboard device in the system + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_PRESENCE_DETECT, + BiosKeyboardPrivate->DevicePath + ); + + // + // Reset the keyboard device + // + Status = BiosKeyboardPrivate->SimpleTextInputEx.Reset ( + &BiosKeyboardPrivate->SimpleTextInputEx, + FALSE + ); + + if (EFI_ERROR (Status)) { + StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED; + goto Done; + } + // + // Do platform specific policy like port swapping and keyboard light default + // + if (Ps2Policy != NULL) { + + Ps2Policy->Ps2InitHardware (Controller); + + Command = 0; + if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_CAPSLOCK) == EFI_KEYBOARD_CAPSLOCK) { + Command |= 4; + } + + if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_NUMLOCK) == EFI_KEYBOARD_NUMLOCK) { + Command |= 2; + } + + if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_SCROLLLOCK) == EFI_KEYBOARD_SCROLLLOCK) { + Command |= 1; + } + + KeyboardWrite (BiosKeyboardPrivate, 0xed); + KeyboardWaitForValue (BiosKeyboardPrivate, 0xfa, KEYBOARD_WAITFORVALUE_TIMEOUT); + KeyboardWrite (BiosKeyboardPrivate, Command); + // + // Call Legacy BIOS Protocol to set whatever is necessary + // + LegacyBios->UpdateKeyboardLedStatus (LegacyBios, Command); + } + // + // Get Configuration + // + Regs.H.AH = 0xc0; + CarryFlag = BiosKeyboardPrivate->LegacyBios->Int86 ( + BiosKeyboardPrivate->LegacyBios, + 0x15, + &Regs + ); + + if (!CarryFlag) { + // + // Check bit 6 of Feature Byte 2. + // If it is set, then Int 16 Func 09 is supported + // + if (*(UINT8 *)(UINTN) ((Regs.X.ES << 4) + Regs.X.BX + 0x06) & 0x40) { + // + // Get Keyboard Functionality + // + Regs.H.AH = 0x09; + CarryFlag = BiosKeyboardPrivate->LegacyBios->Int86 ( + BiosKeyboardPrivate->LegacyBios, + 0x16, + &Regs + ); + + if (!CarryFlag) { + // + // Check bit 5 of AH. + // If it is set, then INT 16 Finc 10-12 are supported. + // + if ((Regs.H.AL & 0x40) != 0) { + // + // Set the flag to use INT 16 Func 10-12 + // + BiosKeyboardPrivate->ExtendedKeyboard = TRUE; + } + } + } + } + // + // Install protocol interfaces for the keyboard device. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiSimpleTextInProtocolGuid, + &BiosKeyboardPrivate->SimpleTextIn, + &gEfiSimpleTextInputExProtocolGuid, + &BiosKeyboardPrivate->SimpleTextInputEx, + NULL + ); + +Done: + if (StatusCode != 0) { + // + // Report an Error Code for failing to start the keyboard device + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + StatusCode, + BiosKeyboardPrivate->DevicePath + ); + } + + if (EFI_ERROR (Status)) { + + if (BiosKeyboardPrivate != NULL) { + if ((BiosKeyboardPrivate->SimpleTextIn).WaitForKey != NULL) { + gBS->CloseEvent ((BiosKeyboardPrivate->SimpleTextIn).WaitForKey); + } + + if ((BiosKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx != NULL) { + gBS->CloseEvent ((BiosKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx); + } + BiosKeyboardFreeNotifyList (&BiosKeyboardPrivate->NotifyList); + + if (BiosKeyboardPrivate->TimerEvent != NULL) { + gBS->CloseEvent (BiosKeyboardPrivate->TimerEvent); + } + + FreePool (BiosKeyboardPrivate); + } + + if (IsaIo != NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + } + + return Status; +} + +/** + Stop the device handled by this driver. + + @param This The driver binding protocol. + @param Controller The controller to release. + @param NumberOfChildren The number of handles in ChildHandleBuffer. + @param ChildHandleBuffer The array of child handle. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + @retval Others Fail to uninstall protocols attached on the device. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextIn; + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + + // + // Disable Keyboard + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSimpleTextInProtocolGuid, + (VOID **) &SimpleTextIn, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiSimpleTextInputExProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (SimpleTextIn); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiSimpleTextInProtocolGuid, + &BiosKeyboardPrivate->SimpleTextIn, + &gEfiSimpleTextInputExProtocolGuid, + &BiosKeyboardPrivate->SimpleTextInputEx, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Release the IsaIo protocol on the controller handle + // + gBS->CloseProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Free other resources + // + gBS->CloseEvent ((BiosKeyboardPrivate->SimpleTextIn).WaitForKey); + gBS->CloseEvent (BiosKeyboardPrivate->TimerEvent); + gBS->CloseEvent (BiosKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx); + BiosKeyboardFreeNotifyList (&BiosKeyboardPrivate->NotifyList); + + FreePool (BiosKeyboardPrivate); + + return EFI_SUCCESS; +} + +/** + Read data byte from output buffer of Keyboard Controller without delay and waiting for buffer-empty state. + + @param BiosKeyboardPrivate Keyboard instance pointer. + + @return The data byte read from output buffer of Keyboard Controller from data port which often is port 60H. + +**/ +UINT8 +KeyReadDataRegister ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate + ) +{ + UINT8 Data; + + // + // Use IsaIo protocol to perform IO operations + // + BiosKeyboardPrivate->IsaIo->Io.Read ( + BiosKeyboardPrivate->IsaIo, + EfiIsaIoWidthUint8, + BiosKeyboardPrivate->DataRegisterAddress, + 1, + &Data + ); + + return Data; +} + +/** + Read status byte from status register of Keyboard Controller without delay and waiting for buffer-empty state. + + @param BiosKeyboardPrivate Keyboard instance pointer. + + @return The status byte read from status register of Keyboard Controller from command port which often is port 64H. + +**/ +UINT8 +KeyReadStatusRegister ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate + ) +{ + UINT8 Data; + + // + // Use IsaIo protocol to perform IO operations + // + BiosKeyboardPrivate->IsaIo->Io.Read ( + BiosKeyboardPrivate->IsaIo, + EfiIsaIoWidthUint8, + BiosKeyboardPrivate->StatusRegisterAddress, + 1, + &Data + ); + + return Data; +} + +/** + Write command byte to control register of Keyboard Controller without delay and waiting for buffer-empty state. + + @param BiosKeyboardPrivate Keyboard instance pointer. + @param Data Data byte to write. + +**/ +VOID +KeyWriteCommandRegister ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, + IN UINT8 Data + ) +{ + // + // Use IsaIo protocol to perform IO operations + // + BiosKeyboardPrivate->IsaIo->Io.Write ( + BiosKeyboardPrivate->IsaIo, + EfiIsaIoWidthUint8, + BiosKeyboardPrivate->CommandRegisterAddress, + 1, + &Data + ); +} + +/** + Write data byte to input buffer or input/output ports of Keyboard Controller without delay and waiting for buffer-empty state. + + @param BiosKeyboardPrivate Keyboard instance pointer. + @param Data Data byte to write. + +**/ +VOID +KeyWriteDataRegister ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, + IN UINT8 Data + ) +{ + // + // Use IsaIo protocol to perform IO operations + // + BiosKeyboardPrivate->IsaIo->Io.Write ( + BiosKeyboardPrivate->IsaIo, + EfiIsaIoWidthUint8, + BiosKeyboardPrivate->DataRegisterAddress, + 1, + &Data + ); +} + +/** + Read data byte from output buffer of Keyboard Controller with delay and waiting for buffer-empty state. + + @param BiosKeyboardPrivate Keyboard instance pointer. + @param Data The pointer for data that being read out. + + @retval EFI_SUCCESS The data byte read out successfully. + @retval EFI_TIMEOUT Timeout occurred during reading out data byte. + +**/ +EFI_STATUS +KeyboardRead ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, + OUT UINT8 *Data + ) +{ + UINT32 TimeOut; + UINT32 RegFilled; + + TimeOut = 0; + RegFilled = 0; + + // + // wait till output buffer full then perform the read + // + for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) { + if ((KeyReadStatusRegister (BiosKeyboardPrivate) & KBC_STSREG_VIA64_OUTB) != 0) { + RegFilled = 1; + *Data = KeyReadDataRegister (BiosKeyboardPrivate); + break; + } + + gBS->Stall (30); + } + + if (RegFilled == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +/** + Write data byte to input buffer or input/output ports of Keyboard Controller with delay and waiting for buffer-empty state. + + @param BiosKeyboardPrivate Keyboard instance pointer. + @param Data Data byte to write. + + @retval EFI_SUCCESS The data byte is written successfully. + @retval EFI_TIMEOUT Timeout occurred during writing. + +**/ +EFI_STATUS +KeyboardWrite ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, + IN UINT8 Data + ) +{ + UINT32 TimeOut; + UINT32 RegEmptied; + + TimeOut = 0; + RegEmptied = 0; + + // + // wait for input buffer empty + // + for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) { + if ((KeyReadStatusRegister (BiosKeyboardPrivate) & KBC_STSREG_VIA64_INPB) == 0) { + RegEmptied = 1; + break; + } + + gBS->Stall (30); + } + + if (RegEmptied == 0) { + return EFI_TIMEOUT; + } + // + // Write it + // + KeyWriteDataRegister (BiosKeyboardPrivate, Data); + + return EFI_SUCCESS; +} + +/** + Write command byte to control register of Keyboard Controller with delay and waiting for buffer-empty state. + + @param BiosKeyboardPrivate Keyboard instance pointer. + @param Data Command byte to write. + + @retval EFI_SUCCESS The command byte is written successfully. + @retval EFI_TIMEOUT Timeout occurred during writing. + +**/ +EFI_STATUS +KeyboardCommand ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, + IN UINT8 Data + ) +{ + UINT32 TimeOut; + UINT32 RegEmptied; + + TimeOut = 0; + RegEmptied = 0; + + // + // Wait For Input Buffer Empty + // + for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) { + if ((KeyReadStatusRegister (BiosKeyboardPrivate) & KBC_STSREG_VIA64_INPB) == 0) { + RegEmptied = 1; + break; + } + + gBS->Stall (30); + } + + if (RegEmptied == 0) { + return EFI_TIMEOUT; + } + // + // issue the command + // + KeyWriteCommandRegister (BiosKeyboardPrivate, Data); + + // + // Wait For Input Buffer Empty again + // + RegEmptied = 0; + for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) { + if ((KeyReadStatusRegister (BiosKeyboardPrivate) & KBC_STSREG_VIA64_INPB) == 0) { + RegEmptied = 1; + break; + } + + gBS->Stall (30); + } + + if (RegEmptied == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +/** + Wait for a specific value to be presented in + Data register of Keyboard Controller by keyboard and then read it, + used in keyboard commands ack + + @param BiosKeyboardPrivate Keyboard instance pointer. + @param Value The value to be waited for + @param WaitForValueTimeOut The limit of microseconds for timeout + + @retval EFI_SUCCESS The command byte is written successfully. + @retval EFI_TIMEOUT Timeout occurred during writing. + +**/ +EFI_STATUS +KeyboardWaitForValue ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, + IN UINT8 Value, + IN UINTN WaitForValueTimeOut + ) +{ + UINT8 Data; + UINT32 TimeOut; + UINT32 SumTimeOut; + UINT32 GotIt; + + GotIt = 0; + TimeOut = 0; + SumTimeOut = 0; + + // + // Make sure the initial value of 'Data' is different from 'Value' + // + Data = 0; + if (Data == Value) { + Data = 1; + } + // + // Read from 8042 (multiple times if needed) + // until the expected value appears + // use SumTimeOut to control the iteration + // + while (1) { + // + // Perform a read + // + for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) { + if ((KeyReadStatusRegister (BiosKeyboardPrivate) & KBC_STSREG_VIA64_OUTB) != 0) { + Data = KeyReadDataRegister (BiosKeyboardPrivate); + break; + } + + gBS->Stall (30); + } + + SumTimeOut += TimeOut; + + if (Data == Value) { + GotIt = 1; + break; + } + + if (SumTimeOut >= WaitForValueTimeOut) { + break; + } + } + // + // Check results + // + if (GotIt != 0) { + return EFI_SUCCESS; + } else { + return EFI_TIMEOUT; + } + +} + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param BiosKeyboardPrivate Bioskeyboard driver private structure. + @param KeyData A pointer to a buffer that is filled in with the keystroke + state data for the key that was pressed. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keystroke information was not returned due to + hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +KeyboardReadKeyStrokeWorker ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, + OUT EFI_KEY_DATA *KeyData + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + if (KeyData == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Use TimerEvent callback funciton to check whether there's any key pressed + // + + // + // Stall 1ms to give a chance to let other driver interrupt this routine for their timer event. + // Csm will be used to check whether there is a key pending, but the csm will disable all + // interrupt before switch to compatibility16, which mean all the efiCompatibility timer + // event will stop work during the compatibility16. And If a caller recursivly invoke this function, + // e.g. OS loader, other drivers which are driven by timer event will have a bad performance during this period, + // e.g. usb keyboard driver. + // Add a stall period can greatly increate other driver performance during the WaitForKey is recursivly invoked. + // 1ms delay will make little impact to the thunk keyboard driver, and user can not feel the delay at all when input. + // + gBS->Stall (1000); + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + BiosKeyboardTimerHandler (NULL, BiosKeyboardPrivate); + // + // If there's no key, just return + // + Status = CheckQueue (&BiosKeyboardPrivate->Queue); + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (OldTpl); + return EFI_NOT_READY; + } + + Status = Dequeue (&BiosKeyboardPrivate->Queue, KeyData); + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + +// +// EFI Simple Text In Protocol Functions +// +/** + Reset the Keyboard and do BAT test for it, if (ExtendedVerification == TRUE) then do some extra keyboard validations. + + @param This Pointer of simple text Protocol. + @param ExtendedVerification Whether perform the extra validation of keyboard. True: perform; FALSE: skip. + + @retval EFI_SUCCESS The command byte is written successfully. + @retval EFI_DEVICE_ERROR Errors occurred during reseting keyboard. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + EFI_STATUS Status; + EFI_TPL OldTpl; + UINT8 CommandByte; + BOOLEAN MouseEnable; + EFI_INPUT_KEY Key; + + MouseEnable = FALSE; + BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (This); + + // + // 1 + // Report reset progress code + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_RESET, + BiosKeyboardPrivate->DevicePath + ); + + // + // Report a Progress Code for clearing the keyboard buffer + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_CLEAR_BUFFER, + BiosKeyboardPrivate->DevicePath + ); + + // + // 2 + // Raise TPL to avoid mouse operation impact + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // + // Exhaust output buffer data + // + do { + Status = BiosKeyboardReadKeyStroke ( + This, + &Key + ); + } while (!EFI_ERROR (Status)); + // + // 3 + // check for KBC itself firstly for setted-up already or not by reading SYSF (bit2) of status register via 64H + // if not skip step 4&5 and jump to step 6 to selftest KBC and report this + // else go step 4 + // + if ((KeyReadStatusRegister (BiosKeyboardPrivate) & KBC_STSREG_VIA64_SYSF) != 0) { + // + // 4 + // CheckMouseStatus to decide enable it later or not + // + // + // Read the command byte of KBC + // + Status = KeyboardCommand ( + BiosKeyboardPrivate, + KBC_CMDREG_VIA64_CMDBYTE_R + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = KeyboardRead ( + BiosKeyboardPrivate, + &CommandByte + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // Check mouse enabled or not before + // + if ((CommandByte & KB_CMMBYTE_DISABLE_AUX) != 0) { + MouseEnable = FALSE; + } else { + MouseEnable = TRUE; + } + // + // 5 + // disable mouse (via KBC) and Keyborad device + // + Status = KeyboardCommand ( + BiosKeyboardPrivate, + KBC_CMDREG_VIA64_AUX_DISABLE + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = KeyboardCommand ( + BiosKeyboardPrivate, + KBC_CMDREG_VIA64_KB_DISABLE + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + } else { + // + // 6 + // KBC Self Test + // + // + // Report a Progress Code for performing a self test on the keyboard controller + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_SELF_TEST, + BiosKeyboardPrivate->DevicePath + ); + + Status = KeyboardCommand ( + BiosKeyboardPrivate, + KBC_CMDREG_VIA64_KBC_SLFTEST + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = KeyboardWaitForValue ( + BiosKeyboardPrivate, + KBC_CMDECHO_KBCSLFTEST_OK, + KEYBOARD_WAITFORVALUE_TIMEOUT + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + } + // + // 7 + // Disable Mouse interface, enable Keyboard interface and declare selftest success + // + // Mouse device will block keyboard interface before it be configured, so we should disable mouse first. + // + Status = KeyboardCommand ( + BiosKeyboardPrivate, + KBC_CMDREG_VIA64_CMDBYTE_W + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + // + // Write 8042 Command Byte, set System Flag + // While at the same time: + // 1. disable mouse interface, + // 2. enable kbd interface, + // 3. enable PC/XT kbd translation mode + // 4. enable mouse and kbd interrupts + // + //Command Byte bits: + // 7: Reserved + // 6: PC/XT translation mode + // 5: Disable Auxiliary device interface + // 4: Disable keyboard interface + // 3: Reserved + // 2: System Flag + // 1: Enable Auxiliary device interrupt + // 0: Enable Keyboard interrupt + // + CommandByte = 0; + Status = KeyboardWrite ( + BiosKeyboardPrivate, + (UINT8) ((CommandByte & + (~KB_CMMBYTE_DISABLE_KB)) | + KB_CMMBYTE_KSCAN2UNI_COV | + KB_CMMBYTE_ENABLE_AUXINT | + KB_CMMBYTE_ENABLE_KBINT | + KB_CMMBYTE_SLFTEST_SUCC | + KB_CMMBYTE_DISABLE_AUX) + ); + + // + // For reseting keyboard is not mandatory before booting OS and sometimes keyboard responses very slow, + // so we only do the real reseting for keyboard when user asks, and normally during booting an OS, it's skipped. + // Call CheckKeyboardConnect() to check whether keyboard is connected, if it is not connected, + // Real reset will not do. + // + if (ExtendedVerification && CheckKeyboardConnect (BiosKeyboardPrivate)) { + // + // 8 + // Send keyboard reset command then read ACK + // + Status = KeyboardWrite ( + BiosKeyboardPrivate, + KBC_INPBUF_VIA60_KBRESET + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = KeyboardWaitForValue ( + BiosKeyboardPrivate, + KBC_CMDECHO_ACK, + KEYBOARD_WAITFORVALUE_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // 9 + // Wait for keyboard return test OK. + // + Status = KeyboardWaitForValue ( + BiosKeyboardPrivate, + KBC_CMDECHO_BATTEST_OK, + KEYBOARD_WAITFORVALUE_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // 10 + // set keyboard scan code set = 02 (standard configuration) + // + Status = KeyboardWrite ( + BiosKeyboardPrivate, + KBC_INPBUF_VIA60_KBSCODE + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = KeyboardWaitForValue ( + BiosKeyboardPrivate, + KBC_CMDECHO_ACK, + KEYBOARD_WAITFORVALUE_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = KeyboardWrite ( + BiosKeyboardPrivate, + KBC_INPBUF_VIA60_SCODESET2 + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = KeyboardWaitForValue ( + BiosKeyboardPrivate, + KBC_CMDECHO_ACK, + KEYBOARD_WAITFORVALUE_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // 11 + // enable keyboard itself (not via KBC) by writing CMD F4 via 60H + // + Status = KeyboardWrite ( + BiosKeyboardPrivate, + KBC_INPBUF_VIA60_KBEN + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = KeyboardWaitForValue ( + BiosKeyboardPrivate, + KBC_CMDECHO_ACK, + KEYBOARD_WAITFORVALUE_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // 12 + // Additional validation, do it as follow: + // 1). check for status register of PARE && TIM via 64H + // 2). perform KB checking by writing ABh via 64H + // + if ((KeyReadStatusRegister (BiosKeyboardPrivate) & (KBC_STSREG_VIA64_PARE | KBC_STSREG_VIA64_TIM)) != 0) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = KeyboardCommand ( + BiosKeyboardPrivate, + KBC_CMDREG_VIA64_KB_CKECK + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = KeyboardWaitForValue ( + BiosKeyboardPrivate, + KBC_CMDECHO_KBCHECK_OK, + KEYBOARD_WAITFORVALUE_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + } + // + // 13 + // Done for validating keyboard. Enable keyboard (via KBC) + // and recover the command byte to proper value + // + Status = KeyboardCommand ( + BiosKeyboardPrivate, + KBC_CMDREG_VIA64_KB_ENABLE + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + // + // 14 + // conditionally enable mouse (via KBC) + // + if (MouseEnable) { + Status = KeyboardCommand ( + BiosKeyboardPrivate, + KBC_CMDREG_VIA64_AUX_ENABLE + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + + } + } + +Exit: + // + // 15 + // resume priority of task level + // + gBS->RestoreTPL (OldTpl); + + return Status; + +} + +/** + Read out the scan code of the key that has just been stroked. + + @param This Pointer of simple text Protocol. + @param Key Pointer for store the key that read out. + + @retval EFI_SUCCESS The key is read out successfully. + @retval other The key reading failed. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ) +{ + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + EFI_STATUS Status; + EFI_KEY_DATA KeyData; + + BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (This); + + Status = KeyboardReadKeyStrokeWorker (BiosKeyboardPrivate, &KeyData); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY)); + + return EFI_SUCCESS; +} + +/** + Waiting on the keyboard event, if there's any key pressed by the user, signal the event + + @param Event The event that be siganlled when any key has been stroked. + @param Context Pointer of the protocol EFI_SIMPLE_TEXT_INPUT_PROTOCOL. + +**/ +VOID +EFIAPI +BiosKeyboardWaitForKey ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Stall 1ms to give a chance to let other driver interrupt this routine for their timer event. + // Csm will be used to check whether there is a key pending, but the csm will disable all + // interrupt before switch to compatibility16, which mean all the efiCompatibility timer + // event will stop work during the compatibility16. And If a caller recursivly invoke this function, + // e.g. UI setup or Shell, other drivers which are driven by timer event will have a bad performance during this period, + // e.g. usb keyboard driver. + // Add a stall period can greatly increate other driver performance during the WaitForKey is recursivly invoked. + // 1ms delay will make little impact to the thunk keyboard driver, and user can not feel the delay at all when input. + // + gBS->Stall (1000); + // + // Use TimerEvent callback funciton to check whether there's any key pressed + // + BiosKeyboardTimerHandler (NULL, BIOS_KEYBOARD_DEV_FROM_THIS (Context)); + + if (!EFI_ERROR (BiosKeyboardCheckForKey (Context))) { + gBS->SignalEvent (Event); + } +} + +/** + Check key buffer to get the key stroke status. + + @param This Pointer of the protocol EFI_SIMPLE_TEXT_IN_PROTOCOL. + + @retval EFI_SUCCESS A key is being pressed now. + @retval Other No key is now pressed. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardCheckForKey ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This + ) +{ + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + + BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (This); + + return CheckQueue (&BiosKeyboardPrivate->Queue); +} +// +// Private worker functions +// +#define TABLE_END 0x0 + +typedef struct _CONVERT_TABLE_ENTRY { + UINT16 ScanCode; + UINT16 EfiScanCode; +} CONVERT_TABLE_ENTRY; + +CONVERT_TABLE_ENTRY mConvertTable[] = { + { + 0x47, + SCAN_HOME + }, + { + 0x48, + SCAN_UP + }, + { + 0x49, + SCAN_PAGE_UP + }, + { + 0x4b, + SCAN_LEFT + }, + { + 0x4d, + SCAN_RIGHT + }, + { + 0x4f, + SCAN_END + }, + { + 0x50, + SCAN_DOWN + }, + { + 0x51, + SCAN_PAGE_DOWN + }, + { + 0x52, + SCAN_INSERT + }, + { + 0x53, + SCAN_DELETE + }, + // + // Function Keys are only valid if KeyChar == 0x00 + // This function does not require KeyChar to be 0x00 + // + { + 0x3b, + SCAN_F1 + }, + { + 0x3c, + SCAN_F2 + }, + { + 0x3d, + SCAN_F3 + }, + { + 0x3e, + SCAN_F4 + }, + { + 0x3f, + SCAN_F5 + }, + { + 0x40, + SCAN_F6 + }, + { + 0x41, + SCAN_F7 + }, + { + 0x42, + SCAN_F8 + }, + { + 0x43, + SCAN_F9 + }, + { + 0x44, + SCAN_F10 + }, + { + 0x85, + SCAN_F11 + }, + { + 0x86, + SCAN_F12 + }, + // + // Convert ALT + Fn keys + // + { + 0x68, + SCAN_F1 + }, + { + 0x69, + SCAN_F2 + }, + { + 0x6a, + SCAN_F3 + }, + { + 0x6b, + SCAN_F4 + }, + { + 0x6c, + SCAN_F5 + }, + { + 0x6d, + SCAN_F6 + }, + { + 0x6e, + SCAN_F7 + }, + { + 0x6f, + SCAN_F8 + }, + { + 0x70, + SCAN_F9 + }, + { + 0x71, + SCAN_F10 + }, + { + TABLE_END, + SCAN_NULL + }, +}; + +/** + Convert unicode combined with scan code of key to the counterpart of EFIScancode of it. + + @param KeyChar Unicode of key. + @param ScanCode Scan code of key. + + @return The value of EFI Scancode for the key. + @retval SCAN_NULL No corresponding value in the EFI convert table is found for the key. + +**/ +UINT16 +ConvertToEFIScanCode ( + IN CHAR16 KeyChar, + IN UINT16 ScanCode + ) +{ + UINT16 EfiScanCode; + UINT16 Index; + + if (KeyChar == CHAR_ESC) { + EfiScanCode = SCAN_ESC; + } else if (KeyChar == 0x00 || KeyChar == 0xe0) { + // + // Movement & Function Keys + // + for (Index = 0; (Index < sizeof (mConvertTable) / sizeof (CONVERT_TABLE_ENTRY)) && (mConvertTable[Index].ScanCode != TABLE_END); Index += 1) { + if (ScanCode == mConvertTable[Index].ScanCode) { + return mConvertTable[Index].EfiScanCode; + } + } + // + // Reach Table end, return default value + // + return SCAN_NULL; + } else { + return SCAN_NULL; + } + + return EfiScanCode; +} + +/** + Check whether there is Ps/2 Keyboard device in system by 0xF4 Keyboard Command + If Keyboard receives 0xF4, it will respond with 'ACK'. If it doesn't respond, the device + should not be in system. + + @param BiosKeyboardPrivate Keyboard Private Data Struture + + @retval TRUE Keyboard in System. + @retval FALSE Keyboard not in System. + +**/ +BOOLEAN +CheckKeyboardConnect ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + // + // enable keyboard itself and wait for its ack + // If can't receive ack, Keyboard should not be connected. + // + Status = KeyboardWrite ( + BiosKeyboardPrivate, + KBC_INPBUF_VIA60_KBEN + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Status = KeyboardWaitForValue ( + BiosKeyboardPrivate, + KBC_CMDECHO_ACK, + KEYBOARD_WAITFORVALUE_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +/** + Timer event handler: read a series of key stroke from 8042 + and put them into memory key buffer. + It is registered as running under TPL_NOTIFY + + @param Event The timer event + @param Context A BIOS_KEYBOARD_DEV pointer + +**/ +VOID +EFIAPI +BiosKeyboardTimerHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_TPL OldTpl; + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + EFI_IA32_REGISTER_SET Regs; + UINT8 KbFlag1; // 0040h:0017h - KEYBOARD - STATUS FLAGS 1 + UINT8 KbFlag2; // 0040h:0018h - KEYBOARD - STATUS FLAGS 2 + EFI_KEY_DATA KeyData; + LIST_ENTRY *Link; + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + + BiosKeyboardPrivate = Context; + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // if there is no key present, just return + // + if (BiosKeyboardPrivate->ExtendedKeyboard) { + Regs.H.AH = 0x11; + } else { + Regs.H.AH = 0x01; + } + + BiosKeyboardPrivate->LegacyBios->Int86 ( + BiosKeyboardPrivate->LegacyBios, + 0x16, + &Regs + ); + if (Regs.X.Flags.ZF != 0) { + gBS->RestoreTPL (OldTpl); + return; + } + + // + // Read the key + // + if (BiosKeyboardPrivate->ExtendedKeyboard) { + Regs.H.AH = 0x10; + } else { + Regs.H.AH = 0x00; + } + + BiosKeyboardPrivate->LegacyBios->Int86 ( + BiosKeyboardPrivate->LegacyBios, + 0x16, + &Regs + ); + + KeyData.Key.ScanCode = (UINT16) Regs.H.AH; + KeyData.Key.UnicodeChar = (UINT16) Regs.H.AL; + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID; + KeyData.KeyState.KeyToggleState = EFI_TOGGLE_STATE_VALID; + // + // Leagcy Bios use Int 9 which is IRQ1 interrupt handler to get keystroke scancode to KB buffer in BDA (BIOS DATE AREA), then + // Int 16 depend KB buffer and some key bits in BDA to translate the scancode to ASCII code, and return both the scancode and ASCII + // code to Int 16 caller. This translation process works well if the Int 9 could response user input in time. But in Tiano enviorment, the Int 9 + // will be disabled after the thunk call finish, which means if user crazy input during int 9 being disabled, some keystrokes will be lost when + // KB device own hardware buffer overflows. And if the lost keystroke code is CTRL or ALT or SHIFT release code, these function key flags bit + // in BDA will not be updated. So the Int 16 will believe the CTRL or ALT or SHIFT is still pressed, and Int 16 will translate later scancode + // to wrong ASCII code. We can increase the Thunk frequence to let Int 9 response in time, but this way will much hurt other dirvers + // performance, like USB. + // + // 1. If CTRL or ALT release code is missed, all later input keys will be translated to wrong ASCII codes which the Tiano cannot support. In + // this case, the KB input seems fail to work, and user input is blocked. To solve the problem, we can help to clear the CTRL or ALT flag in BDA + // after every Int 16 finish. Thus persist to press CTRL or ALT has same effection as only press one time. It is Ok, since user not often use the + // CTRL and ALT. + // + // 2. If SHIFT release code is missed, all later lowercase input will become capital. This is ugly, but not block user input. If user press the lost + // SHIFT again, the lowercase will come back to normal. Since user often use the SHIFT, it is not reasonable to help to clear the SHIFT flag in BDA, + // which will let persist to press SHIFT has same effection as only press one time. + // + //0040h:0017h - KEYBOARD - STATUS FLAGS 1 + // 7 INSert active + // 6 Caps Lock active + // 5 Num Lock active + // 4 Scroll Lock active + // 3 either Alt pressed + // 2 either Ctrl pressed + // 1 Left Shift pressed + // 0 Right Shift pressed + + + // + // Clear the CTRL and ALT BDA flag + // + KbFlag1 = *((UINT8 *) (UINTN) 0x417); // read the STATUS FLAGS 1 + KbFlag2 = *((UINT8 *) (UINTN) 0x418); // read STATUS FLAGS 2 + + // + // Record toggle state + // + if ((KbFlag1 & KB_CAPS_LOCK_BIT) == KB_CAPS_LOCK_BIT) { + KeyData.KeyState.KeyToggleState |= EFI_CAPS_LOCK_ACTIVE; + } + if ((KbFlag1 & KB_NUM_LOCK_BIT) == KB_NUM_LOCK_BIT) { + KeyData.KeyState.KeyToggleState |= EFI_NUM_LOCK_ACTIVE; + } + if ((KbFlag1 & KB_SCROLL_LOCK_BIT) == KB_SCROLL_LOCK_BIT) { + KeyData.KeyState.KeyToggleState |= EFI_SCROLL_LOCK_ACTIVE; + } + // + // Record shift state + // BUGBUG: Need add Menu key and Left/Right Logo key state in the future + // + if ((KbFlag1 & KB_ALT_PRESSED) == KB_ALT_PRESSED) { + KeyData.KeyState.KeyShiftState |= ((KbFlag2 & KB_LEFT_ALT_PRESSED) == KB_LEFT_ALT_PRESSED) ? EFI_LEFT_ALT_PRESSED : EFI_RIGHT_ALT_PRESSED; + } + if ((KbFlag1 & KB_CTRL_PRESSED) == KB_CTRL_PRESSED) { + KeyData.KeyState.KeyShiftState |= ((KbFlag2 & KB_LEFT_CTRL_PRESSED) == KB_LEFT_CTRL_PRESSED) ? EFI_LEFT_CONTROL_PRESSED : EFI_RIGHT_CONTROL_PRESSED; + } + if ((KbFlag1 & KB_LEFT_SHIFT_PRESSED) == KB_LEFT_SHIFT_PRESSED) { + KeyData.KeyState.KeyShiftState |= EFI_LEFT_SHIFT_PRESSED; + } + if ((KbFlag1 & KB_RIGHT_SHIFT_PRESSED) == KB_RIGHT_SHIFT_PRESSED) { + KeyData.KeyState.KeyShiftState |= EFI_RIGHT_SHIFT_PRESSED; + } + + // + // Clear left alt and left ctrl BDA flag + // + KbFlag2 &= ~(KB_LEFT_ALT_PRESSED | KB_LEFT_CTRL_PRESSED); + *((UINT8 *) (UINTN) 0x418) = KbFlag2; + KbFlag1 &= ~0x0C; + *((UINT8 *) (UINTN) 0x417) = KbFlag1; + + + // + // Output EFI input key and shift/toggle state + // + if (KeyData.Key.UnicodeChar == CHAR_NULL || KeyData.Key.UnicodeChar == CHAR_SCANCODE || KeyData.Key.UnicodeChar == CHAR_ESC) { + KeyData.Key.ScanCode = ConvertToEFIScanCode (KeyData.Key.UnicodeChar, KeyData.Key.ScanCode); + KeyData.Key.UnicodeChar = CHAR_NULL; + } else { + KeyData.Key.ScanCode = SCAN_NULL; + } + + // + // CSM16 has converted the Ctrl+[a-z] to [1-26], converted it back. + // + if ((KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) != 0) { + if (KeyData.Key.UnicodeChar >= 1 && KeyData.Key.UnicodeChar <= 26) { + if (((KeyData.KeyState.KeyShiftState & (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED)) != 0) == + ((KeyData.KeyState.KeyToggleState & EFI_CAPS_LOCK_ACTIVE) != 0) + ) { + KeyData.Key.UnicodeChar = (UINT16) (KeyData.Key.UnicodeChar + L'a' - 1); + } else { + KeyData.Key.UnicodeChar = (UINT16) (KeyData.Key.UnicodeChar + L'A' - 1); + } + } + } + + // + // Need not return associated shift state if a class of printable characters that + // are normally adjusted by shift modifiers. + // e.g. Shift Key + 'f' key = 'F'; Shift Key + 'F' key = 'f'. + // + if ((KeyData.Key.UnicodeChar >= L'A' && KeyData.Key.UnicodeChar <= L'Z') || + (KeyData.Key.UnicodeChar >= L'a' && KeyData.Key.UnicodeChar <= L'z') + ) { + KeyData.KeyState.KeyShiftState &= ~(EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED); + } + + // + // Invoke notification functions if exist + // + for (Link = BiosKeyboardPrivate->NotifyList.ForwardLink; Link != &BiosKeyboardPrivate->NotifyList; Link = Link->ForwardLink) { + CurrentNotify = CR ( + Link, + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) { + CurrentNotify->KeyNotificationFn (&KeyData); + } + } + + // + // Convert the Ctrl+[a-z] to Ctrl+[1-26] + // + if ((KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) != 0) { + if (KeyData.Key.UnicodeChar >= L'a' && KeyData.Key.UnicodeChar <= L'z') { + KeyData.Key.UnicodeChar = (UINT16) (KeyData.Key.UnicodeChar - L'a' + 1); + } else if (KeyData.Key.UnicodeChar >= L'A' && KeyData.Key.UnicodeChar <= L'Z') { + KeyData.Key.UnicodeChar = (UINT16) (KeyData.Key.UnicodeChar - L'A' + 1); + } + } + Enqueue (&BiosKeyboardPrivate->Queue, &KeyData); + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + + return ; +} + +/** + Free keyboard notify list. + + @param ListHead The list head + + @retval EFI_SUCCESS Free the notify list successfully + @retval EFI_INVALID_PARAMETER ListHead is invalid. + +**/ +EFI_STATUS +BiosKeyboardFreeNotifyList ( + IN OUT LIST_ENTRY *ListHead + ) +{ + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode; + + if (ListHead == NULL) { + return EFI_INVALID_PARAMETER; + } + while (!IsListEmpty (ListHead)) { + NotifyNode = CR ( + ListHead->ForwardLink, + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + RemoveEntryList (ListHead->ForwardLink); + gBS->FreePool (NotifyNode); + } + + return EFI_SUCCESS; +} + +/** + Check if key is registered. + + @param RegsiteredData A pointer to a buffer that is filled in with the keystroke + state data for the key that was registered. + @param InputData A pointer to a buffer that is filled in with the keystroke + state data for the key that was pressed. + + @retval TRUE Key be pressed matches a registered key. + @retval FLASE Match failed. + +**/ +BOOLEAN +IsKeyRegistered ( + IN EFI_KEY_DATA *RegsiteredData, + IN EFI_KEY_DATA *InputData + ) +{ + ASSERT (RegsiteredData != NULL && InputData != NULL); + + if ((RegsiteredData->Key.ScanCode != InputData->Key.ScanCode) || + (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar)) { + return FALSE; + } + + // + // Assume KeyShiftState/KeyToggleState = 0 in Registered key data means these state could be ignored. + // + if (RegsiteredData->KeyState.KeyShiftState != 0 && + RegsiteredData->KeyState.KeyShiftState != InputData->KeyState.KeyShiftState) { + return FALSE; + } + if (RegsiteredData->KeyState.KeyToggleState != 0 && + RegsiteredData->KeyState.KeyToggleState != InputData->KeyState.KeyToggleState) { + return FALSE; + } + + return TRUE; + +} + +/** + Waiting on the keyboard event, if there's any key pressed by the user, signal the event + + @param Event The event that be siganlled when any key has been stroked. + @param Context Pointer of the protocol EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + +**/ +VOID +EFIAPI +BiosKeyboardWaitForKeyEx ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + + BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (Context); + BiosKeyboardWaitForKey (Event, &BiosKeyboardPrivate->SimpleTextIn); + +} + +/** + Reset the input device and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + EFI_STATUS Status; + EFI_TPL OldTpl; + + BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This); + + Status = BiosKeyboardPrivate->SimpleTextIn.Reset ( + &BiosKeyboardPrivate->SimpleTextIn, + ExtendedVerification + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +} + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the keystroke + state data for the key that was pressed. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keystroke information was not returned due to + hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *KeyData + ) +{ + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + + if (KeyData == NULL) { + return EFI_INVALID_PARAMETER; + } + + BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This); + + return KeyboardReadKeyStrokeWorker (BiosKeyboardPrivate, KeyData); + +} + +/** + Set certain state for the input device. + + @param This Protocol instance pointer. + @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the + state for the input device. + + @retval EFI_SUCCESS The device state was set successfully. + @retval EFI_DEVICE_ERROR The device is not functioning correctly and could + not have the setting adjusted. + @retval EFI_UNSUPPORTED The device does not have the ability to set its state. + @retval EFI_INVALID_PARAMETER KeyToggleState is NULL. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardSetState ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_TOGGLE_STATE *KeyToggleState + ) +{ + EFI_STATUS Status; + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + EFI_TPL OldTpl; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + UINT8 Command; + + if (KeyToggleState == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID) { + return EFI_UNSUPPORTED; + } + + BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This); + // + // See if the Legacy BIOS Protocol is available + // + Status = gBS->LocateProtocol ( + &gEfiLegacyBiosProtocolGuid, + NULL, + (VOID **) &LegacyBios + ); + + ASSERT_EFI_ERROR (Status); + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + Command = 0; + if ((*KeyToggleState & EFI_CAPS_LOCK_ACTIVE) == EFI_CAPS_LOCK_ACTIVE) { + Command |= 4; + } + if ((*KeyToggleState & EFI_NUM_LOCK_ACTIVE) == EFI_NUM_LOCK_ACTIVE) { + Command |= 2; + } + if ((*KeyToggleState & EFI_SCROLL_LOCK_ACTIVE) == EFI_SCROLL_LOCK_ACTIVE) { + Command |= 1; + } + + Status = KeyboardWrite (BiosKeyboardPrivate, 0xed); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + Status = KeyboardWaitForValue (BiosKeyboardPrivate, 0xfa, KEYBOARD_WAITFORVALUE_TIMEOUT); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + Status = KeyboardWrite (BiosKeyboardPrivate, Command); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + // + // Call Legacy BIOS Protocol to set whatever is necessary + // + LegacyBios->UpdateKeyboardLedStatus (LegacyBios, Command); + + Status = EFI_SUCCESS; + + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + + return Status; + +} + +/** + Register a notification function for a particular keystroke for the input device. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the keystroke + information data for the key that was pressed. + @param KeyNotificationFunction Points to the function to be called when the key + sequence is typed specified by KeyData. + @param NotifyHandle Points to the unique handle assigned to the registered notification. + + + @retval EFI_SUCCESS The notification function was registered successfully. + @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necesssary data structures. + @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardRegisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_DATA *KeyData, + IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, + OUT EFI_HANDLE *NotifyHandle + ) +{ + EFI_STATUS Status; + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + EFI_TPL OldTpl; + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NewNotify; + LIST_ENTRY *Link; + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + + if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) { + return EFI_INVALID_PARAMETER; + } + + BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This); + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered. + // + for (Link = BiosKeyboardPrivate->NotifyList.ForwardLink; Link != &BiosKeyboardPrivate->NotifyList; Link = Link->ForwardLink) { + CurrentNotify = CR ( + Link, + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) { + if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) { + *NotifyHandle = CurrentNotify->NotifyHandle; + Status = EFI_SUCCESS; + goto Exit; + } + } + } + + // + // Allocate resource to save the notification function + // + + NewNotify = (BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY)); + if (NewNotify == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + NewNotify->Signature = BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE; + NewNotify->KeyNotificationFn = KeyNotificationFunction; + NewNotify->NotifyHandle = (EFI_HANDLE) NewNotify; + CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA)); + InsertTailList (&BiosKeyboardPrivate->NotifyList, &NewNotify->NotifyEntry); + + *NotifyHandle = NewNotify->NotifyHandle; + Status = EFI_SUCCESS; + +Exit: + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Remove a registered notification function from a particular keystroke. + + @param This Protocol instance pointer. + @param NotificationHandle The handle of the notification function being unregistered. + + @retval EFI_SUCCESS The notification function was unregistered successfully. + @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardUnregisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_HANDLE NotificationHandle + ) +{ + EFI_STATUS Status; + BIOS_KEYBOARD_DEV *BiosKeyboardPrivate; + EFI_TPL OldTpl; + LIST_ENTRY *Link; + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + + // + // Check incoming notification handle + // + if (NotificationHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (((BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) NotificationHandle)->Signature != BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This); + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + for (Link = BiosKeyboardPrivate->NotifyList.ForwardLink; Link != &BiosKeyboardPrivate->NotifyList; Link = Link->ForwardLink) { + CurrentNotify = CR ( + Link, + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (CurrentNotify->NotifyHandle == NotificationHandle) { + // + // Remove the notification function from NotifyList and free resources + // + RemoveEntryList (&CurrentNotify->NotifyEntry); + + Status = EFI_SUCCESS; + goto Exit; + } + } + + // + // Can not find the specified Notification Handle + // + Status = EFI_INVALID_PARAMETER; + +Exit: + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + The user Entry Point for module BiosKeyboard. The user code starts with this function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeBiosKeyboard( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gBiosKeyboardDriverBinding, + ImageHandle, + &gBiosKeyboardComponentName, + &gBiosKeyboardComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/BiosKeyboard.h b/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/BiosKeyboard.h new file mode 100644 index 0000000000..c9f86a1dbd --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/BiosKeyboard.h @@ -0,0 +1,744 @@ +/** @file + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _BIOS_KEYBOARD_H_ +#define _BIOS_KEYBOARD_H_ + + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Driver Binding Externs +// +extern EFI_DRIVER_BINDING_PROTOCOL gBiosKeyboardDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gBiosKeyboardComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gBiosKeyboardComponentName2; + + +#include + +// +// BISO Keyboard Defines +// +#define CHAR_SCANCODE 0xe0 +#define CHAR_ESC 0x1b + +#define KEYBOARD_8042_DATA_REGISTER 0x60 +#define KEYBOARD_8042_STATUS_REGISTER 0x64 +#define KEYBOARD_8042_COMMAND_REGISTER 0x64 + +#define KEYBOARD_TIMEOUT 65536 // 0.07s +#define KEYBOARD_WAITFORVALUE_TIMEOUT 1000000 // 1s +#define KEYBOARD_BAT_TIMEOUT 4000000 // 4s +#define KEYBOARD_TIMER_INTERVAL 200000 // 0.02s +// KEYBOARD COMMAND BYTE -- read by writing command KBC_CMDREG_VIA64_CMDBYTE_R to 64H, then read from 60H +// write by wrting command KBC_CMDREG_VIA64_CMDBYTE_W to 64H, then write to 60H +// 7: Reserved +// 6: PC/XT translation mode convert +// 5: Disable Auxiliary device interface +// 4: Disable keyboard interface +// 3: Reserved +// 2: System Flag: selftest successful +// 1: Enable Auxiliary device interrupt +// 0: Enable Keyboard interrupt ) +// +#define KB_CMMBYTE_KSCAN2UNI_COV (0x1 << 6) +#define KB_CMMBYTE_DISABLE_AUX (0x1 << 5) +#define KB_CMMBYTE_DISABLE_KB (0x1 << 4) +#define KB_CMMBYTE_SLFTEST_SUCC (0x1 << 2) +#define KB_CMMBYTE_ENABLE_AUXINT (0x1 << 1) +#define KB_CMMBYTE_ENABLE_KBINT (0x1 << 0) + +// +// KEYBOARD CONTROLLER STATUS REGISTER - read from 64h +// 7: Parity error +// 6: General time out +// 5: Output buffer holds data for AUX +// 4: Keyboard is not locked +// 3: Command written via 64h / Data written via 60h +// 2: KBC self-test successful / Power-on reset +// 1: Input buffer holds CPU data / empty +// 0: Output buffer holds keyboard data / empty +// +#define KBC_STSREG_VIA64_PARE (0x1 << 7) +#define KBC_STSREG_VIA64_TIM (0x1 << 6) +#define KBC_STSREG_VIA64_AUXB (0x1 << 5) +#define KBC_STSREG_VIA64_KEYL (0x1 << 4) +#define KBC_STSREG_VIA64_C_D (0x1 << 3) +#define KBC_STSREG_VIA64_SYSF (0x1 << 2) +#define KBC_STSREG_VIA64_INPB (0x1 << 1) +#define KBC_STSREG_VIA64_OUTB (0x1 << 0) + +// +// COMMANDs of KEYBOARD CONTROLLER COMMAND REGISTER - write to 64h +// +#define KBC_CMDREG_VIA64_CMDBYTE_R 0x20 +#define KBC_CMDREG_VIA64_CMDBYTE_W 0x60 +#define KBC_CMDREG_VIA64_AUX_DISABLE 0xA7 +#define KBC_CMDREG_VIA64_AUX_ENABLE 0xA8 +#define KBC_CMDREG_VIA64_KBC_SLFTEST 0xAA +#define KBC_CMDREG_VIA64_KB_CKECK 0xAB +#define KBC_CMDREG_VIA64_KB_DISABLE 0xAD +#define KBC_CMDREG_VIA64_KB_ENABLE 0xAE +#define KBC_CMDREG_VIA64_INTP_LOW_R 0xC0 +#define KBC_CMDREG_VIA64_INTP_HIGH_R 0xC2 +#define KBC_CMDREG_VIA64_OUTP_R 0xD0 +#define KBC_CMDREG_VIA64_OUTP_W 0xD1 +#define KBC_CMDREG_VIA64_OUTB_KB_W 0xD2 +#define KBC_CMDREG_VIA64_OUTB_AUX_W 0xD3 +#define KBC_CMDREG_VIA64_AUX_W 0xD4 + +// +// echos of KEYBOARD CONTROLLER COMMAND - read from 60h +// +#define KBC_CMDECHO_KBCSLFTEST_OK 0x55 +#define KBC_CMDECHO_KBCHECK_OK 0x00 +#define KBC_CMDECHO_ACK 0xFA +#define KBC_CMDECHO_BATTEST_OK 0xAA +#define KBC_CMDECHO_BATTEST_FAILE 0xFC + +// +// OUTPUT PORT COMMANDs - write port by writing KBC_CMDREG_VIA64_OUTP_W via 64H, then write the command to 60H +// drive data and clock of KB to high for at least 500us for BAT needs +// +#define KBC_OUTPORT_DCHIGH_BAT 0xC0 +// +// scan code set type +// +#define KBC_INPBUF_VIA60_SCODESET1 0x01 +#define KBC_INPBUF_VIA60_SCODESET2 0x02 +#define KBC_INPBUF_VIA60_SCODESET3 0x03 + +// +// COMMANDs written to INPUT BUFFER - write to 60h +// +#define KBC_INPBUF_VIA60_KBECHO 0xEE +#define KBC_INPBUF_VIA60_KBSCODE 0xF0 +#define KBC_INPBUF_VIA60_KBTYPE 0xF2 +#define KBC_INPBUF_VIA60_KBDELAY 0xF3 +#define KBC_INPBUF_VIA60_KBEN 0xF4 +#define KBC_INPBUF_VIA60_KBSTDDIS 0xF5 +#define KBC_INPBUF_VIA60_KBSTDEN 0xF6 +#define KBC_INPBUF_VIA60_KBRESEND 0xFE +#define KBC_INPBUF_VIA60_KBRESET 0xFF + +// +// 0040h:0017h - KEYBOARD - STATUS FLAGS 1 +// 7 INSert active +// 6 Caps Lock active +// 5 Num Lock active +// 4 Scroll Lock active +// 3 either Alt pressed +// 2 either Ctrl pressed +// 1 Left Shift pressed +// 0 Right Shift pressed +// +// 0040h:0018h - KEYBOARD - STATUS FLAGS 2 +// 7: insert key is depressed +// 6: caps-lock key is depressed (does not work well) +// 5: num-lock key is depressed (does not work well) +// 4: scroll lock key is depressed (does not work well) +// 3: suspend key has been toggled (does not work well) +// 2: system key is pressed and held (does not work well) +// 1: left ALT key is pressed +// 0: left CTRL key is pressed +// +#define KB_INSERT_BIT (0x1 << 7) +#define KB_CAPS_LOCK_BIT (0x1 << 6) +#define KB_NUM_LOCK_BIT (0x1 << 5) +#define KB_SCROLL_LOCK_BIT (0x1 << 4) +#define KB_ALT_PRESSED (0x1 << 3) +#define KB_CTRL_PRESSED (0x1 << 2) +#define KB_LEFT_SHIFT_PRESSED (0x1 << 1) +#define KB_RIGHT_SHIFT_PRESSED (0x1 << 0) + +#define KB_SUSPEND_PRESSED (0x1 << 3) +#define KB_SYSREQ_PRESSED (0x1 << 2) +#define KB_LEFT_ALT_PRESSED (0x1 << 1) +#define KB_LEFT_CTRL_PRESSED (0x1 << 0) + +// +// BIOS Keyboard Device Structure +// +#define BIOS_KEYBOARD_DEV_SIGNATURE SIGNATURE_32 ('B', 'K', 'B', 'D') +#define BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE SIGNATURE_32 ('c', 'b', 'k', 'h') + +typedef struct _BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY { + UINTN Signature; + EFI_HANDLE NotifyHandle; + EFI_KEY_DATA KeyData; + EFI_KEY_NOTIFY_FUNCTION KeyNotificationFn; + LIST_ENTRY NotifyEntry; +} BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY; + +#define QUEUE_MAX_COUNT 32 +typedef struct { + UINTN Front; + UINTN Rear; + EFI_KEY_DATA Buffer[QUEUE_MAX_COUNT]; +} SIMPLE_QUEUE; + +typedef struct { + UINTN Signature; + EFI_HANDLE Handle; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + EFI_ISA_IO_PROTOCOL *IsaIo; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL SimpleTextIn; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL SimpleTextInputEx; + UINT16 DataRegisterAddress; + UINT16 StatusRegisterAddress; + UINT16 CommandRegisterAddress; + BOOLEAN ExtendedKeyboard; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + // + // Buffer storing EFI_KEY_DATA + // + SIMPLE_QUEUE Queue; + + // + // Notification Function List + // + LIST_ENTRY NotifyList; + EFI_EVENT TimerEvent; + +} BIOS_KEYBOARD_DEV; + +#define BIOS_KEYBOARD_DEV_FROM_THIS(a) CR (a, BIOS_KEYBOARD_DEV, SimpleTextIn, BIOS_KEYBOARD_DEV_SIGNATURE) +#define TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS(a) \ + CR (a, \ + BIOS_KEYBOARD_DEV, \ + SimpleTextInputEx, \ + BIOS_KEYBOARD_DEV_SIGNATURE \ + ) + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gBiosKeyboardDriverBinding; + +// +// Driver Binding Protocol functions +// + +/** + Check whether the driver supports this device. + + @param This The Udriver binding protocol. + @param Controller The controller handle to check. + @param RemainingDevicePath The remaining device path. + + @retval EFI_SUCCESS The driver supports this controller. + @retval other This device isn't supported. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Starts the device with this driver. + + @param This The driver binding instance. + @param Controller Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The controller is controlled by the driver. + @retval Other This controller cannot be started. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop the device handled by this driver. + + @param This The driver binding protocol. + @param Controller The controller to release. + @param NumberOfChildren The number of handles in ChildHandleBuffer. + @param ChildHandleBuffer The array of child handle. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + @retval Others Fail to uninstall protocols attached on the device. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// Simple Text Input Protocol functions +// +/** + Reset the Keyboard and do BAT test for it, if (ExtendedVerification == TRUE) then do some extra keyboard validations. + + @param This Pointer of simple text Protocol. + @param ExtendedVerification Whether perform the extra validation of keyboard. True: perform; FALSE: skip. + + @retval EFI_SUCCESS The command byte is written successfully. + @retval EFI_DEVICE_ERROR Errors occurred during reseting keyboard. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Read out the scan code of the key that has just been stroked. + + @param This Pointer of simple text Protocol. + @param Key Pointer for store the key that read out. + + @retval EFI_SUCCESS The key is read out successfully. + @retval other The key reading failed. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ); + +// +// Private worker functions +// +/** + Waiting on the keyboard event, if there's any key pressed by the user, signal the event + + @param Event The event that be siganlled when any key has been stroked. + @param Context Pointer of the protocol EFI_SIMPLE_TEXT_INPUT_PROTOCOL. + +**/ +VOID +EFIAPI +BiosKeyboardWaitForKey ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Check key buffer to get the key stroke status. + + @param This Pointer of the protocol EFI_SIMPLE_TEXT_IN_PROTOCOL. + + @retval EFI_SUCCESS A key is being pressed now. + @retval Other No key is now pressed. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardCheckForKey ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This + ); + +/** + Convert unicode combined with scan code of key to the counterpart of EFIScancode of it. + + @param KeyChar Unicode of key. + @param ScanCode Scan code of key. + + @return The value of EFI Scancode for the key. + @retval SCAN_NULL No corresponding value in the EFI convert table is found for the key. + +**/ +UINT16 +ConvertToEFIScanCode ( + IN CHAR16 KeyChar, + IN UINT16 ScanCode + ); + +/** + Check whether there is Ps/2 Keyboard device in system by 0xF4 Keyboard Command + If Keyboard receives 0xF4, it will respond with 'ACK'. If it doesn't respond, the device + should not be in system. + + @param BiosKeyboardPrivate Keyboard Private Data Struture + + @retval TRUE Keyboard in System. + @retval FALSE Keyboard not in System. + +**/ +BOOLEAN +CheckKeyboardConnect ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate + ); + +/** + Timer event handler: read a series of key stroke from 8042 + and put them into memory key buffer. + It is registered as running under TPL_NOTIFY + + @param Event The timer event + @param Context A BIOS_KEYBOARD_DEV pointer + +**/ +VOID +EFIAPI +BiosKeyboardTimerHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Reset the input device and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the keystroke + state data for the key that was pressed. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keystroke information was not returned due to + hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *KeyData + ); + +/** + Set certain state for the input device. + + @param This Protocol instance pointer. + @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the + state for the input device. + + @retval EFI_SUCCESS The device state was set successfully. + @retval EFI_DEVICE_ERROR The device is not functioning correctly and could + not have the setting adjusted. + @retval EFI_UNSUPPORTED The device does not have the ability to set its state. + @retval EFI_INVALID_PARAMETER KeyToggleState is NULL. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardSetState ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_TOGGLE_STATE *KeyToggleState + ); + +/** + Register a notification function for a particular keystroke for the input device. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the keystroke + information data for the key that was pressed. + @param KeyNotificationFunction Points to the function to be called when the key + sequence is typed specified by KeyData. + @param NotifyHandle Points to the unique handle assigned to the registered notification. + + + @retval EFI_SUCCESS The notification function was registered successfully. + @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necesssary data structures. + @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardRegisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_DATA *KeyData, + IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, + OUT EFI_HANDLE *NotifyHandle + ); + +/** + Remove a registered notification function from a particular keystroke. + + @param This Protocol instance pointer. + @param NotificationHandle The handle of the notification function being unregistered. + + @retval EFI_SUCCESS The notification function was unregistered successfully. + @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardUnregisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_HANDLE NotificationHandle + ); + +/** + Wait for a specific value to be presented in + Data register of Keyboard Controller by keyboard and then read it, + used in keyboard commands ack + + @param BiosKeyboardPrivate Keyboard instance pointer. + @param Value The value to be waited for + @param WaitForValueTimeOut The limit of microseconds for timeout + + @retval EFI_SUCCESS The command byte is written successfully. + @retval EFI_TIMEOUT Timeout occurred during writing. + +**/ +EFI_STATUS +KeyboardWaitForValue ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, + IN UINT8 Value, + IN UINTN WaitForValueTimeOut + ); + +/** + Write data byte to input buffer or input/output ports of Keyboard Controller with delay and waiting for buffer-empty state. + + @param BiosKeyboardPrivate Keyboard instance pointer. + @param Data Data byte to write. + + @retval EFI_SUCCESS The data byte is written successfully. + @retval EFI_TIMEOUT Timeout occurred during writing. + +**/ +EFI_STATUS +KeyboardWrite ( + IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate, + IN UINT8 Data + ); + +/** + Free keyboard notify list. + + @param ListHead The list head + + @retval EFI_SUCCESS Free the notify list successfully + @retval EFI_INVALID_PARAMETER ListHead is invalid. + +**/ +EFI_STATUS +BiosKeyboardFreeNotifyList ( + IN OUT LIST_ENTRY *ListHead + ); + +/** + Check if key is registered. + + @param RegsiteredData A pointer to a buffer that is filled in with the keystroke + state data for the key that was registered. + @param InputData A pointer to a buffer that is filled in with the keystroke + state data for the key that was pressed. + + @retval TRUE Key be pressed matches a registered key. + @retval FLASE Match failed. + +**/ +BOOLEAN +IsKeyRegistered ( + IN EFI_KEY_DATA *RegsiteredData, + IN EFI_KEY_DATA *InputData + ); + +/** + Waiting on the keyboard event, if there's any key pressed by the user, signal the event + + @param Event The event that be siganlled when any key has been stroked. + @param Context Pointer of the protocol EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + +**/ +VOID +EFIAPI +BiosKeyboardWaitForKeyEx ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +#endif + diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/ComponentName.c b/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/ComponentName.c new file mode 100644 index 0000000000..caeecf70a7 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/ComponentName.c @@ -0,0 +1,183 @@ +/** @file + +Copyright (c) 2006, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "BiosKeyboard.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gBiosKeyboardComponentName = { + BiosKeyboardComponentNameGetDriverName, + BiosKeyboardComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gBiosKeyboardComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) BiosKeyboardComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) BiosKeyboardComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mBiosKeyboardDriverNameTable[] = { + { + "eng;en", + L"BIOS[INT16] Keyboard Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mBiosKeyboardDriverNameTable, + DriverName, + (BOOLEAN)(This == &gBiosKeyboardComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/ComponentName.h b/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/ComponentName.h new file mode 100644 index 0000000000..a20061b753 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/ComponentName.h @@ -0,0 +1,153 @@ +/** @file + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _BIOS_KEYBOARD_COMPONENT_NAME_H_ +#define _BIOS_KEYBOARD_COMPONENT_NAME_H_ + + +extern EFI_COMPONENT_NAME_PROTOCOL gBiosKeyboardComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gBiosKeyboardComponentName2; + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosKeyboardComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +#endif diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/KeyboardDxe.inf b/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/KeyboardDxe.inf new file mode 100644 index 0000000000..6166ba3ec3 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/KeyboardDxe.inf @@ -0,0 +1,68 @@ +## @file +# Component description file for BiosKeyboard module. +# +# Ps2 Keyboard driver by using Legacy Bios protocol service and IsaIo protocol service. +# This dirver uses legacy INT16 to get the key stroke status. +# +# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions +# of the BSD License which accompanies this distribution. The +# full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = KeyboardDxe + FILE_GUID = 5479662B-6AE4-49e8-A6BD-6DE4B625811F + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeBiosKeyboard + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gBiosKeyboardDriverBinding +# COMPONENT_NAME = gBiosKeyboardComponentName +# + +[Sources] + ComponentName.c + ComponentName.h + BiosKeyboard.c + BiosKeyboard.h + + +[Packages] + MdePkg/MdePkg.dec + IntelFrameworkPkg/IntelFrameworkPkg.dec + IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec + + +[LibraryClasses] + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + ReportStatusCodeLib + BaseMemoryLib + UefiLib + DebugLib + BaseLib + +[Protocols] + gEfiIsaIoProtocolGuid # PROTOCOL TO_START + gEfiSimpleTextInProtocolGuid # PROTOCOL BY_START + gEfiSimpleTextInputExProtocolGuid # PROTOCOL BY_START + gEfiLegacyBiosProtocolGuid # PROTOCOL TO_START + gEfiPs2PolicyProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/BiosSnp16.c b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/BiosSnp16.c new file mode 100644 index 0000000000..2ae8daca73 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/BiosSnp16.c @@ -0,0 +1,3507 @@ +/** @file + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "BiosSnp16.h" + + +/// +/// EFI Driver Binding Protocol Instance +/// +EFI_DRIVER_BINDING_PROTOCOL gBiosSnp16DriverBinding = { + BiosSnp16DriverBindingSupported, + BiosSnp16DriverBindingStart, + BiosSnp16DriverBindingStop, + 0x3, + NULL, + NULL +}; + +/// +/// This boolean is used to determine if we should release the cached vector during an error condition. +/// +BOOLEAN mCachedInt1A = FALSE; + +// +// Private worker functions; +// + +/** + Start the UNDI interface. + + @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure. + @param Ax PCI address of Undi device. + + @retval EFI_DEVICE_ERROR Fail to start 16 bit UNDI ROM. + @retval Others Status of start 16 bit UNDI ROM. +**/ +EFI_STATUS +Undi16SimpleNetworkStartUndi ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + UINT16 Ax + ); + +/** + Start the UNDI interface + + @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure. + + @retval EFI_DEVICE_ERROR Fail to start 16 bit UNDI ROM. + @retval Others Status of start 16 bit UNDI ROM. +**/ +EFI_STATUS +Undi16SimpleNetworkStopUndi ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice + ); + +/** + Stop the UNDI interface + + @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure. + + @retval EFI_DEVICE_ERROR Fail to stop 16 bit UNDI ROM. + @retval Others Status of stop 16 bit UNDI ROM. +**/ +EFI_STATUS +Undi16SimpleNetworkCleanupUndi ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice + ); + +/** + Get runtime information for Undi network interface + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_SUCCESS Sucess operation. + @retval Others Fail to get runtime information for Undi network interface. +**/ +EFI_STATUS +Undi16SimpleNetworkGetInformation ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ); + +/** + Get NIC type + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_SUCCESS Sucess operation. + @retval Others Fail to get NIC type. +**/ +EFI_STATUS +Undi16SimpleNetworkGetNicType ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ); + +/** + Get NDIS information + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_SUCCESS Sucess operation. + @retval Others Fail to get NDIS information. +**/ +EFI_STATUS +Undi16SimpleNetworkGetNdisInfo ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ); + +/** + Signal handlers for ExitBootServices event. + + Clean up any Real-mode UNDI residue from the system + + @param Event ExitBootServices event + @param Context +**/ +VOID +EFIAPI +Undi16SimpleNetworkEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Loads the undi driver. + + @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure. + + @retval EFI_SUCCESS - Successfully loads undi driver. + @retval EFI_NOT_FOUND - Doesn't find undi driver or undi driver load failure. +**/ +EFI_STATUS +Undi16SimpleNetworkLoadUndi ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice + ); + +/** + Unload 16 bit UNDI Option ROM from memory + + @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure. + + @return EFI_STATUS +**/ +EFI_STATUS +Undi16SimpleNetworkUnloadUndi ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice + ); + +/** + Entry point for EFI drivers. + + @param ImageHandle Handle that identifies the loaded image. + @param SystemTable System Table for this image. + + @return EFI_STATUS Return status from EfiLibInstallAllDriverProtocols. +**/ +EFI_STATUS +EFIAPI +BiosSnp16DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gBiosSnp16DriverBinding, + ImageHandle, + &gBiosSnp16ComponentName, + &gBiosSnp16ComponentName2 + ); +} + +// +// EFI Driver Binding Protocol Functions +// +/** + Tests to see if this driver supports a given controller. + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param RemainingDevicePath A pointer to the remaining portion of a device path. + + @retval EFI_SUCCESS The driver supports given controller. + @retval EFI_UNSUPPORT The driver doesn't support given controller. + @retval Other Other errors prevent driver finishing to test + if the driver supports given controller. +**/ +EFI_STATUS +EFIAPI +BiosSnp16DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + + // + // See if the Legacy BIOS Protocol is available + // + Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // See if this is a PCI Network Controller by looking at the Command register and + // Class Code Register + // + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0, sizeof (Pci) / sizeof (UINT32), &Pci); + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + Status = EFI_UNSUPPORTED; + if (Pci.Hdr.ClassCode[2] == PCI_CLASS_NETWORK) { + Status = EFI_SUCCESS; + } + +Done: + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + +/** + Starts the Snp device controller + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param RemainingDevicePath A pointer to the remaining portion of a device path. + + @retval EFI_SUCCESS - The device was started. + @retval EFI_DEVICE_ERROR - The device could not be started due to a device error. + @retval EFI_OUT_OF_RESOURCES - The request could not be completed due to a lack of resources. +**/ +EFI_STATUS +EFIAPI +BiosSnp16DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + EFI_DEV_PATH Node; + UINTN Index; + UINTN Index2; + UINTN Segment; + UINTN Bus; + UINTN Device; + UINTN Function; + UINTN Flags; + UINT64 Supports; + + SimpleNetworkDevice = NULL; + PciIo = NULL; + + // + // See if the Legacy BIOS Protocol is available + // + Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Open the IO Abstraction(s) needed + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= EFI_PCI_DEVICE_ENABLE; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } + + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Check to see if there is a legacy option ROM image associated with this PCI device + // + Status = LegacyBios->CheckPciRom ( + LegacyBios, + Controller, + NULL, + NULL, + &Flags + ); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Post the legacy option ROM if it is available. + // + Status = LegacyBios->InstallPciRom ( + LegacyBios, + Controller, + NULL, + &Flags, + NULL, + NULL, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Allocate memory for this SimpleNetwork device instance + // + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (EFI_SIMPLE_NETWORK_DEV), + (VOID **) &SimpleNetworkDevice + ); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + ZeroMem (SimpleNetworkDevice, sizeof (EFI_SIMPLE_NETWORK_DEV)); + + // + // Initialize the SimpleNetwork device instance + // + SimpleNetworkDevice->Signature = EFI_SIMPLE_NETWORK_DEV_SIGNATURE; + SimpleNetworkDevice->LegacyBios = LegacyBios; + SimpleNetworkDevice->BaseDevicePath = DevicePath; + SimpleNetworkDevice->PciIo = PciIo; + + // + // Initialize the Nii Protocol + // + SimpleNetworkDevice->Nii.Revision = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION; + SimpleNetworkDevice->Nii.Type = EfiNetworkInterfaceUndi; + + CopyMem (&SimpleNetworkDevice->Nii.StringId, "UNDI", 4); + + // + // Load 16 bit UNDI Option ROM into Memory + // + Status = Undi16SimpleNetworkLoadUndi (SimpleNetworkDevice); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_NET, "ERROR : Could not load UNDI. Status = %r\n", Status)); + goto Done; + } + + SimpleNetworkDevice->UndiLoaded = TRUE; + + // + // Call PXENV_START_UNDI - Initilizes the UNID interface for use. + // + PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function); + Status = Undi16SimpleNetworkStartUndi ( + SimpleNetworkDevice, + (UINT16) ((Bus << 0x8) | (Device << 0x3) | (Function)) + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_NET, "ERROR : Could not StartUndi. Status = %r\n", Status)); + goto Done; + } + // + // Initialize the Simple Network Protocol + // + DEBUG ((DEBUG_NET, "Initialize SimpleNetworkDevice instance\n")); + + SimpleNetworkDevice->SimpleNetwork.Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION; + SimpleNetworkDevice->SimpleNetwork.Start = Undi16SimpleNetworkStart; + SimpleNetworkDevice->SimpleNetwork.Stop = Undi16SimpleNetworkStop; + SimpleNetworkDevice->SimpleNetwork.Initialize = Undi16SimpleNetworkInitialize; + SimpleNetworkDevice->SimpleNetwork.Reset = Undi16SimpleNetworkReset; + SimpleNetworkDevice->SimpleNetwork.Shutdown = Undi16SimpleNetworkShutdown; + SimpleNetworkDevice->SimpleNetwork.ReceiveFilters = Undi16SimpleNetworkReceiveFilters; + SimpleNetworkDevice->SimpleNetwork.StationAddress = Undi16SimpleNetworkStationAddress; + SimpleNetworkDevice->SimpleNetwork.Statistics = Undi16SimpleNetworkStatistics; + SimpleNetworkDevice->SimpleNetwork.MCastIpToMac = Undi16SimpleNetworkMCastIpToMac; + SimpleNetworkDevice->SimpleNetwork.NvData = Undi16SimpleNetworkNvData; + SimpleNetworkDevice->SimpleNetwork.GetStatus = Undi16SimpleNetworkGetStatus; + SimpleNetworkDevice->SimpleNetwork.Transmit = Undi16SimpleNetworkTransmit; + SimpleNetworkDevice->SimpleNetwork.Receive = Undi16SimpleNetworkReceive; + SimpleNetworkDevice->SimpleNetwork.Mode = &(SimpleNetworkDevice->SimpleNetworkMode); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + Undi16SimpleNetworkWaitForPacket, + &SimpleNetworkDevice->SimpleNetwork, + &SimpleNetworkDevice->SimpleNetwork.WaitForPacket + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ERROR : Could not create event. Status = %r\n", Status)); + goto Done; + } + // + // Create an event to be signalled when ExitBootServices occurs in order + // to clean up nicely + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Undi16SimpleNetworkEvent, + NULL, + &gEfiEventExitBootServicesGuid, + &SimpleNetworkDevice->EfiBootEvent + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ERROR : Could not create event. Status = %r\n", Status)); + goto Done; + } + + // + // Create an event to be signalled when Legacy Boot occurs to clean up the IVT + // + Status = EfiCreateEventLegacyBootEx( + TPL_NOTIFY, + Undi16SimpleNetworkEvent, + NULL, + &SimpleNetworkDevice->LegacyBootEvent + ); + + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR,"ERROR : Could not create event. Status = %r\n",Status)); + goto Done; + } + + // + // Initialize the SimpleNetwork Mode Information + // + DEBUG ((DEBUG_NET, "Initialize Mode Information\n")); + + SimpleNetworkDevice->SimpleNetworkMode.State = EfiSimpleNetworkStopped; + SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize = 14; + SimpleNetworkDevice->SimpleNetworkMode.MacAddressChangeable = TRUE; + SimpleNetworkDevice->SimpleNetworkMode.MultipleTxSupported = TRUE; + SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | + EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST | + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS | + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + SimpleNetworkDevice->SimpleNetworkMode.MaxMCastFilterCount = MAXNUM_MCADDR; + + // + // Initialize the SimpleNetwork Private Information + // + DEBUG ((DEBUG_NET, "Initialize Private Information\n")); + + Status = BiosSnp16AllocatePagesBelowOneMb ( + sizeof (PXENV_UNDI_TBD_T) / EFI_PAGE_SIZE + 1, + (VOID **) &SimpleNetworkDevice->Xmit + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = BiosSnp16AllocatePagesBelowOneMb ( + 1, + &SimpleNetworkDevice->TxRealModeMediaHeader + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = BiosSnp16AllocatePagesBelowOneMb ( + 1, + &SimpleNetworkDevice->TxRealModeDataBuffer + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = BiosSnp16AllocatePagesBelowOneMb ( + 1, + &SimpleNetworkDevice->TxDestAddr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + SimpleNetworkDevice->Xmit->XmitOffset = (UINT16) (((UINT32)(UINTN) SimpleNetworkDevice->TxRealModeMediaHeader) & 0x000f); + + SimpleNetworkDevice->Xmit->XmitSegment = (UINT16) (((UINT32)(UINTN) SimpleNetworkDevice->TxRealModeMediaHeader) >> 4); + + SimpleNetworkDevice->Xmit->DataBlkCount = 1; + + SimpleNetworkDevice->Xmit->DataBlock[0].TDPtrType = 1; + SimpleNetworkDevice->Xmit->DataBlock[0].TDRsvdByte = 0; + + SimpleNetworkDevice->Xmit->DataBlock[0].TDDataPtrOffset = (UINT16) (((UINT32)(UINTN) SimpleNetworkDevice->TxRealModeDataBuffer) & 0x000f); + + SimpleNetworkDevice->Xmit->DataBlock[0].TDDataPtrSegment = (UINT16) (((UINT32)(UINTN) SimpleNetworkDevice->TxRealModeDataBuffer) >> 4); + + SimpleNetworkDevice->TxBufferFifo.First = 0; + SimpleNetworkDevice->TxBufferFifo.Last = 0; + + // + // Start() the SimpleNetwork device + // + DEBUG ((DEBUG_NET, "Start()\n")); + + Status = Undi16SimpleNetworkStart (&SimpleNetworkDevice->SimpleNetwork); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // GetInformation() the SimpleNetwork device + // + DEBUG ((DEBUG_NET, "GetInformation()\n")); + + Status = Undi16SimpleNetworkGetInformation (&SimpleNetworkDevice->SimpleNetwork); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Build the device path for the child device + // + ZeroMem (&Node, sizeof (Node)); + Node.DevPath.Type = MESSAGING_DEVICE_PATH; + Node.DevPath.SubType = MSG_MAC_ADDR_DP; + SetDevicePathNodeLength (&Node.DevPath, sizeof (MAC_ADDR_DEVICE_PATH)); + CopyMem ( + &Node.MacAddr.MacAddress, + &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress, + sizeof (EFI_MAC_ADDRESS) + ); + SimpleNetworkDevice->DevicePath = AppendDevicePathNode ( + SimpleNetworkDevice->BaseDevicePath, + &Node.DevPath + ); + + // + // GetNicType() the SimpleNetwork device + // + DEBUG ((DEBUG_NET, "GetNicType()\n")); + + Status = Undi16SimpleNetworkGetNicType (&SimpleNetworkDevice->SimpleNetwork); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // GetNdisInfo() the SimpleNetwork device + // + DEBUG ((DEBUG_NET, "GetNdisInfo()\n")); + + Status = Undi16SimpleNetworkGetNdisInfo (&SimpleNetworkDevice->SimpleNetwork); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Stop() the SimpleNetwork device + // + DEBUG ((DEBUG_NET, "Stop()\n")); + + Status = SimpleNetworkDevice->SimpleNetwork.Stop (&SimpleNetworkDevice->SimpleNetwork); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Print Mode information + // + DEBUG ((DEBUG_NET, "Mode->State = %d\n", SimpleNetworkDevice->SimpleNetworkMode.State)); + DEBUG ((DEBUG_NET, "Mode->HwAddressSize = %d\n", SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize)); + DEBUG ((DEBUG_NET, "Mode->MacAddressChangeable = %d\n", SimpleNetworkDevice->SimpleNetworkMode.MacAddressChangeable)); + DEBUG ((DEBUG_NET, "Mode->MultiplTxSupported = %d\n", SimpleNetworkDevice->SimpleNetworkMode.MultipleTxSupported)); + DEBUG ((DEBUG_NET, "Mode->NvRamSize = %d\n", SimpleNetworkDevice->SimpleNetworkMode.NvRamSize)); + DEBUG ((DEBUG_NET, "Mode->NvRamAccessSize = %d\n", SimpleNetworkDevice->SimpleNetworkMode.NvRamAccessSize)); + DEBUG ((DEBUG_NET, "Mode->ReceiveFilterSetting = %d\n", SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting)); + DEBUG ((DEBUG_NET, "Mode->IfType = %d\n", SimpleNetworkDevice->SimpleNetworkMode.IfType)); + DEBUG ((DEBUG_NET, "Mode->MCastFilterCount = %d\n", SimpleNetworkDevice->SimpleNetworkMode.MCastFilterCount)); + for (Index = 0; Index < SimpleNetworkDevice->SimpleNetworkMode.MCastFilterCount; Index++) { + DEBUG ((DEBUG_NET, " Filter[%02d] = ", Index)); + for (Index2 = 0; Index2 < 16; Index2++) { + DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->SimpleNetworkMode.MCastFilter[Index].Addr[Index2])); + } + + DEBUG ((DEBUG_NET, "\n")); + } + + DEBUG ((DEBUG_NET, "CurrentAddress = ")); + for (Index2 = 0; Index2 < 16; Index2++) { + DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress.Addr[Index2])); + } + + DEBUG ((DEBUG_NET, "\n")); + + DEBUG ((DEBUG_NET, "BroadcastAddress = ")); + for (Index2 = 0; Index2 < 16; Index2++) { + DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->SimpleNetworkMode.BroadcastAddress.Addr[Index2])); + } + + DEBUG ((DEBUG_NET, "\n")); + + DEBUG ((DEBUG_NET, "PermanentAddress = ")); + for (Index2 = 0; Index2 < 16; Index2++) { + DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress.Addr[Index2])); + } + + DEBUG ((DEBUG_NET, "\n")); + + // + // The network device was started, information collected, and stopped. + // Install protocol interfaces for the SimpleNetwork device. + // + DEBUG ((DEBUG_NET, "Install Protocol Interfaces on network interface\n")); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &SimpleNetworkDevice->Handle, + &gEfiSimpleNetworkProtocolGuid, + &SimpleNetworkDevice->SimpleNetwork, + &gEfiNetworkInterfaceIdentifierProtocolGuid, + &SimpleNetworkDevice->Nii, + &gEfiDevicePathProtocolGuid, + SimpleNetworkDevice->DevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Open PCI I/O from the newly created child handle + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + SimpleNetworkDevice->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + DEBUG ((DEBUG_INIT, "UNDI16 Driver : EFI_SUCCESS\n")); + +Done: + if (EFI_ERROR (Status)) { + if (SimpleNetworkDevice != NULL) { + + Undi16SimpleNetworkShutdown (&SimpleNetworkDevice->SimpleNetwork); + // + // CLOSE + SHUTDOWN + // + Undi16SimpleNetworkCleanupUndi (SimpleNetworkDevice); + // + // CLEANUP + // + Undi16SimpleNetworkStopUndi (SimpleNetworkDevice); + // + // STOP + // + if (SimpleNetworkDevice->UndiLoaded) { + Undi16SimpleNetworkUnloadUndi (SimpleNetworkDevice); + } + + if (SimpleNetworkDevice->SimpleNetwork.WaitForPacket != NULL) { + gBS->CloseEvent (SimpleNetworkDevice->SimpleNetwork.WaitForPacket); + } + + if (SimpleNetworkDevice->LegacyBootEvent != NULL) { + gBS->CloseEvent (SimpleNetworkDevice->LegacyBootEvent); + } + + if (SimpleNetworkDevice->EfiBootEvent != NULL) { + gBS->CloseEvent (SimpleNetworkDevice->EfiBootEvent); + } + + if (SimpleNetworkDevice->Xmit != NULL) { + gBS->FreePages ( + (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->Xmit, + sizeof (PXENV_UNDI_TBD_T) / EFI_PAGE_SIZE + 1 + ); + } + + if (SimpleNetworkDevice->TxRealModeMediaHeader != NULL) { + gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxRealModeMediaHeader, 1); + } + + if (SimpleNetworkDevice->TxRealModeDataBuffer != NULL) { + gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxRealModeDataBuffer, 1); + } + + if (SimpleNetworkDevice->TxDestAddr != NULL) { + gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxDestAddr, 1); + } + + gBS->FreePool (SimpleNetworkDevice); + + // + // Only restore the vector if it was cached. + // + if (mCachedInt1A) { + RestoreCachedVectorAddress (0x1A); + mCachedInt1A = FALSE; + } + } + + if (PciIo != NULL) { + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= EFI_PCI_DEVICE_ENABLE; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationDisable, + Supports, + NULL + ); + } + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + if (Status != EFI_OUT_OF_RESOURCES) { + Status = EFI_DEVICE_ERROR; + } + } + return Status; +} + +/** + Stops the device by given device controller. + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param ChildHandleBuffer An array of child handles to be freed. May be NULL if + NumberOfChildren is 0. + + @retval EFI_SUCCESS - The device was stopped. + @retval EFI_DEVICE_ERROR - The device could not be stopped due to a device error. +**/ +EFI_STATUS +EFIAPI +BiosSnp16DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + UINTN Index; + BOOLEAN AllChildrenStopped; + EFI_SIMPLE_NETWORK_PROTOCOL *SimpleNetwork; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 Supports; + + // + // Complete all outstanding transactions to Controller. + // Don't allow any new transaction to Controller to be started. + // + if (NumberOfChildren == 0) { + // + // Close the bus driver + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= EFI_PCI_DEVICE_ENABLE; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationDisable, + Supports, + NULL + ); + } + } + + Status = gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + } + return Status; + } + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &SimpleNetwork, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (SimpleNetwork); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + SimpleNetworkDevice->Handle, + &gEfiSimpleNetworkProtocolGuid, + &SimpleNetworkDevice->SimpleNetwork, + &gEfiNetworkInterfaceIdentifierProtocolGuid, + &SimpleNetworkDevice->Nii, + &gEfiDevicePathProtocolGuid, + SimpleNetworkDevice->DevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + + Undi16SimpleNetworkShutdown (&SimpleNetworkDevice->SimpleNetwork); + // + // CLOSE + SHUTDOWN + // + Undi16SimpleNetworkCleanupUndi (SimpleNetworkDevice); + // + // CLEANUP + // + Undi16SimpleNetworkStopUndi (SimpleNetworkDevice); + // + // STOP + // + if (SimpleNetworkDevice->UndiLoaded) { + Undi16SimpleNetworkUnloadUndi (SimpleNetworkDevice); + } + + if (SimpleNetworkDevice->SimpleNetwork.WaitForPacket != NULL) { + gBS->CloseEvent (SimpleNetworkDevice->SimpleNetwork.WaitForPacket); + } + + if (SimpleNetworkDevice->LegacyBootEvent != NULL) { + gBS->CloseEvent (SimpleNetworkDevice->LegacyBootEvent); + } + + if (SimpleNetworkDevice->EfiBootEvent != NULL) { + gBS->CloseEvent (SimpleNetworkDevice->EfiBootEvent); + } + + if (SimpleNetworkDevice->Xmit != NULL) { + gBS->FreePages ( + (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->Xmit, + sizeof (PXENV_UNDI_TBD_T) / EFI_PAGE_SIZE + 1 + ); + } + + if (SimpleNetworkDevice->TxRealModeMediaHeader != NULL) { + gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxRealModeMediaHeader, 1); + } + + if (SimpleNetworkDevice->TxRealModeDataBuffer != NULL) { + gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxRealModeDataBuffer, 1); + } + + if (SimpleNetworkDevice->TxDestAddr != NULL) { + gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxDestAddr, 1); + } + + gBS->FreePool (SimpleNetworkDevice); + } + + } + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +// +// FIFO Support Functions +// +/** + Judge whether transmit FIFO is full. + + @param Fifo Point to trasmit FIFO structure. + + @return BOOLEAN whether transmit FIFO is full. +**/ +BOOLEAN +SimpleNetworkTransmitFifoFull ( + EFI_SIMPLE_NETWORK_DEV_FIFO *Fifo + ) +{ + if (((Fifo->Last + 1) % EFI_SIMPLE_NETWORK_MAX_TX_FIFO_SIZE) == Fifo->First) { + return TRUE; + } + + return FALSE; +} + +/** + Judge whether transmit FIFO is empty. + + @param Fifo Point to trasmit FIFO structure. + + @return BOOLEAN whether transmit FIFO is empty. +**/ +BOOLEAN +SimpleNetworkTransmitFifoEmpty ( + EFI_SIMPLE_NETWORK_DEV_FIFO *Fifo + ) +{ + if (Fifo->Last == Fifo->First) { + return TRUE; + } + + return FALSE; +} + + +/** + Add data into transmit buffer. + + @param Fifo Point to trasmit FIFO structure. + @param Data The data point want to be added. + + @retval EFI_OUT_OF_RESOURCES FIFO is full + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +SimpleNetworkTransmitFifoAdd ( + EFI_SIMPLE_NETWORK_DEV_FIFO *Fifo, + VOID *Data + ) +{ + if (SimpleNetworkTransmitFifoFull (Fifo)) { + return EFI_OUT_OF_RESOURCES; + } + + Fifo->Data[Fifo->Last] = Data; + Fifo->Last = (Fifo->Last + 1) % EFI_SIMPLE_NETWORK_MAX_TX_FIFO_SIZE; + return EFI_SUCCESS; +} + +/** + Get a data and remove it from network transmit FIFO. + + @param Fifo Point to trasmit FIFO structure. + @param Data On return, point to the data point want to be got and removed. + + @retval EFI_OUT_OF_RESOURCES network transmit buffer is empty. + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +SimpleNetworkTransmitFifoRemove ( + EFI_SIMPLE_NETWORK_DEV_FIFO *Fifo, + VOID **Data + ) +{ + if (SimpleNetworkTransmitFifoEmpty (Fifo)) { + return EFI_OUT_OF_RESOURCES; + } + + *Data = Fifo->Data[Fifo->First]; + Fifo->First = (Fifo->First + 1) % EFI_SIMPLE_NETWORK_MAX_TX_FIFO_SIZE; + return EFI_SUCCESS; +} + +/** + Get recive filter setting according to EFI mask value. + + @param ReceiveFilterSetting filter setting EFI mask value. + + @return UINT16 Undi filter setting value. +**/ +UINT16 +Undi16GetPacketFilterSetting ( + UINTN ReceiveFilterSetting + ) +{ + UINT16 PktFilter; + + PktFilter = 0; + if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST) != 0) { + PktFilter |= FLTR_DIRECTED; + } + + if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) { + PktFilter |= FLTR_DIRECTED; + } + + if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) != 0) { + PktFilter |= FLTR_BRDCST; + } + + if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) { + PktFilter |= FLTR_PRMSCS; + } + + if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) { + PktFilter |= FLTR_PRMSCS; + // + // @bug : Do not know if this is right???? + // + } + // + // @bug : What is FLTR_SRC_RTG? + // + return PktFilter; +} + +/** + Get filter setting from multi cast buffer . + + @param Mode Point to mode structure. + @param McastBuffer The multi cast buffer + @param HwAddressSize Size of filter value. + +**/ +VOID +Undi16GetMCastFilters ( + IN EFI_SIMPLE_NETWORK_MODE *Mode, + IN OUT PXENV_UNDI_MCAST_ADDR_T *McastBuffer, + IN UINTN HwAddressSize + ) +{ + UINTN Index; + + // + // @bug : What if Mode->MCastFilterCount > MAXNUM_MCADDR? + // + McastBuffer->MCastAddrCount = (UINT16) Mode->MCastFilterCount; + for (Index = 0; Index < MAXNUM_MCADDR; Index++) { + if (Index < McastBuffer->MCastAddrCount) { + CopyMem (&McastBuffer->MCastAddr[Index], &Mode->MCastFilter[Index], HwAddressSize); + } else { + ZeroMem (&McastBuffer->MCastAddr[Index], HwAddressSize); + } + } +} +// +// Load 16 bit UNDI Option ROM into memory +// +/** + Loads the undi driver. + + @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure. + + @retval EFI_SUCCESS - Successfully loads undi driver. + @retval EFI_NOT_FOUND - Doesn't find undi driver or undi driver load failure. +**/ +EFI_STATUS +Undi16SimpleNetworkLoadUndi ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + UINTN RomAddress; + PCI_EXPANSION_ROM_HEADER *PciExpansionRomHeader; + PCI_DATA_STRUCTURE *PciDataStructure; + PCI_TYPE00 Pci; + + if (!mCachedInt1A) { + Status = CacheVectorAddress (0x1A); + if (!EFI_ERROR (Status)) { + mCachedInt1A = TRUE; + } + } + + PciIo = SimpleNetworkDevice->PciIo; + + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + + for (RomAddress = 0xc0000; RomAddress < 0xfffff; RomAddress += 0x800) { + + PciExpansionRomHeader = (PCI_EXPANSION_ROM_HEADER *) RomAddress; + + if (PciExpansionRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) { + continue; + } + + DEBUG ((DEBUG_INIT, "Option ROM found at %X\n", RomAddress)); + + PciDataStructure = (PCI_DATA_STRUCTURE *) (RomAddress + PciExpansionRomHeader->PcirOffset); + + if (PciDataStructure->Signature != PCI_DATA_STRUCTURE_SIGNATURE) { + continue; + } + + DEBUG ((DEBUG_INIT, "PCI Data Structure found at %X\n", PciDataStructure)); + + if (PciDataStructure->VendorId != Pci.Hdr.VendorId || PciDataStructure->DeviceId != Pci.Hdr.DeviceId) { + continue; + } + + DEBUG ( + (DEBUG_INIT, + "PCI device with matchinng VendorId and DeviceId (%d,%d)\n", + (UINTN) PciDataStructure->VendorId, + (UINTN) PciDataStructure->DeviceId) + ); + + Status = LaunchBaseCode (SimpleNetworkDevice, RomAddress); + + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Unload 16 bit UNDI Option ROM from memory + + @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure. + + @return EFI_STATUS +**/ +EFI_STATUS +Undi16SimpleNetworkUnloadUndi ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice + ) +{ + if (SimpleNetworkDevice->UndiLoaderTable != NULL) { + ZeroMem (SimpleNetworkDevice->UndiLoaderTable, SimpleNetworkDevice->UndiLoaderTablePages << EFI_PAGE_SHIFT); + gBS->FreePages ( + (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->UndiLoaderTable, + SimpleNetworkDevice->UndiLoaderTablePages + ); + } + + if (SimpleNetworkDevice->DestinationDataSegment != NULL) { + ZeroMem ( + SimpleNetworkDevice->DestinationDataSegment, + SimpleNetworkDevice->DestinationDataSegmentPages << EFI_PAGE_SHIFT + ); + gBS->FreePages ( + (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->DestinationDataSegment, + SimpleNetworkDevice->DestinationDataSegmentPages + ); + } + + if (SimpleNetworkDevice->DestinationStackSegment != NULL) { + ZeroMem ( + SimpleNetworkDevice->DestinationStackSegment, + SimpleNetworkDevice->DestinationStackSegmentPages << EFI_PAGE_SHIFT + ); + gBS->FreePages ( + (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->DestinationStackSegment, + SimpleNetworkDevice->DestinationStackSegmentPages + ); + } + + if (SimpleNetworkDevice->DestinationCodeSegment != NULL) { + ZeroMem ( + SimpleNetworkDevice->DestinationCodeSegment, + SimpleNetworkDevice->DestinationCodeSegmentPages << EFI_PAGE_SHIFT + ); + gBS->FreePages ( + (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->DestinationCodeSegment, + SimpleNetworkDevice->DestinationCodeSegmentPages + ); + } + + return EFI_SUCCESS; +} + +/** + Start the UNDI interface. + + @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure. + @param Ax PCI address of Undi device. + + @retval EFI_DEVICE_ERROR Fail to start 16 bit UNDI ROM. + @retval Others Status of start 16 bit UNDI ROM. +**/ +EFI_STATUS +Undi16SimpleNetworkStartUndi ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + UINT16 Ax + ) +{ + EFI_STATUS Status; + PXENV_START_UNDI_T Start; + + // + // Call 16 bit UNDI ROM to start the network interface + // + // + // @bug : What is this state supposed to be??? + // + Start.Status = INIT_PXE_STATUS; + Start.Ax = Ax; + Start.Bx = 0x0000; + Start.Dx = 0x0000; + Start.Di = 0x0000; + Start.Es = 0x0000; + + Status = PxeStartUndi (SimpleNetworkDevice, &Start); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (Start.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + + return Status; +} + + +/** + Stop the UNDI interface + + @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure. + + @retval EFI_DEVICE_ERROR Fail to stop 16 bit UNDI ROM. + @retval Others Status of stop 16 bit UNDI ROM. +**/ +EFI_STATUS +Undi16SimpleNetworkStopUndi ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice + ) +{ + EFI_STATUS Status; + PXENV_STOP_UNDI_T Stop; + + // + // Call 16 bit UNDI ROM to start the network interface + // + Stop.Status = INIT_PXE_STATUS; + + Status = PxeUndiStop (SimpleNetworkDevice, &Stop); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (Stop.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + + return Status; +} + +/** + Cleanup Unid network interface + + @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure. + + @retval EFI_DEVICE_ERROR Fail to cleanup 16 bit UNDI ROM. + @retval Others Status of cleanup 16 bit UNDI ROM. +**/ +EFI_STATUS +Undi16SimpleNetworkCleanupUndi ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice + ) +{ + EFI_STATUS Status; + PXENV_UNDI_CLEANUP_T Cleanup; + + // + // Call 16 bit UNDI ROM to cleanup the network interface + // + Cleanup.Status = INIT_PXE_STATUS; + + Status = PxeUndiCleanup (SimpleNetworkDevice, &Cleanup); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (Cleanup.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + + return Status; +} + +/** + Get runtime information for Undi network interface + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_SUCCESS Sucess operation. + @retval Others Fail to get runtime information for Undi network interface. +**/ +EFI_STATUS +Undi16SimpleNetworkGetInformation ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + UINTN Index; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkStarted: + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + default: + return EFI_DEVICE_ERROR; + } + // + // Call 16 bit UNDI ROM to start the network interface + // + ZeroMem (&SimpleNetworkDevice->GetInformation, sizeof (PXENV_UNDI_GET_INFORMATION_T)); + + SimpleNetworkDevice->GetInformation.Status = INIT_PXE_STATUS; + + Status = PxeUndiGetInformation (SimpleNetworkDevice, &SimpleNetworkDevice->GetInformation); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG ((DEBUG_NET, " GetInformation.Status = %d\n", SimpleNetworkDevice->GetInformation.Status)); + DEBUG ((DEBUG_NET, " GetInformation.BaseIo = %d\n", SimpleNetworkDevice->GetInformation.BaseIo)); + DEBUG ((DEBUG_NET, " GetInformation.IntNumber = %d\n", SimpleNetworkDevice->GetInformation.IntNumber)); + DEBUG ((DEBUG_NET, " GetInformation.MaxTranUnit = %d\n", SimpleNetworkDevice->GetInformation.MaxTranUnit)); + DEBUG ((DEBUG_NET, " GetInformation.HwType = %d\n", SimpleNetworkDevice->GetInformation.HwType)); + DEBUG ((DEBUG_NET, " GetInformation.HwAddrLen = %d\n", SimpleNetworkDevice->GetInformation.HwAddrLen)); + DEBUG ((DEBUG_NET, " GetInformation.ROMAddress = %d\n", SimpleNetworkDevice->GetInformation.ROMAddress)); + DEBUG ((DEBUG_NET, " GetInformation.RxBufCt = %d\n", SimpleNetworkDevice->GetInformation.RxBufCt)); + DEBUG ((DEBUG_NET, " GetInformation.TxBufCt = %d\n", SimpleNetworkDevice->GetInformation.TxBufCt)); + + DEBUG ((DEBUG_NET, " GetInformation.CurNodeAddr =")); + for (Index = 0; Index < 16; Index++) { + DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->GetInformation.CurrentNodeAddress[Index])); + } + + DEBUG ((DEBUG_NET, "\n")); + + DEBUG ((DEBUG_NET, " GetInformation.PermNodeAddr =")); + for (Index = 0; Index < 16; Index++) { + DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->GetInformation.PermNodeAddress[Index])); + } + + DEBUG ((DEBUG_NET, "\n")); + + // + // Check the status code from the 16 bit UNDI ROM + // + if (SimpleNetworkDevice->GetInformation.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // The information has been retrieved. Fill in Mode data. + // + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize = SimpleNetworkDevice->GetInformation.HwAddrLen; + + SimpleNetworkDevice->SimpleNetworkMode.MaxPacketSize = SimpleNetworkDevice->GetInformation.MaxTranUnit; + + SimpleNetworkDevice->SimpleNetworkMode.IfType = (UINT8) SimpleNetworkDevice->GetInformation.HwType; + + ZeroMem ( + &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress, + sizeof SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress + ); + + CopyMem ( + &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress, + &SimpleNetworkDevice->GetInformation.CurrentNodeAddress, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + + ZeroMem ( + &SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress, + sizeof SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress + ); + + CopyMem ( + &SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress, + &SimpleNetworkDevice->GetInformation.PermNodeAddress, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + + // + // hard code broadcast address - not avail in PXE2.1 + // + ZeroMem ( + &SimpleNetworkDevice->SimpleNetworkMode.BroadcastAddress, + sizeof SimpleNetworkDevice->SimpleNetworkMode.BroadcastAddress + ); + + SetMem ( + &SimpleNetworkDevice->SimpleNetworkMode.BroadcastAddress, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize, + 0xff + ); + + return Status; +} + +/** + Get NIC type + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_SUCCESS Sucess operation. + @retval Others Fail to get NIC type. +**/ +EFI_STATUS +Undi16SimpleNetworkGetNicType ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + + ZeroMem (&SimpleNetworkDevice->GetNicType, sizeof (PXENV_UNDI_GET_NIC_TYPE_T)); + + SimpleNetworkDevice->GetNicType.Status = INIT_PXE_STATUS; + + Status = PxeUndiGetNicType (SimpleNetworkDevice, &SimpleNetworkDevice->GetNicType); + + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG ((DEBUG_NET, " GetNicType.Status = %d\n", SimpleNetworkDevice->GetNicType.Status)); + DEBUG ((DEBUG_NET, " GetNicType.NicType = %d\n", SimpleNetworkDevice->GetNicType.NicType)); + // + // Check the status code from the 16 bit UNDI ROM + // + if (SimpleNetworkDevice->GetNicType.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // The information has been retrieved. Fill in Mode data. + // + return Status; +} + +/** + Get NDIS information + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_SUCCESS Sucess operation. + @retval Others Fail to get NDIS information. +**/ +EFI_STATUS +Undi16SimpleNetworkGetNdisInfo ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + + ZeroMem (&SimpleNetworkDevice->GetNdisInfo, sizeof (PXENV_UNDI_GET_NDIS_INFO_T)); + + SimpleNetworkDevice->GetNdisInfo.Status = INIT_PXE_STATUS; + + Status = PxeUndiGetNdisInfo (SimpleNetworkDevice, &SimpleNetworkDevice->GetNdisInfo); + + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG ((DEBUG_NET, " GetNdisInfo.Status = %d\n", SimpleNetworkDevice->GetNdisInfo.Status)); + DEBUG ((DEBUG_NET, " GetNdisInfo.IfaceType = %a\n", SimpleNetworkDevice->GetNdisInfo.IfaceType)); + DEBUG ((DEBUG_NET, " GetNdisInfo.LinkSpeed = %d\n", SimpleNetworkDevice->GetNdisInfo.LinkSpeed)); + DEBUG ((DEBUG_NET, " GetNdisInfo.ServiceFlags = %08x\n", SimpleNetworkDevice->GetNdisInfo.ServiceFlags)); + + // + // Check the status code from the 16 bit UNDI ROM + // + if (SimpleNetworkDevice->GetNdisInfo.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // The information has been retrieved. Fill in Mode data. + // + return Status; +} + +/** + Call Undi ROM 16bit ISR() to check interrupt cause. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param FrameLength The length of frame buffer. + @param FrameHeaderLength The length of frame buffer's header if has. + @param Frame The frame buffer to process network interrupt. + @param ProtType The type network transmit protocol + @param PktType The type of package. + + @retval EFI_DEVICE_ERROR Fail to execute 16 bit ROM's ISR, or status is invalid. + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +Undi16SimpleNetworkIsr ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * This, + IN UINTN *FrameLength, + IN UINTN *FrameHeaderLength, OPTIONAL + IN UINT8 *Frame, OPTIONAL + IN UINT8 *ProtType, OPTIONAL + IN UINT8 *PktType OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + BOOLEAN FrameReceived; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + + FrameReceived = FALSE; + + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkStarted: + default: + return EFI_DEVICE_ERROR; + } + + DEBUG ((DEBUG_NET, "Isr() IsrValid = %d\n", SimpleNetworkDevice->IsrValid)); + + if (!SimpleNetworkDevice->IsrValid) { + // + // Call 16 bit UNDI ROM to open the network interface + // + ZeroMem (&SimpleNetworkDevice->Isr, sizeof (PXENV_UNDI_ISR_T)); + SimpleNetworkDevice->Isr.Status = INIT_PXE_STATUS; + SimpleNetworkDevice->Isr.FuncFlag = PXENV_UNDI_ISR_IN_START; + + DEBUG ((DEBUG_NET, "Isr() START\n")); + + Status = PxeUndiIsr (SimpleNetworkDevice, &SimpleNetworkDevice->Isr); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (SimpleNetworkDevice->Isr.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // There have been no events on this UNDI interface, so return EFI_NOT_READY + // + if (SimpleNetworkDevice->Isr.FuncFlag == PXENV_UNDI_ISR_OUT_NOT_OURS) { + return EFI_SUCCESS; + } + // + // There is data to process, so call until all events processed. + // + ZeroMem (&SimpleNetworkDevice->Isr, sizeof (PXENV_UNDI_ISR_T)); + SimpleNetworkDevice->Isr.Status = INIT_PXE_STATUS; + SimpleNetworkDevice->Isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; + + DEBUG ((DEBUG_NET, "Isr() PROCESS\n")); + + Status = PxeUndiIsr (SimpleNetworkDevice, &SimpleNetworkDevice->Isr); + if (EFI_ERROR (Status)) { + return Status; + } + + SimpleNetworkDevice->IsrValid = TRUE; + } + // + // Call UNDI GET_NEXT until DONE + // + while (SimpleNetworkDevice->Isr.FuncFlag != PXENV_UNDI_ISR_OUT_DONE) { + // + // Check the status code from the 16 bit UNDI ROM + // + if (SimpleNetworkDevice->Isr.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // UNDI is busy. Caller will have to call again. + // This should never happen with a polled mode driver. + // + if (SimpleNetworkDevice->Isr.FuncFlag == PXENV_UNDI_ISR_OUT_BUSY) { + DEBUG ((DEBUG_NET, " BUSY\n")); + return EFI_SUCCESS; + } + // + // Check for invalud UNDI FuncFlag + // + if (SimpleNetworkDevice->Isr.FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE && + SimpleNetworkDevice->Isr.FuncFlag != PXENV_UNDI_ISR_OUT_TRANSMIT + ) { + DEBUG ((DEBUG_NET, " Invalid SimpleNetworkDevice->Isr.FuncFlag value %d\n", SimpleNetworkDevice->Isr.FuncFlag)); + return EFI_DEVICE_ERROR; + } + // + // Check for Transmit Event + // + if (SimpleNetworkDevice->Isr.FuncFlag == PXENV_UNDI_ISR_OUT_TRANSMIT) { + DEBUG ((DEBUG_NET, " TRANSMIT\n")); + SimpleNetworkDevice->InterruptStatus |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; + } + // + // Check for Receive Event + // + else if (SimpleNetworkDevice->Isr.FuncFlag == PXENV_UNDI_ISR_OUT_RECEIVE) { + // + // note - this code will hang on a receive interrupt in a GetStatus loop + // + DEBUG ((DEBUG_NET, " RECEIVE\n")); + SimpleNetworkDevice->InterruptStatus |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + + DEBUG ((DEBUG_NET, "SimpleNetworkDevice->Isr.BufferLength = %d\n", SimpleNetworkDevice->Isr.BufferLength)); + DEBUG ((DEBUG_NET, "SimpleNetworkDevice->Isr.FrameLength = %d\n", SimpleNetworkDevice->Isr.FrameLength)); + DEBUG ((DEBUG_NET, "SimpleNetworkDevice->Isr.FrameHeaderLength = %d\n", SimpleNetworkDevice->Isr.FrameHeaderLength)); + DEBUG ( + ( + DEBUG_NET, "SimpleNetworkDevice->Isr.Frame = %04x:%04x\n", SimpleNetworkDevice->Isr.FrameSegSel, + SimpleNetworkDevice->Isr.FrameOffset + ) + ); + DEBUG ((DEBUG_NET, "SimpleNetworkDevice->Isr.ProtType = 0x%02x\n", SimpleNetworkDevice->Isr.BufferLength)); + DEBUG ((DEBUG_NET, "SimpleNetworkDevice->Isr.PktType = 0x%02x\n", SimpleNetworkDevice->Isr.BufferLength)); + + if (FrameReceived) { + return EFI_SUCCESS; + } + + if ((Frame == NULL) || (SimpleNetworkDevice->Isr.FrameLength > *FrameLength)) { + DEBUG ((DEBUG_NET, "return EFI_BUFFER_TOO_SMALL *FrameLength = %08x\n", *FrameLength)); + *FrameLength = SimpleNetworkDevice->Isr.FrameLength; + return EFI_BUFFER_TOO_SMALL; + } + + *FrameLength = SimpleNetworkDevice->Isr.FrameLength; + if (FrameHeaderLength != NULL) { + *FrameHeaderLength = SimpleNetworkDevice->Isr.FrameHeaderLength; + } + + if (ProtType != NULL) { + *ProtType = SimpleNetworkDevice->Isr.ProtType; + } + + if (PktType != NULL) { + *PktType = SimpleNetworkDevice->Isr.PktType; + } + + CopyMem ( + Frame, + (VOID *)(UINTN) ((SimpleNetworkDevice->Isr.FrameSegSel << 4) + SimpleNetworkDevice->Isr.FrameOffset), + SimpleNetworkDevice->Isr.BufferLength + ); + Frame = Frame + SimpleNetworkDevice->Isr.BufferLength; + if (SimpleNetworkDevice->Isr.BufferLength == SimpleNetworkDevice->Isr.FrameLength) { + FrameReceived = TRUE; + } + } + // + // There is data to process, so call until all events processed. + // + ZeroMem (&SimpleNetworkDevice->Isr, sizeof (PXENV_UNDI_ISR_T)); + SimpleNetworkDevice->Isr.Status = INIT_PXE_STATUS; + SimpleNetworkDevice->Isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + + DEBUG ((DEBUG_NET, "Isr() GET NEXT\n")); + + Status = PxeUndiIsr (SimpleNetworkDevice, &SimpleNetworkDevice->Isr); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + // if (SimpleNetworkDevice->Isr.Status != PXENV_STATUS_SUCCESS) { + // return EFI_DEVICE_ERROR; + // } + // + } + + SimpleNetworkDevice->IsrValid = FALSE; + return EFI_SUCCESS; +} +// +// /////////////////////////////////////////////////////////////////////////////////////// +// Simple Network Protocol Interface Functions using 16 bit UNDI Option ROMs +///////////////////////////////////////////////////////////////////////////////////////// +// +// Start() +// +/** + Call 16 bit UNDI ROM to start the network interface + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_DEVICE_ERROR Network interface has not be initialized. + @retval EFI_DEVICE_ERROR Fail to execute 16 bit ROM call. + @retval EFI_SUCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkStart ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + PXENV_UNDI_STARTUP_T Startup; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkStopped: + break; + + case EfiSimpleNetworkStarted: + case EfiSimpleNetworkInitialized: + return EFI_ALREADY_STARTED; + + default: + return EFI_DEVICE_ERROR; + } + // + // Call 16 bit UNDI ROM to start the network interface + // + Startup.Status = INIT_PXE_STATUS; + + Status = PxeUndiStartup (SimpleNetworkDevice, &Startup); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (Startup.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // The UNDI interface has been started, so update the State. + // + SimpleNetworkDevice->SimpleNetworkMode.State = EfiSimpleNetworkStarted; + + // + // + // + SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting = 0; + SimpleNetworkDevice->SimpleNetworkMode.MCastFilterCount = 0; + + return Status; +} +// +// Stop() +// +/** + Call 16 bit UNDI ROM to stop the network interface + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_DEVICE_ERROR Network interface has not be initialized. + @retval EFI_DEVICE_ERROR Fail to execute 16 bit ROM call. + @retval EFI_SUCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkStop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkStarted: + break; + + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkInitialized: + default: + return EFI_DEVICE_ERROR; + } + + SimpleNetworkDevice->SimpleNetworkMode.State = EfiSimpleNetworkStopped; + + return Status; +} + +// +// Initialize() +// +/** + Initialize network interface + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param ExtraRxBufferSize The size of extra request receive buffer. + @param ExtraTxBufferSize The size of extra request transmit buffer. + + @retval EFI_DEVICE_ERROR Fail to execute 16 bit ROM call. + @retval EFI_SUCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkInitialize ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINTN ExtraRxBufferSize OPTIONAL, + IN UINTN ExtraTxBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + PXENV_UNDI_INITIALIZE_T Initialize; + PXENV_UNDI_OPEN_T Open; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkStarted: + break; + + case EfiSimpleNetworkInitialized: + default: + return EFI_DEVICE_ERROR; + } + // + // Call 16 bit UNDI ROM to start the network interface + // + Initialize.Status = INIT_PXE_STATUS; + Initialize.ProtocolIni = 0; + + Status = PxeUndiInitialize (SimpleNetworkDevice, &Initialize); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ERROR : PxeUndiInitialize() - Status = %r\n", Status)); + DEBUG ((DEBUG_ERROR, "Initialize.Status == %xh\n", Initialize.Status)); + + if (Initialize.Status == PXENV_STATUS_UNDI_MEDIATEST_FAILED) { + Status = EFI_NO_MEDIA; + } + + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (Initialize.Status != PXENV_STATUS_SUCCESS) { + DEBUG ((DEBUG_ERROR, "ERROR : PxeUndiInitialize() - Initialize.Status = %04x\n", Initialize.Status)); + return EFI_DEVICE_ERROR; + } + // + // Call 16 bit UNDI ROM to open the network interface + // + Open.Status = INIT_PXE_STATUS; + Open.OpenFlag = 0; + Open.PktFilter = Undi16GetPacketFilterSetting (SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting); + Undi16GetMCastFilters ( + &SimpleNetworkDevice->SimpleNetworkMode, + &Open.McastBuffer, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + + Status = PxeUndiOpen (SimpleNetworkDevice, &Open); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ERROR : PxeUndiOpen() - Status = %r\n", Status)); + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (Open.Status != PXENV_STATUS_SUCCESS) { + DEBUG ((DEBUG_ERROR, "ERROR : PxeUndiOpen() - Open.Status = %04x\n", Open.Status)); + return EFI_DEVICE_ERROR; + } + // + // The UNDI interface has been initialized, so update the State. + // + SimpleNetworkDevice->SimpleNetworkMode.State = EfiSimpleNetworkInitialized; + + // + // If initialize succeeds, then assume that media is present. + // + SimpleNetworkDevice->SimpleNetworkMode.MediaPresent = TRUE; + + // + // Reset the recycled transmit buffer FIFO + // + SimpleNetworkDevice->TxBufferFifo.First = 0; + SimpleNetworkDevice->TxBufferFifo.Last = 0; + SimpleNetworkDevice->IsrValid = FALSE; + + return Status; +} +// +// Reset() +// +/** + Reset network interface. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param ExtendedVerification Need extended verfication. + + @retval EFI_INVALID_PARAMETER Invalid This paramter. + @retval EFI_DEVICE_ERROR Network device has not been initialized. + @retval EFI_NOT_STARTED Network device has been stopped. + @retval EFI_DEVICE_ERROR Invalid status for network device + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkReset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + PXENV_UNDI_RESET_T Reset; + UINT16 Rx_filter; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStarted: + default: + return EFI_DEVICE_ERROR; + } + + Reset.Status = INIT_PXE_STATUS; + + Rx_filter = Undi16GetPacketFilterSetting (SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting); + + Undi16GetMCastFilters ( + &SimpleNetworkDevice->SimpleNetworkMode, + &Reset.R_Mcast_Buf, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + + Status = PxeUndiResetNic (SimpleNetworkDevice, &Reset, Rx_filter); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (Reset.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // Reset the recycled transmit buffer FIFO + // + SimpleNetworkDevice->TxBufferFifo.First = 0; + SimpleNetworkDevice->TxBufferFifo.Last = 0; + SimpleNetworkDevice->IsrValid = FALSE; + + return Status; +} +// +// Shutdown() +// +/** + Shutdown network interface. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_INVALID_PARAMETER Invalid This paramter. + @retval EFI_DEVICE_ERROR Network device has not been initialized. + @retval EFI_NOT_STARTED Network device has been stopped. + @retval EFI_DEVICE_ERROR Invalid status for network device + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkShutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + PXENV_UNDI_CLOSE_T Close; + PXENV_UNDI_SHUTDOWN_T Shutdown; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStarted: + default: + return EFI_DEVICE_ERROR; + } + + SimpleNetworkDevice->IsrValid = FALSE; + + // + // Call 16 bit UNDI ROM to start the network interface + // + Close.Status = INIT_PXE_STATUS; + + Status = PxeUndiClose (SimpleNetworkDevice, &Close); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (Close.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // Call 16 bit UNDI ROM to open the network interface + // + Shutdown.Status = INIT_PXE_STATUS; + + Status = PxeUndiShutdown (SimpleNetworkDevice, &Shutdown); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (Shutdown.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // The UNDI interface has been initialized, so update the State. + // + SimpleNetworkDevice->SimpleNetworkMode.State = EfiSimpleNetworkStarted; + + // + // If shutdown succeeds, then assume that media is not present. + // + SimpleNetworkDevice->SimpleNetworkMode.MediaPresent = FALSE; + + // + // Reset the recycled transmit buffer FIFO + // + SimpleNetworkDevice->TxBufferFifo.First = 0; + SimpleNetworkDevice->TxBufferFifo.Last = 0; + + // + // A short delay. Without this an initialize immediately following + // a shutdown will cause some versions of UNDI-16 to stop operating. + // + gBS->Stall (250000); + + return Status; +} +// +// ReceiveFilters() +// +/** + Reset network interface. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param Enable Enable mask value + @param Disable Disable mask value + @param ResetMCastFilter Whether reset multi cast filter or not + @param MCastFilterCnt Count of mutli cast filter for different MAC address + @param MCastFilter Buffer for mustli cast filter for different MAC address. + + @retval EFI_INVALID_PARAMETER Invalid This paramter. + @retval EFI_DEVICE_ERROR Network device has not been initialized. + @retval EFI_NOT_STARTED Network device has been stopped. + @retval EFI_DEVICE_ERROR Invalid status for network device + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkReceiveFilters ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * This, + IN UINT32 Enable, + IN UINT32 Disable, + IN BOOLEAN ResetMCastFilter, + IN UINTN MCastFilterCnt OPTIONAL, + IN EFI_MAC_ADDRESS * MCastFilter OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN Index; + UINT32 NewFilter; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + PXENV_UNDI_CLOSE_T Close; + PXENV_UNDI_OPEN_T Open; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStarted: + default: + return EFI_DEVICE_ERROR; + } + // + // First deal with possible filter setting changes + // + if ((Enable == 0) && (Disable == 0) && !ResetMCastFilter) { + return EFI_SUCCESS; + } + + NewFilter = (SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting | Enable) &~Disable; + + if ((NewFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) { + if ((MCastFilterCnt == 0) || (MCastFilter == 0) || MCastFilterCnt > SimpleNetworkDevice->SimpleNetworkMode.MaxMCastFilterCount) { + return EFI_INVALID_PARAMETER; + } + } + // + // Call 16 bit UNDI ROM to close the network interface + // + Close.Status = INIT_PXE_STATUS; + + Status = PxeUndiClose (SimpleNetworkDevice, &Close); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (Close.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // Call 16 bit UNDI ROM to open the network interface + // + // + // Reset the recycled transmit buffer FIFO + // + SimpleNetworkDevice->TxBufferFifo.First = 0; + SimpleNetworkDevice->TxBufferFifo.Last = 0; + + // + // Call 16 bit UNDI ROM to open the network interface + // + ZeroMem (&Open, sizeof Open); + + Open.Status = INIT_PXE_STATUS; + Open.PktFilter = Undi16GetPacketFilterSetting (NewFilter); + + if ((NewFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) { + // + // Copy the MAC addresses into the UNDI open parameter structure + // + Open.McastBuffer.MCastAddrCount = (UINT16) MCastFilterCnt; + for (Index = 0; Index < MCastFilterCnt; ++Index) { + CopyMem ( + Open.McastBuffer.MCastAddr[Index], + &MCastFilter[Index], + sizeof Open.McastBuffer.MCastAddr[Index] + ); + } + } else if (!ResetMCastFilter) { + for (Index = 0; Index < SimpleNetworkDevice->SimpleNetworkMode.MCastFilterCount; ++Index) { + CopyMem ( + Open.McastBuffer.MCastAddr[Index], + &SimpleNetworkDevice->SimpleNetworkMode.MCastFilter[Index], + sizeof Open.McastBuffer.MCastAddr[Index] + ); + } + } + + Status = PxeUndiOpen (SimpleNetworkDevice, &Open); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (Open.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + + SimpleNetworkDevice->IsrValid = FALSE; + SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting = NewFilter; + + if ((NewFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) { + SimpleNetworkDevice->SimpleNetworkMode.MCastFilterCount = (UINT32) MCastFilterCnt; + for (Index = 0; Index < MCastFilterCnt; ++Index) { + CopyMem ( + &SimpleNetworkDevice->SimpleNetworkMode.MCastFilter[Index], + &MCastFilter[Index], + sizeof (EFI_MAC_ADDRESS) + ); + } + } + // + // Read back multicast addresses. + // + return EFI_SUCCESS; +} +// +// StationAddress() +// +/** + Set new MAC address. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param Reset Whether reset station MAC address to permenent address + @param New A pointer to New address + + @retval EFI_INVALID_PARAMETER Invalid This paramter. + @retval EFI_DEVICE_ERROR Network device has not been initialized. + @retval EFI_NOT_STARTED Network device has been stopped. + @retval EFI_DEVICE_ERROR Invalid status for network device + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkStationAddress ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * This, + IN BOOLEAN Reset, + IN EFI_MAC_ADDRESS * New OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + PXENV_UNDI_SET_STATION_ADDR_T SetStationAddr; + // + // EFI_DEVICE_PATH_PROTOCOL *OldDevicePath; + // + PXENV_UNDI_CLOSE_T Close; + PXENV_UNDI_OPEN_T Open; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkStarted: + default: + return EFI_DEVICE_ERROR; + } + // + // Call 16 bit UNDI ROM to open the network interface + // + SetStationAddr.Status = INIT_PXE_STATUS; + + if (Reset) { + // + // If we are reseting the Station Address to the permanent address, and the + // Station Address is not programmable, then just return EFI_SUCCESS. + // + if (!SimpleNetworkDevice->SimpleNetworkMode.MacAddressChangeable) { + return EFI_SUCCESS; + } + // + // If the address is already the permanent address, then just return success. + // + if (CompareMem ( + &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress, + &SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ) == 0) { + return EFI_SUCCESS; + } + // + // Copy the adapters permanent address to the new station address + // + CopyMem ( + &SetStationAddr.StationAddress, + &SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + } else { + // + // If we are setting the Station Address, and the + // Station Address is not programmable, return invalid parameter. + // + if (!SimpleNetworkDevice->SimpleNetworkMode.MacAddressChangeable) { + return EFI_INVALID_PARAMETER; + } + // + // If the address is already the new address, then just return success. + // + if (CompareMem ( + &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress, + New, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ) == 0) { + return EFI_SUCCESS; + } + // + // Copy New to the new station address + // + CopyMem ( + &SetStationAddr.StationAddress, + New, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + + } + // + // Call 16 bit UNDI ROM to stop the network interface + // + Close.Status = INIT_PXE_STATUS; + + PxeUndiClose (SimpleNetworkDevice, &Close); + + // + // Call 16-bit UNDI ROM to set the station address + // + SetStationAddr.Status = PXENV_STATUS_SUCCESS; + + Status = PxeUndiSetStationAddr (SimpleNetworkDevice, &SetStationAddr); + + // + // Call 16-bit UNDI ROM to start the network interface + // + Open.Status = PXENV_STATUS_SUCCESS; + Open.OpenFlag = 0; + Open.PktFilter = Undi16GetPacketFilterSetting (SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting); + Undi16GetMCastFilters ( + &SimpleNetworkDevice->SimpleNetworkMode, + &Open.McastBuffer, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + + PxeUndiOpen (SimpleNetworkDevice, &Open); + + // + // Check status from station address change + // + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (SetStationAddr.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + + CopyMem ( + &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress, + &SetStationAddr.StationAddress, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + +#if 0 /* The device path is based on the permanent address not the current address. */ + // + // The station address was changed, so update the device path with the new MAC address. + // + OldDevicePath = SimpleNetworkDevice->DevicePath; + SimpleNetworkDevice->DevicePath = DuplicateDevicePath (SimpleNetworkDevice->BaseDevicePath); + SimpleNetworkAppendMacAddressDevicePath ( + &SimpleNetworkDevice->DevicePath, + &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress + ); + + Status = LibReinstallProtocolInterfaces ( + SimpleNetworkDevice->Handle, + &DevicePathProtocol, + OldDevicePath, + SimpleNetworkDevice->DevicePath, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to reinstall the DevicePath protocol for the Simple Network Device\n")); + DEBUG ((DEBUG_ERROR, " Status = %r\n", Status)); + } + + FreePool (OldDevicePath); +#endif /* 0 */ + + return Status; +} +// +// Statistics() +// +/** + Resets or collects the statistics on a network interface. + + @param This Protocol instance pointer. + @param Reset Set to TRUE to reset the statistics for the network interface. + @param StatisticsSize On input the size, in bytes, of StatisticsTable. On + output the size, in bytes, of the resulting table of + statistics. + @param StatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that + contains the statistics. + + @retval EFI_SUCCESS The statistics were collected from the network interface. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_BUFFER_TOO_SMALL The Statistics buffer was too small. The current buffer + size needed to hold the statistics is returned in + StatisticsSize. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkStatistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * This, + IN BOOLEAN Reset, + IN OUT UINTN *StatisticsSize OPTIONAL, + OUT EFI_NETWORK_STATISTICS * StatisticsTable OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + PXENV_UNDI_CLEAR_STATISTICS_T ClearStatistics; + PXENV_UNDI_GET_STATISTICS_T GetStatistics; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkStarted: + default: + return EFI_DEVICE_ERROR; + } + + if ((StatisticsSize != NULL) && (*StatisticsSize != 0) && (StatisticsTable == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // If Reset is TRUE, then clear all the statistics. + // + if (Reset) { + + DEBUG ((DEBUG_NET, " RESET Statistics\n")); + + // + // Call 16 bit UNDI ROM to open the network interface + // + ClearStatistics.Status = INIT_PXE_STATUS; + + Status = PxeUndiClearStatistics (SimpleNetworkDevice, &ClearStatistics); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (ClearStatistics.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + + DEBUG ((DEBUG_NET, " RESET Statistics Complete")); + } + + if (StatisticsSize != NULL) { + EFI_NETWORK_STATISTICS LocalStatisticsTable; + + DEBUG ((DEBUG_NET, " GET Statistics\n")); + + // + // If the size if valid, then see if the table is valid + // + if (StatisticsTable == NULL) { + DEBUG ((DEBUG_NET, " StatisticsTable is NULL\n")); + return EFI_INVALID_PARAMETER; + } + // + // Call 16 bit UNDI ROM to open the network interface + // + GetStatistics.Status = INIT_PXE_STATUS; + GetStatistics.XmtGoodFrames = 0; + GetStatistics.RcvGoodFrames = 0; + GetStatistics.RcvCRCErrors = 0; + GetStatistics.RcvResourceErrors = 0; + + Status = PxeUndiGetStatistics (SimpleNetworkDevice, &GetStatistics); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (GetStatistics.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // Fill in the Statistics Table with the collected values. + // + SetMem (&LocalStatisticsTable, sizeof LocalStatisticsTable, 0xff); + + LocalStatisticsTable.TxGoodFrames = GetStatistics.XmtGoodFrames; + LocalStatisticsTable.RxGoodFrames = GetStatistics.RcvGoodFrames; + LocalStatisticsTable.RxCrcErrorFrames = GetStatistics.RcvCRCErrors; + LocalStatisticsTable.RxDroppedFrames = GetStatistics.RcvResourceErrors; + + CopyMem (StatisticsTable, &LocalStatisticsTable, *StatisticsSize); + + DEBUG ( + (DEBUG_NET, + " Statistics Collected : Size=%d Buf=%08x\n", + *StatisticsSize, + StatisticsTable) + ); + + DEBUG ((DEBUG_NET, " GET Statistics Complete")); + + if (*StatisticsSize < sizeof LocalStatisticsTable) { + DEBUG ((DEBUG_NET, " BUFFER TOO SMALL\n")); + Status = EFI_BUFFER_TOO_SMALL; + } + + *StatisticsSize = sizeof LocalStatisticsTable; + + return Status; + + } + + return EFI_SUCCESS; +} +// +// MCastIpToMac() +// +/** + Translate IP address to MAC address. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param IPv6 IPv6 or IPv4 + @param IP A pointer to given Ip address. + @param MAC On return, translated MAC address. + + @retval EFI_INVALID_PARAMETER Invalid This paramter. + @retval EFI_INVALID_PARAMETER Invalid IP address. + @retval EFI_INVALID_PARAMETER Invalid return buffer for holding MAC address. + @retval EFI_UNSUPPORTED Do not support IPv6 + @retval EFI_DEVICE_ERROR Network device has not been initialized. + @retval EFI_NOT_STARTED Network device has been stopped. + @retval EFI_DEVICE_ERROR Invalid status for network device + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkMCastIpToMac ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN IPv6, + IN EFI_IP_ADDRESS *IP, + OUT EFI_MAC_ADDRESS *MAC + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + PXENV_UNDI_GET_MCAST_ADDR_T GetMcastAddr; + + if (This == NULL || IP == NULL || MAC == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStarted: + default: + return EFI_DEVICE_ERROR; + } + // + // 16 bit UNDI Option ROMS do not support IPv6. Check for IPv6 usage. + // + if (IPv6) { + return EFI_UNSUPPORTED; + } + // + // Call 16 bit UNDI ROM to open the network interface + // + GetMcastAddr.Status = INIT_PXE_STATUS; + CopyMem (&GetMcastAddr.InetAddr, IP, 4); + + Status = PxeUndiGetMcastAddr (SimpleNetworkDevice, &GetMcastAddr); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + if (GetMcastAddr.Status != PXENV_STATUS_SUCCESS) { + return EFI_DEVICE_ERROR; + } + // + // Copy the MAC address from the returned data structure. + // + CopyMem ( + MAC, + &GetMcastAddr.MediaAddr, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + + return Status; +} +// +// NvData() +// +/** + Performs read and write operations on the NVRAM device attached to a + network interface. + + @param This The protocol instance pointer. + @param ReadWrite TRUE for read operations, FALSE for write operations. + @param Offset Byte offset in the NVRAM device at which to start the read or + write operation. This must be a multiple of NvRamAccessSize and + less than NvRamSize. + @param BufferSize The number of bytes to read or write from the NVRAM device. + This must also be a multiple of NvramAccessSize. + @param Buffer A pointer to the data buffer. + + @retval EFI_SUCCESS The NVRAM access was performed. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkNvData ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN ReadWrite, + IN UINTN Offset, + IN UINTN BufferSize, + IN OUT VOID *Buffer + ) +{ + return EFI_UNSUPPORTED; +} +// +// GetStatus() +// +/** + Reads the current interrupt status and recycled transmit buffer status from + a network interface. + + @param This The protocol instance pointer. + @param InterruptStatus A pointer to the bit mask of the currently active interrupts + If this is NULL, the interrupt status will not be read from + the device. If this is not NULL, the interrupt status will + be read from the device. When the interrupt status is read, + it will also be cleared. Clearing the transmit interrupt + does not empty the recycled transmit buffer array. + @param TxBuf Recycled transmit buffer address. The network interface will + not transmit if its internal recycled transmit buffer array + is full. Reading the transmit buffer does not clear the + transmit interrupt. If this is NULL, then the transmit buffer + status will not be read. If there are no transmit buffers to + recycle and TxBuf is not NULL, * TxBuf will be set to NULL. + + @retval EFI_SUCCESS The status of the network interface was retrieved. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkGetStatus ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * This, + OUT UINT32 *InterruptStatus OPTIONAL, + OUT VOID **TxBuf OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + UINTN FrameLength; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkStarted: + default: + return EFI_DEVICE_ERROR; + } + + if (InterruptStatus == NULL && TxBuf == NULL) { + return EFI_INVALID_PARAMETER; + } + + FrameLength = 0; + Status = Undi16SimpleNetworkIsr (This, &FrameLength, NULL, NULL, NULL, NULL); + + if (Status != EFI_BUFFER_TOO_SMALL) { + if (EFI_ERROR (Status)) { + return Status; + } + } + // + // See if the caller wants interrupt info. + // + if (InterruptStatus != NULL) { + *InterruptStatus = SimpleNetworkDevice->InterruptStatus; + SimpleNetworkDevice->InterruptStatus = 0; + } + // + // See if the caller wants transmit buffer status info. + // + if (TxBuf != NULL) { + *TxBuf = 0; + SimpleNetworkTransmitFifoRemove (&(SimpleNetworkDevice->TxBufferFifo), TxBuf); + } + + return EFI_SUCCESS; +} + +/** + Places a packet in the transmit queue of a network interface. + + @param This The protocol instance pointer. + @param HeaderSize The size, in bytes, of the media header to be filled in by + the Transmit() function. If HeaderSize is non-zero, then it + must be equal to This->Mode->MediaHeaderSize and the DestAddr + and Protocol parameters must not be NULL. + @param BufferSize The size, in bytes, of the entire packet (media header and + data) to be transmitted through the network interface. + @param Buffer A pointer to the packet (media header followed by data) to be + transmitted. This parameter cannot be NULL. If HeaderSize is zero, + then the media header in Buffer must already be filled in by the + caller. If HeaderSize is non-zero, then the media header will be + filled in by the Transmit() function. + @param SrcAddr The source HW MAC address. If HeaderSize is zero, then this parameter + is ignored. If HeaderSize is non-zero and SrcAddr is NULL, then + This->Mode->CurrentAddress is used for the source HW MAC address. + @param DestAddr The destination HW MAC address. If HeaderSize is zero, then this + parameter is ignored. + @param Protocol The type of header to build. If HeaderSize is zero, then this + parameter is ignored. See RFC 1700, section "Ether Types", for + examples. + + @retval EFI_SUCCESS The packet was placed on the transmit queue. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkTransmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINTN HeaderSize, + IN UINTN BufferSize, + IN VOID *Buffer, + IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + IN EFI_MAC_ADDRESS *DestAddr OPTIONAL, + IN UINT16 *Protocol OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + PXENV_UNDI_TRANSMIT_T XmitInfo; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkStarted: + default: + return EFI_DEVICE_ERROR; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize < SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize) { + return EFI_BUFFER_TOO_SMALL; + } + + if (HeaderSize != 0) { + if (HeaderSize != SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize) { + return EFI_INVALID_PARAMETER; + } + + if (DestAddr == NULL || Protocol == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DestAddr != NULL) { + CopyMem ( + Buffer, + DestAddr, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + } + + if (SrcAddr == NULL) { + SrcAddr = &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress; + } + + CopyMem ( + (UINT8 *) Buffer + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize, + SrcAddr, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + + if (Protocol != NULL) { + *(UINT16 *) ((UINT8 *) Buffer + 2 * SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize) = (UINT16) (((*Protocol & 0xFF) << 8) | ((*Protocol >> 8) & 0xFF)); + } + } + // + // See if the recycled transmit buffer FIFO is full. + // If it is full, then we can not transmit until the caller calls GetStatus() to pull + // off recycled transmit buffers. + // + if (SimpleNetworkTransmitFifoFull (&(SimpleNetworkDevice->TxBufferFifo))) { + return EFI_NOT_READY; + } + // + // Output debug trace message. + // + DEBUG ((DEBUG_NET, "Undi16SimpleNetworkTransmit\n\r ")); + + // + // Initialize UNDI WRITE parameter structure. + // + XmitInfo.Status = INIT_PXE_STATUS; + XmitInfo.Protocol = P_UNKNOWN; + XmitInfo.XmitFlag = XMT_DESTADDR; + XmitInfo.DestAddrOffset = (UINT16) ((UINT32)(UINTN) SimpleNetworkDevice->TxDestAddr & 0x000f); + XmitInfo.DestAddrSegment = (UINT16) ((UINT32)(UINTN) SimpleNetworkDevice->TxDestAddr >> 4); + XmitInfo.TBDOffset = (UINT16) ((UINT32)(UINTN) SimpleNetworkDevice->Xmit & 0x000f); + XmitInfo.TBDSegment = (UINT16) ((UINT32)(UINTN) SimpleNetworkDevice->Xmit >> 4); + XmitInfo.Reserved[0] = 0; + XmitInfo.Reserved[1] = 0; + + CopyMem ( + SimpleNetworkDevice->TxDestAddr, + Buffer, + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize + ); + + CopyMem ( + SimpleNetworkDevice->TxRealModeMediaHeader, + Buffer, + SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize + ); + + SimpleNetworkDevice->Xmit->ImmedLength = (UINT16) SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize; + + SimpleNetworkDevice->Xmit->DataBlock[0].TDDataLen = (UINT16) (BufferSize - SimpleNetworkDevice->Xmit->ImmedLength); + + CopyMem ( + SimpleNetworkDevice->TxRealModeDataBuffer, + (UINT8 *) Buffer + SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize, + SimpleNetworkDevice->Xmit->DataBlock[0].TDDataLen + ); + + // + // Make API call to UNDI TRANSMIT + // + XmitInfo.Status = 0; + + Status = PxeUndiTransmit (SimpleNetworkDevice, &XmitInfo); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the status code from the 16 bit UNDI ROM + // + switch (XmitInfo.Status) { + case PXENV_STATUS_OUT_OF_RESOURCES: + return EFI_NOT_READY; + + case PXENV_STATUS_SUCCESS: + break; + + default: + return EFI_DEVICE_ERROR; + } + // + // Add address of Buffer to the recycled transmit buffer FIFO + // + SimpleNetworkTransmitFifoAdd (&(SimpleNetworkDevice->TxBufferFifo), Buffer); + + return EFI_SUCCESS; +} + +/** + Receives a packet from a network interface. + + @param This The protocol instance pointer. + @param HeaderSize The size, in bytes, of the media header received on the network + interface. If this parameter is NULL, then the media header size + will not be returned. + @param BufferSize On entry, the size, in bytes, of Buffer. On exit, the size, in + bytes, of the packet that was received on the network interface. + @param Buffer A pointer to the data buffer to receive both the media header and + the data. + @param SrcAddr The source HW MAC address. If this parameter is NULL, the + HW MAC source address will not be extracted from the media + header. + @param DestAddr The destination HW MAC address. If this parameter is NULL, + the HW MAC destination address will not be extracted from the + media header. + @param Protocol The media header type. If this parameter is NULL, then the + protocol will not be extracted from the media header. See + RFC 1700 section "Ether Types" for examples. + + @retval EFI_SUCCESS The received data was stored in Buffer, and BufferSize has + been updated to the number of bytes received. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit + request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkReceive ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + OUT UINTN *HeaderSize OPTIONAL, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer, + OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + OUT EFI_MAC_ADDRESS *DestAddr OPTIONAL, + OUT UINT16 *Protocol OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + UINTN MediaAddrSize; + UINT8 ProtType; + + if (This == NULL || BufferSize == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkStarted: + default: + return EFI_DEVICE_ERROR; + } + + Status = Undi16SimpleNetworkIsr ( + This, + BufferSize, + HeaderSize, + Buffer, + &ProtType, + NULL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if ((SimpleNetworkDevice->InterruptStatus & EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT) == 0) { + return EFI_NOT_READY; + + } + + SimpleNetworkDevice->InterruptStatus &= ~EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + + MediaAddrSize = This->Mode->HwAddressSize; + + if (SrcAddr != NULL) { + CopyMem (SrcAddr, (UINT8 *) Buffer + MediaAddrSize, MediaAddrSize); + } + + if (DestAddr != NULL) { + CopyMem (DestAddr, Buffer, MediaAddrSize); + } + + if (Protocol != NULL) { + *((UINT8 *) Protocol) = *((UINT8 *) Buffer + (2 * MediaAddrSize) + 1); + *((UINT8 *) Protocol + 1) = *((UINT8 *) Buffer + (2 * MediaAddrSize)); + } + + DEBUG ((DEBUG_NET, "Packet Received: BufferSize=%d HeaderSize = %d\n", *BufferSize, *HeaderSize)); + + return Status; + +} +// +// WaitForPacket() +// +/** + wait for a packet to be received. + + @param Event Event used with WaitForEvent() to wait for a packet to be received. + @param Context Event Context + +**/ +VOID +EFIAPI +Undi16SimpleNetworkWaitForPacket ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Someone is waiting on the receive packet event, if there's + // a packet pending, signal the event + // + if (!EFI_ERROR (Undi16SimpleNetworkCheckForPacket (Context))) { + gBS->SignalEvent (Event); + } +} +// +// CheckForPacket() +// +/** + Check whether packet is ready for receive. + + @param This The protocol instance pointer. + + @retval EFI_SUCCESS Receive data is ready. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit + request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. +**/ +EFI_STATUS +Undi16SimpleNetworkCheckForPacket ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice; + UINTN FrameLength; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + if (SimpleNetworkDevice == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Verify that the current state of the adapter is valid for this call. + // + switch (SimpleNetworkDevice->SimpleNetworkMode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + return EFI_NOT_STARTED; + + case EfiSimpleNetworkStarted: + default: + return EFI_DEVICE_ERROR; + } + + FrameLength = 0; + Status = Undi16SimpleNetworkIsr ( + This, + &FrameLength, + NULL, + NULL, + NULL, + NULL + ); + + if (Status != EFI_BUFFER_TOO_SMALL) { + if (EFI_ERROR (Status)) { + return Status; + } + } + + return ((SimpleNetworkDevice->InterruptStatus & EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT) != 0) ? EFI_SUCCESS : EFI_NOT_READY; +} + +/** + Signal handlers for ExitBootServices event. + + Clean up any Real-mode UNDI residue from the system + + @param Event ExitBootServices event + @param Context +**/ +VOID +EFIAPI +Undi16SimpleNetworkEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // NOTE: This is not the only way to effect this cleanup. The prescribed mechanism + // would be to perform an UNDI STOP command. This strategam has been attempted + // but results in problems making some of the EFI core services from TPL_CALLBACK. + // This issue needs to be resolved, but the other alternative has been to perform + // the unchain logic explicitly, as done below. + // + RestoreCachedVectorAddress (0x1A); +} + +/** + Allocate buffer below 1M for real mode. + + @param NumPages The number pages want to be allocated. + @param Buffer On return, allocated buffer. + + @return Status of allocating pages. +**/ +EFI_STATUS +BiosSnp16AllocatePagesBelowOneMb ( + UINTN NumPages, + VOID **Buffer + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + + PhysicalAddress = 0x000fffff; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiRuntimeServicesData, + NumPages, + &PhysicalAddress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *Buffer = (VOID *) (UINTN) PhysicalAddress; + return EFI_SUCCESS; +} diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/BiosSnp16.h b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/BiosSnp16.h new file mode 100644 index 0000000000..b29059f685 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/BiosSnp16.h @@ -0,0 +1,1655 @@ +/** @file + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _BIOS_SNP_16_H_ +#define _BIOS_SNP_16_H_ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "Pxe.h" + +// +// BIOS Simple Network Protocol Device Structure +// +#define EFI_SIMPLE_NETWORK_DEV_SIGNATURE SIGNATURE_32 ('s', 'n', '1', '6') + +#define INIT_PXE_STATUS 0xabcd + +#define EFI_SIMPLE_NETWORK_MAX_TX_FIFO_SIZE 64 + +typedef struct { + UINT32 First; + UINT32 Last; + VOID * Data[EFI_SIMPLE_NETWORK_MAX_TX_FIFO_SIZE]; +} EFI_SIMPLE_NETWORK_DEV_FIFO; + +typedef struct { + UINTN Signature; + EFI_HANDLE Handle; + EFI_SIMPLE_NETWORK_PROTOCOL SimpleNetwork; + EFI_SIMPLE_NETWORK_MODE SimpleNetworkMode; + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL Nii; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + + // + // Local Data for Simple Network Protocol interface goes here + // + BOOLEAN UndiLoaded; + EFI_EVENT EfiBootEvent; + EFI_EVENT LegacyBootEvent; + UINT16 PxeEntrySegment; + UINT16 PxeEntryOffset; + EFI_SIMPLE_NETWORK_DEV_FIFO TxBufferFifo; + EFI_DEVICE_PATH_PROTOCOL *BaseDevicePath; + PXE_T *Pxe; ///< Pointer to !PXE structure + PXENV_UNDI_GET_INFORMATION_T GetInformation; ///< Data from GET INFORMATION + PXENV_UNDI_GET_NIC_TYPE_T GetNicType; ///< Data from GET NIC TYPE + PXENV_UNDI_GET_NDIS_INFO_T GetNdisInfo; ///< Data from GET NDIS INFO + BOOLEAN IsrValid; ///< TRUE if Isr contains valid data + PXENV_UNDI_ISR_T Isr; ///< Data from ISR + PXENV_UNDI_TBD_T *Xmit; // + VOID *TxRealModeMediaHeader; ///< < 1 MB Size = 0x100 + VOID *TxRealModeDataBuffer; ///< < 1 MB Size = GetInformation.MaxTranUnit + VOID *TxDestAddr; ///< < 1 MB Size = 16 + UINT8 InterruptStatus; ///< returned/cleared by GetStatus, set in ISR + UINTN UndiLoaderTablePages; + UINTN DestinationDataSegmentPages; + UINTN DestinationStackSegmentPages; + UINTN DestinationCodeSegmentPages; + VOID *UndiLoaderTable; + VOID *DestinationDataSegment; + VOID *DestinationStackSegment; + VOID *DestinationCodeSegment; +} EFI_SIMPLE_NETWORK_DEV; + +#define EFI_SIMPLE_NETWORK_DEV_FROM_THIS(a) \ + CR (a, \ + EFI_SIMPLE_NETWORK_DEV, \ + SimpleNetwork, \ + EFI_SIMPLE_NETWORK_DEV_SIGNATURE \ + ) + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gBiosSnp16DriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gBiosSnp16ComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gBiosSnp16ComponentName2; + + +// +// Driver Binding Protocol functions +// +/** + Tests to see if this driver supports a given controller. + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param RemainingDevicePath A pointer to the remaining portion of a device path. + + @retval EFI_SUCCESS The driver supports given controller. + @retval EFI_UNSUPPORT The driver doesn't support given controller. + @retval Other Other errors prevent driver finishing to test + if the driver supports given controller. +**/ +EFI_STATUS +EFIAPI +BiosSnp16DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +; + +/** + Starts the Snp device controller + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param RemainingDevicePath A pointer to the remaining portion of a device path. + + @retval EFI_SUCCESS - The device was started. + @retval EFI_DEVICE_ERROR - The device could not be started due to a device error. + @retval EFI_OUT_OF_RESOURCES - The request could not be completed due to a lack of resources. +**/ +EFI_STATUS +EFIAPI +BiosSnp16DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +; + +/** + Stops the device by given device controller. + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param ChildHandleBuffer An array of child handles to be freed. May be NULL if + NumberOfChildren is 0. + + @retval EFI_SUCCESS - The device was stopped. + @retval EFI_DEVICE_ERROR - The device could not be stopped due to a device error. +**/ +EFI_STATUS +EFIAPI +BiosSnp16DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +; + +// +// Simple Network Protocol functions +// +/** + Call 16 bit UNDI ROM to start the network interface + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_DEVICE_ERROR Network interface has not be initialized. + @retval EFI_DEVICE_ERROR Fail to execute 16 bit ROM call. + @retval EFI_SUCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkStart ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +; + +/** + Call 16 bit UNDI ROM to stop the network interface + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_DEVICE_ERROR Network interface has not be initialized. + @retval EFI_DEVICE_ERROR Fail to execute 16 bit ROM call. + @retval EFI_SUCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkStop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +; + +/** + Initialize network interface + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param ExtraRxBufferSize The size of extra request receive buffer. + @param ExtraTxBufferSize The size of extra request transmit buffer. + + @retval EFI_DEVICE_ERROR Fail to execute 16 bit ROM call. + @retval EFI_SUCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkInitialize ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINTN ExtraRxBufferSize OPTIONAL, + IN UINTN ExtraTxBufferSize OPTIONAL + ) +; + +/** + Reset network interface. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param ExtendedVerification Need extended verfication. + + @retval EFI_INVALID_PARAMETER Invalid This paramter. + @retval EFI_DEVICE_ERROR Network device has not been initialized. + @retval EFI_NOT_STARTED Network device has been stopped. + @retval EFI_DEVICE_ERROR Invalid status for network device + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkReset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +; + +/** + Shutdown network interface. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + + @retval EFI_INVALID_PARAMETER Invalid This paramter. + @retval EFI_DEVICE_ERROR Network device has not been initialized. + @retval EFI_NOT_STARTED Network device has been stopped. + @retval EFI_DEVICE_ERROR Invalid status for network device + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkShutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +; + +/** + Reset network interface. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param Enable Enable mask value + @param Disable Disable mask value + @param ResetMCastFilter Whether reset multi cast filter or not + @param MCastFilterCnt Count of mutli cast filter for different MAC address + @param MCastFilter Buffer for mustli cast filter for different MAC address. + + @retval EFI_INVALID_PARAMETER Invalid This paramter. + @retval EFI_DEVICE_ERROR Network device has not been initialized. + @retval EFI_NOT_STARTED Network device has been stopped. + @retval EFI_DEVICE_ERROR Invalid status for network device + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkReceiveFilters ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * This, + IN UINT32 Enable, + IN UINT32 Disable, + IN BOOLEAN ResetMCastFilter, + IN UINTN MCastFilterCnt OPTIONAL, + IN EFI_MAC_ADDRESS * MCastFilter OPTIONAL + ) +; + +/** + Set new MAC address. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param Reset Whether reset station MAC address to permenent address + @param New A pointer to New address + + @retval EFI_INVALID_PARAMETER Invalid This paramter. + @retval EFI_DEVICE_ERROR Network device has not been initialized. + @retval EFI_NOT_STARTED Network device has been stopped. + @retval EFI_DEVICE_ERROR Invalid status for network device + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkStationAddress ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * This, + IN BOOLEAN Reset, + IN EFI_MAC_ADDRESS * New OPTIONAL + ) +; + +/** + Collect statistics. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param Reset Whether cleanup old statistics data. + @param StatisticsSize The buffer of statistics table. + @param StatisticsTable A pointer to statistics buffer. + + @retval EFI_INVALID_PARAMETER Invalid This paramter. + @retval EFI_DEVICE_ERROR Network device has not been initialized. + @retval EFI_NOT_STARTED Network device has been stopped. + @retval EFI_DEVICE_ERROR Invalid status for network device + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkStatistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * This, + IN BOOLEAN Reset, + IN OUT UINTN *StatisticsSize OPTIONAL, + OUT EFI_NETWORK_STATISTICS * StatisticsTable OPTIONAL + ) +; + +/** + Translate IP address to MAC address. + + @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure. + @param IPv6 IPv6 or IPv4 + @param IP A pointer to given Ip address. + @param MAC On return, translated MAC address. + + @retval EFI_INVALID_PARAMETER Invalid This paramter. + @retval EFI_INVALID_PARAMETER Invalid IP address. + @retval EFI_INVALID_PARAMETER Invalid return buffer for holding MAC address. + @retval EFI_UNSUPPORTED Do not support IPv6 + @retval EFI_DEVICE_ERROR Network device has not been initialized. + @retval EFI_NOT_STARTED Network device has been stopped. + @retval EFI_DEVICE_ERROR Invalid status for network device + @retval EFI_SUCCESS Success operation. +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkMCastIpToMac ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN IPv6, + IN EFI_IP_ADDRESS *IP, + OUT EFI_MAC_ADDRESS *MAC + ) +; + +/** + Performs read and write operations on the NVRAM device attached to a + network interface. + + @param This The protocol instance pointer. + @param ReadWrite TRUE for read operations, FALSE for write operations. + @param Offset Byte offset in the NVRAM device at which to start the read or + write operation. This must be a multiple of NvRamAccessSize and + less than NvRamSize. + @param BufferSize The number of bytes to read or write from the NVRAM device. + This must also be a multiple of NvramAccessSize. + @param Buffer A pointer to the data buffer. + + @retval EFI_SUCCESS The NVRAM access was performed. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkNvData ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN Write, + IN UINTN Offset, + IN UINTN BufferSize, + IN OUT VOID *Buffer + ) +; + +/** + Reads the current interrupt status and recycled transmit buffer status from + a network interface. + + @param This The protocol instance pointer. + @param InterruptStatus A pointer to the bit mask of the currently active interrupts + If this is NULL, the interrupt status will not be read from + the device. If this is not NULL, the interrupt status will + be read from the device. When the interrupt status is read, + it will also be cleared. Clearing the transmit interrupt + does not empty the recycled transmit buffer array. + @param TxBuf Recycled transmit buffer address. The network interface will + not transmit if its internal recycled transmit buffer array + is full. Reading the transmit buffer does not clear the + transmit interrupt. If this is NULL, then the transmit buffer + status will not be read. If there are no transmit buffers to + recycle and TxBuf is not NULL, * TxBuf will be set to NULL. + + @retval EFI_SUCCESS The status of the network interface was retrieved. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkGetStatus ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * This, + OUT UINT32 *InterruptStatus OPTIONAL, + OUT VOID **TxBuf OPTIONAL + ) +; + +/** + Places a packet in the transmit queue of a network interface. + + @param This The protocol instance pointer. + @param HeaderSize The size, in bytes, of the media header to be filled in by + the Transmit() function. If HeaderSize is non-zero, then it + must be equal to This->Mode->MediaHeaderSize and the DestAddr + and Protocol parameters must not be NULL. + @param BufferSize The size, in bytes, of the entire packet (media header and + data) to be transmitted through the network interface. + @param Buffer A pointer to the packet (media header followed by data) to be + transmitted. This parameter cannot be NULL. If HeaderSize is zero, + then the media header in Buffer must already be filled in by the + caller. If HeaderSize is non-zero, then the media header will be + filled in by the Transmit() function. + @param SrcAddr The source HW MAC address. If HeaderSize is zero, then this parameter + is ignored. If HeaderSize is non-zero and SrcAddr is NULL, then + This->Mode->CurrentAddress is used for the source HW MAC address. + @param DestAddr The destination HW MAC address. If HeaderSize is zero, then this + parameter is ignored. + @param Protocol The type of header to build. If HeaderSize is zero, then this + parameter is ignored. See RFC 1700, section "Ether Types", for + examples. + + @retval EFI_SUCCESS The packet was placed on the transmit queue. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkTransmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINTN HeaderSize, + IN UINTN BufferSize, + IN VOID *Buffer, + IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + IN EFI_MAC_ADDRESS *DestAddr OPTIONAL, + IN UINT16 *Protocol OPTIONAL + ) +; + +/** + Receives a packet from a network interface. + + @param This The protocol instance pointer. + @param HeaderSize The size, in bytes, of the media header received on the network + interface. If this parameter is NULL, then the media header size + will not be returned. + @param BufferSize On entry, the size, in bytes, of Buffer. On exit, the size, in + bytes, of the packet that was received on the network interface. + @param Buffer A pointer to the data buffer to receive both the media header and + the data. + @param SrcAddr The source HW MAC address. If this parameter is NULL, the + HW MAC source address will not be extracted from the media + header. + @param DestAddr The destination HW MAC address. If this parameter is NULL, + the HW MAC destination address will not be extracted from the + media header. + @param Protocol The media header type. If this parameter is NULL, then the + protocol will not be extracted from the media header. See + RFC 1700 section "Ether Types" for examples. + + @retval EFI_SUCCESS The received data was stored in Buffer, and BufferSize has + been updated to the number of bytes received. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit + request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +EFI_STATUS +EFIAPI +Undi16SimpleNetworkReceive ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + OUT UINTN *HeaderSize OPTIONAL, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer, + OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + OUT EFI_MAC_ADDRESS *DestAddr OPTIONAL, + OUT UINT16 *Protocol OPTIONAL + ) +; + +/** + wait for a packet to be received. + + @param Event Event used with WaitForEvent() to wait for a packet to be received. + @param Context Event Context + +**/ +VOID +EFIAPI +Undi16SimpleNetworkWaitForPacket ( + IN EFI_EVENT Event, + IN VOID *Context + ) +; + +/** + Check whether packet is ready for receive. + + @param This The protocol instance pointer. + + @retval EFI_SUCCESS Receive data is ready. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit + request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. +**/ +EFI_STATUS +Undi16SimpleNetworkCheckForPacket ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +; + +/** + Cache Interrupt verctor address converted from IVT number. + + @param VectorNumber IVT number + + @retval EFI_SUCCESS Success to operation. +**/ +EFI_STATUS +CacheVectorAddress ( + UINT8 VectorNumber + ) +; + +/** + Get interrupt vector address according to IVT number. + + @param VectorNumber Given IVT number + + @return cached interrupt vector address. +**/ +EFI_STATUS +RestoreCachedVectorAddress ( + UINT8 VectorNumber + ) +; + +/** + If available, launch the BaseCode from a NIC option ROM. + This should install the !PXE and PXENV+ structures in memory for + subsequent use. + + + @param SimpleNetworkDevice Simple network device instance + @param RomAddress The ROM base address for NIC rom. + + @retval EFI_NOT_FOUND The check sum does not match + @retval EFI_NOT_FOUND Rom ID offset is wrong + @retval EFI_NOT_FOUND No Rom ID structure is found +**/ +EFI_STATUS +LaunchBaseCode ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + UINTN RomAddress + ) +; + +/** + PXE + START UNDI + Op-Code: PXENV_START_UNDI (0000h) + Input: Far pointer to a PXENV_START_UNDI_T parameter structure that has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This service is used to pass the BIOS parameter registers to the UNDI driver. The UNDI driver is + responsible for saving the information it needs to communicate with the hardware. + This service is also responsible for hooking the Int 1Ah service routine + Note: This API service must be called only once during UNDI Option ROM boot. + The UNDI driver is responsible for saving this information and using it every time + PXENV_UNDI_STARTUP is called. + Service cannot be used in protected mode. + typedef struct { + PXENV_STATUS Status; + UINT16 AX; + UINT16 BX; + UINT16 DX; + UINT16 DI; + UINT16 ES; + } PXENV_START_UNDI_T; + Set before calling API service + AX, BX, DX, DI, ES: BIOS initialization parameter registers. These + fields should contain the same information passed to the option ROM + initialization routine by the Host System BIOS. Information about the + contents of these registers can be found in the [PnP], [PCI] and + [BBS] specifications. + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeStartUndi ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_START_UNDI_T *PxeUndiTable + ) +; + +/** + PXE + UNDI STARTUP + Op-Code: PXENV_UNDI_STARTUP (0001h) + Input: Far pointer to a PXENV_UNDI_STARTUP_T parameter structure that has been initialized by the + caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This API is responsible for initializing the contents of the UNDI code & data segment for proper + operation. Information from the !PXE structure and the first PXENV_START_UNDI API call is used + to complete this initialization. The rest of the UNDI APIs will not be available until this call has + been completed. + Note: PXENV_UNDI_STARTUP must not be called again without first calling + PXENV_UNDI_SHUTDOWN. + PXENV_UNDI_STARTUP and PXENV_UNDI_SHUTDOWN are no longer responsible for + chaining interrupt 1Ah. This must be done by the PXENV_START_UNDI and + PXENV_STOP_UNDI API calls. + This service cannot be used in protected mode. + typedef struct + { + PXENV_STATUS Status; + } PXENV_UNDI_STARTUP_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiStartup ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_STARTUP_T *PxeUndiTable + ) +; + +/** + PXE + UNDI CLEANUP + Op-Code: PXENV_UNDI_CLEANUP (0002h) + Input: Far pointer to a PXENV_UNDI_CLEANUP_T parameter structure. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field + in the parameter structure must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This call will prepare the network adapter driver to be unloaded from memory. This call must be + made just before unloading the Universal NIC Driver. The rest of the API will not be available + after this call executes. + This service cannot be used in protected mode. + typedef struct { + PXENX_STATUS Status; + } PXENV_UNDI_CLEANUP_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiCleanup ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_CLEANUP_T *PxeUndiTable + ) +; + +/** + PXE + UNDI INITIALIZE + Op-Code: PXENV_UNDI_INITIALIZE (0003h) + Input: Far pointer to a PXENV_UNDI_INITIALIZE_T parameter structure that has been initialized by the + caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call resets the adapter and programs it with default parameters. The default parameters used + are those supplied to the most recent UNDI_STARTUP call. This routine does not enable the + receive and transmit units of the network adapter to readily receive or transmit packets. The + application must call PXENV_UNDI_OPEN to logically connect the network adapter to the network. + This call must be made by an application to establish an interface to the network adapter driver. + Note: When the PXE code makes this call to initialize the network adapter, it passes a NULL pointer for + the Protocol field in the parameter structure. + typedef struct { + PXENV_STATUS Status; + ADDR32 ProtocolIni; + UINT8 reserved[8]; + } PXENV_UNDI_INITIALIZE_T; + Set before calling API service + ProtocolIni: Physical address of a memory copy of the driver + module from the protocol.ini file obtained from the protocol manager + driver (refer to the NDIS 2.0 specification). This parameter is + supported for the universal NDIS driver to pass the information + contained in the protocol.ini file to the NIC driver for any specific + configuration of the NIC. (Note that the module identification in the + protocol.ini file was done by NDIS.) This value can be NULL for any + other application interfacing to the universal NIC driver + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance. + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiInitialize ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_INITIALIZE_T *PxeUndiTable + ) +; + +/** + Wrapper routine for reset adapter. + + PXE + UNDI RESET ADAPTER + Op-Code: PXENV_UNDI_RESET_ADAPTER (0004h) + Input: Far pointer to a PXENV_UNDI_RESET_ADAPTER_t parameter structure that has been initialized + by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call resets and reinitializes the network adapter with the same set of parameters supplied to + Initialize Routine. Unlike Initialize, this call opens the adapter that is, it connects logically to the + network. This routine cannot be used to replace Initialize or Shutdown calls. + typedef struct { + PXENV_STATUS Status; + PXENV_UNDI_MCAST_ADDRESS_t R_Mcast_Buf; + } PXENV_UNDI_RESET_T; + + #define MAXNUM_MCADDR 8 + + typedef struct { + UINT16 MCastAddrCount; + MAC_ADDR McastAddr[MAXNUM_MCADDR]; + } PXENV_UNDI_MCAST_ADDRESS_t; + + Set before calling API service + R_Mcast_Buf: This is a structure of MCastAddrCount and + McastAddr. + MCastAddrCount: Number of multicast MAC addresses in the + buffer. + McastAddr: List of up to MAXNUM_MCADDR multicast MAC + addresses. + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance. + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + @param RxFilter Filter setting mask value for PXE recive . + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiResetNic ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_RESET_T *PxeUndiTable, + IN UINT16 RxFilter + ) +; + +/** + PXE + UNDI SHUTDOWN + Op-Code: PXENV_UNDI_SHUTDOWN (0005h) + Input: Far pointer to a PXENV_UNDI_SHUTDOWN_T parameter. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call resets the network adapter and leaves it in a safe state for another driver to program it. + Note: The contents of the PXENV_UNDI_STARTUP parameter structure need to be saved by the + Universal NIC Driver in case PXENV_UNDI_INITIALIZE is called again. + typedef struct + { + PXENV_STATUS Status; + } PXENV_UNDI_SHUTDOWN_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiShutdown ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_SHUTDOWN_T *PxeUndiTable + ) +; + +/** + PXE + UNDI OPEN + Op-Code: PXENV_UNDI_OPEN (0006h) + Input: Far pointer to a PXENV_UNDI_OPEN_T parameter structure that has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call activates the adapter network connection and sets the adapter ready to accept packets + for transmit and receive. + typedef struct { + PXENV_STATUS Status; + UINT16 OpenFlag; + UINT16 PktFilter; + #define FLTR_DIRECTED 0x0001 + #define FLTR_BRDCST 0x0002 + #define FLTR_PRMSCS 0x0004 + #define FLTR_SRC_RTG 0x0008 + PXENV_UNDI_MCAST_ADDRESS_t R_Mcast_Buf; + } PXENV_UNDI_OPEN_T; + Set before calling API service + OpenFlag: This is an adapter specific input parameter. This is + supported for the universal NDIS 2.0 driver to pass in the open flags + provided by the protocol driver. (See the NDIS 2.0 specification.) + This can be zero. + PktFilter: Filter for receiving packets. This can be one, or more, of + the FLTR_xxx constants. Multiple values are arithmetically or-ed + together. + directed packets are packets that may come to your MAC address + or the multicast MAC address. + R_Mcast_Buf: See definition in UNDI RESET ADAPTER (0004h). + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiOpen ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_OPEN_T *PxeUndiTable + ) +; + +/** + PXE + UNDI CLOSE + Op-Code: PXENV_UNDI_CLOSE (0007h) + Input: Far pointer to a PXENV_UNDI_CLOSE_T parameter. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call disconnects the network adapter from the network. Packets cannot be transmitted or + received until the network adapter is open again. + typedef struct { + PXENV_STATUS Status; + } PXENV_UNDI_CLOSE_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiClose ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_CLOSE_T *PxeUndiTable + ) +; + +/** + PXE + UNDI TRANSMIT PACKET + Op-Code: PXENV_UNDI_TRANSMIT (0008h) + Input: Far pointer to a PXENV_UNDI_TRANSMIT_T parameter structure that + has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. + The status code must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This call transmits a buffer to the network. The media header + for the packet can be filled by the calling protocol, but it might not be. + The network adapter driver will fill it if required by the values in the + parameter block. The packet is buffered for transmission provided there is + an available buffer, and the function returns PXENV_EXIT_SUCCESS. If no + buffer is available the function returns PXENV_EXIT_FAILURE with a status + code of PXE_UNDI_STATUS__OUT OF_RESOURCE. The number of buffers is + implementation-dependent. An interrupt is generated on completion of the + transmission of one or more packets. A call to PXENV_UNDI_TRANSMIT is + permitted in the context of a transmit complete interrupt. + + typedef struct { + PXENV_STATUS Status; + UINT8 Protocol; + #define P_UNKNOWN 0 + #define P_IP 1 + #define P_ARP 2 + #define P_RARP 3 + UINT8 XmitFlag; + #define XMT_DESTADDR 0x0000 + #define XMT_BROADCAST 0x0001 + SEGOFF16 DestAddr; + SEGOFF16 TBD; + UINT32 Reserved[2]; + } t_PXENV_UNDI_TRANSMIT; + + #define MAX_DATA_BLKS 8 + + typedef struct { + UINT16 ImmedLength; + SEGOFF16 Xmit; + UINT16 DataBlkCount; + struct DataBlk { + UINT8 TDPtrType; + UINT8 TDRsvdByte; + UINT16 TDDataLen; + SEGOFF16 TDDataPtr; + } DataBlock[MAX_DATA_BLKS]; + } PXENV_UNDI_TBD_T + + Set before calling API service + Protocol: This is the protocol of the upper layer that is calling UNDI + TRANSMIT call. If the upper layer has filled the media header, this + field must be P_UNKNOWN. + XmitFlag: If this flag is XMT_DESTADDR, the NIC driver expects a + pointer to the destination media address in the field DestAddr. If + XMT_BROADCAST, the NIC driver fills the broadcast address for the + destination. + TBD: Segment:Offset address of the transmit buffer descriptor. + ImmedLength: Length of the immediate transmit buffer: Xmit. + Xmit: Segment:Offset of the immediate transmit buffer. + DataBlkCount: Number of blocks in this transmit buffer. + TDPtrType: + 0 => 32-bit physical address in TDDataPtr (not supported in this + version of PXE) + 1 => segment:offset in TDDataPtr which can be a real mode or 16-bit + protected mode pointer + TDRsvdByte: Reserved must be zero. + TDDatalen: Data block length in bytes. + TDDataPtr: Segment:Offset of the transmit block. + DataBlock: Array of transmit data blocks. + Returned from API service + Status: See the PXENV_STATUS_xxx constants + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiTransmit ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_TRANSMIT_T *PxeUndiTable + ) +; + +/** + PXE + UNDI SET MULTICAST ADDRESS + Op-Code: PXENV_UNDI_SET_MCAST_ADDRESS (0009h) + Input: Far pointer to a PXENV_TFTP_SET_MCAST_ADDRESS_t parameter structure that has been + initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call changes the current list of multicast addresses to the input list and resets the network + adapter to accept it. If the number of multicast addresses is zero, multicast is disabled. + typedef struct { + PXENV_STATUS Status; + PXENV_UNDI_MCAST_ADDRESS_t R_Mcast_Buf; + } PXENV_UNDI_SET_MCAST_ADDR_T; + Set before calling API service + R_Mcast_Buf: See description in the UNDI RESET ADAPTER + (0004h) API. + Returned from API service + Status: See the PXENV_STATUS_xxx constants + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiSetMcastAddr ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_SET_MCAST_ADDR_T *PxeUndiTable + ) +; + +/** + PXE + UNDI SET STATION ADDRESS + Op-Code: PXENV_UNDI_SET_STATION_ADDRESS (000Ah) + Input: Far pointer to a PXENV_UNDI_SET_STATION_ADDRESS_t parameter structure that has been + initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call sets the MAC address to be the input value and is called before opening the network + adapter. Later, the open call uses this variable as a temporary MAC address to program the + adapter individual address registers. + typedef struct { + PXENV_STATUS Status; + MAC_ADDR StationAddress; + } PXENV_UNDI_SET_STATION_ADDR_T; + Set before calling API service + StationAddress: Temporary MAC address to be used for + transmit and receive. + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiSetStationAddr ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_SET_STATION_ADDR_T *PxeUndiTable + ) +; + +/** + PXE + UNDI SET PACKET FILTER + Op-Code: PXENV_UNDI_SET_PACKET_FILTER (000Bh) + Input: Far pointer to a PXENV_UNDI_SET_PACKET_FILTER_T parameter structure that has been + initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call resets the adapter's receive unit to accept a new filter, different from the one provided with + the open call. + typedef struct { + PXENV_STATUS Status; + UINT8 filter; + } PXENV_UNDI_SET_PACKET_FILTER_T; + Set before calling API service + Filter: See the receive filter values in the UNDI OPEN + (0006h) API description. + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiSetPacketFilter ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_SET_PACKET_FILTER_T *PxeUndiTable + ) +; + +/** + PXE + UNDI GET INFORMATION + Op-Code: PXENV_UNDI_GET_INFORMATION (000Ch) + Input: Far pointer to a PXENV_UNDI_GET_INFORMATION_T parameter structure that has been + initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This call copies the network adapter variables, including the MAC address, into the input buffer. + Note: The PermNodeAddress field must be valid after PXENV_START_UNDI and + PXENV_UNDI_STARTUP have been issued. All other fields must be valid after + PXENV_START_UNDI, PXENV_UNDI_STARTUP and PXENV_UNDI_INITIALIZE have been + called. + typedef struct { + PXENV_STATUS Status; + UINT16 BaseIo; + UINT16 IntNumber; + UINT16 MaxTranUnit; + UINT16 HwType; + #define ETHER_TYPE 1 + #define EXP_ETHER_TYPE 2 + #define IEEE_TYPE 6 + #define ARCNET_TYPE 7 + UINT16 HwAddrLen; + MAC_ADDR CurrentNodeAddress; + MAC_ADDR PermNodeAddress; + SEGSEL ROMAddress; + UINT16 RxBufCt; + UINT16 TxBufCt; + } PXENV_UNDI_GET_INFORMATION_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + BaseIO: Adapter base I/O address. + IntNumber: Adapter IRQ number. + MaxTranUnit: Adapter maximum transmit unit. + HWType: Type of protocol at the hardware level. + HWAddrLen: Length of the hardware address. + CurrentNodeAddress: Current hardware address. + PermNodeAddress: Permanent hardware address. + ROMAddress: Real mode ROM segment address. + RxBufCnt: Receive queue length. + TxBufCnt: Transmit queue length. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetInformation ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_INFORMATION_T *PxeUndiTable + ) +; + +/** + PXE + UNDI GET STATISTICS + Op-Code: PXENV_UNDI_GET_STATISTICS (000Dh) + Input: Far pointer to a PXENV_UNDI_GET_STATISTICS_T parameter structure that has been initialized + by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call reads statistical information from the network adapter, and returns. + typedef struct { + PXENV_STATUS Status; + UINT32 XmtGoodFrames; + UINT32 RcvGoodFrames; + UINT32 RcvCRCErrors; + UINT32 RcvResourceErrors; + } PXENV_UNDI_GET_STATISTICS_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + XmtGoodFrames: Number of successful transmissions. + RcvGoodFrames: Number of good frames received. + RcvCRCErrors: Number of frames received with CRC + error. + RcvResourceErrors: Number of frames discarded + because receive queue was full. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetStatistics ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_STATISTICS_T *PxeUndiTable + ) +; + +/** + PXE + UNDI CLEAR STATISTICS + Op-Code: PXENV_UNDI_CLEAR_STATISTICS (000Eh) + Input: Far pointer to a PXENV_UNDI_CLEAR_STATISTICS_T parameter. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This call clears the statistical information from the network adapter. + typedef struct { + PXENV_STATUS Status; + } PXENV_UNDI_CLEAR_STATISTICS_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiClearStatistics ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_CLEAR_STATISTICS_T *PxeUndiTable + ) +; + +/** + PXE + UNDI INITIATE DIAGS + Op-Code: PXENV_UNDI_INITIATE_DIAGS (000Fh) + Input: Far pointer to a PXENV_UNDI_INITIATE_DIAGS_T parameter. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This call can be used to initiate the run-time diagnostics. It causes the network adapter to run + hardware diagnostics and to update its status information. + typedef struct { + PXENV_STATUS Status; + } PXENV_UNDI_INITIATE_DIAGS_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiInitiateDiags ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_INITIATE_DIAGS_T *PxeUndiTable + ) +; + +/** + PXE + UNDI FORCE INTERRUPT + Op-Code: PXENV_UNDI_FORCE_INTERRUPT (0010h) + Input: Far pointer to a PXENV_UNDI_FORCE_INTERRUPT_T parameter structure that has been + initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call forces the network adapter to generate an interrupt. When a receive interrupt occurs, the + network adapter driver usually queues the packet and calls the application's callback receive + routine with a pointer to the packet received. Then, the callback routine either can copy the packet + to its buffer or can decide to delay the copy to a later time. If the packet is not immediately copied, + the network adapter driver does not remove it from the input queue. When the application wants to + copy the packet, it can call the PXENV_UNDI_FORCE_INTERRUPT routine to simulate the receive + interrupt. + typedef struct { + PXENV_STATUS Status; + } PXENV_UNDI_FORCE_INTERRUPT_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiForceInterrupt ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_FORCE_INTERRUPT_T *PxeUndiTable + ) +; + +/** + PXE + UNDI GET MULTICAST ADDRESS + Op-Code: PXENV_UNDI_GET_MCAST_ADDRESS (0011h) + Input: Far pointer to a PXENV_GET_MCAST_ADDRESS_t parameter structure that has been initialized + by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call converts the given IP multicast address to a hardware multicast address. + typedef struct { + PXENV_STATUS Status; + IP4 InetAddr; + MAC_ADDR MediaAddr; + } PXENV_UNDI_GET_MCAST_ADDR_T; + Set before calling API service + InetAddr: IP multicast address. + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + MediaAddr: MAC multicast address. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetMcastAddr ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_MCAST_ADDR_T *PxeUndiTable + ) +; + +/** + PXE + UNDI GET NIC TYPE + Op-Code: PXENV_UNDI_GET_NIC_TYPE (0012h) + Input: Far pointer to a PXENV_UNDI_GET_NIC_TYPE_T parameter structure that has been initialized by + the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. If the PXENV_EXIT_SUCCESS is returned the parameter structure must contain the + NIC information. + Description: This call, if successful, provides the NIC-specific information necessary to identify the network + adapter that is used to boot the system. + Note: The application first gets the DHCPDISCOVER packet using GET_CACHED_INFO and checks if + the UNDI is supported before making this call. If the UNDI is not supported, the NIC-specific + information can be obtained from the DHCPDISCOVER packet itself. + PXENV_START_UNDI, PXENV_UNDI_STARTUP and PXENV_UNDI_INITIALIZE must be called + before the information provided is valid. + typedef { + PXENV_STATUS Status; + UINT8 NicType; + #define PCI_NIC 2 + #define PnP_NIC 3 + #define CardBus_NIC 4 + Union { + Struct { + UINT16 Vendor_ID; + UINT16 Dev_ID; + UINT8 Base_Class; + UINT8 Sub_Class; + UINT8 Prog_Intf; + UINT8 Rev; + UINT16 BusDevFunc; + UINT16 SubVendor_ID; + UINT16 SubDevice_ID; + } pci, cardbus; + struct { + UINT32 EISA_Dev_ID; + UINT8 Base_Class; + UINT8 Sub_Class; + UINT8 Prog_Intf; + UINT16 CardSelNum; + } pnp; + } info; + } PXENV_UNDI_GET_NIC_TYPE_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + NICType: Type of NIC information stored in the parameter + structure. + Info: Information about the fields in this union can be found + in the [PnP] and [PCI] specifications + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetNicType ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_NIC_TYPE_T *PxeUndiTable + ) +; + +/** + PXE + UNDI GET IFACE INFO + Op-Code: PXENV_UNDI_GET_IFACE_INFO (0013h) + Input: Far pointer to a PXENV_UNDI_GET_IFACE_INFO_t parameter structure that has been initialized + by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. If the PXENV_EXIT_SUCCESS is returned, the parameter structure must contain the + interface specific information. + Description: This call, if successful, provides the network interface specific information such as the interface + type at the link layer (Ethernet, Tokenring) and the link speed. This information can be used in the + universal drivers such as NDIS or Miniport to communicate to the upper protocol modules. + Note: UNDI follows the NDIS2 specification in giving this information. It is the responsibility of the + universal driver to translate/convert this information into a format that is required in its specification + or to suit the expectation of the upper level protocol modules. + PXENV_START_UNDI, PXENV_UNDI_STARTUP and PXENV_UNDI_INITIALIZE must be called + before the information provided is valid. + typedef struct { + PXENV_STATUS Status + UINT8 IfaceType[16]; + UINT32 LinkSpeed; + UINT32 ServiceFlags; + UINT32 Reserved[4]; + } PXENV_UNDI_GET_NDIS_INFO_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + IfaceType: Name of MAC type in ASCIIZ format. This is + used by the universal NDIS driver to specify its driver type + to the protocol driver. + LinkSpeed: Defined in the NDIS 2.0 specification. + ServiceFlags: Defined in the NDIS 2.0 specification. + Reserved: Must be zero. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetNdisInfo ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_NDIS_INFO_T *PxeUndiTable + ) +; + +/** + PXE + UNDI ISR + Op-Code: PXENV_UNDI_ISR (0014h) + Input: Far pointer to a PXENV_UNDI_ISR_T parameter structure that has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This API function will be called at different levels of processing the interrupt. The FuncFlag field in + the parameter block indicates the operation to be performed for the call. This field is filled with the + status of that operation on return. + Note: Interrupt Service Routine Operation: + In this design the UNDI does not hook the interrupt for the Network Interface. Instead, the + application or the protocol driver hooks the interrupt and calls UNDI with the PXENV_UNDI_ISR + API call for interrupt verification (PXENV_UNDI_ISR_IN_START) and processing + (PXENV_UNDI_ISR_IN_PROCESS and PXENV_UNDI_ISR_GET_NEXT). + When the Network Interface HW generates an interrupt the protocol driver interrupt service + routine (ISR) gets control and takes care of the interrupt processing at the PIC level. The ISR then + calls the UNDI using the PXENV_UNDI_ISR API with the value PXENV_UNDI_ISR_IN_START for + the FuncFlag parameter. At this time UNDI must disable the interrupts at the Network Interface + level and read any status values required to further process the interrupt. UNDI must return as + quickly as possible with one of the two values, PXENV_UNDI_ISR_OUT_OURS or + PXENV_UNDI_ISR_OUT_NOT_OURS, for the parameter FuncFlag depending on whether the + interrupt was generated by this particular Network Interface or not. + If the value returned in FuncFlag is PXENV_UNDI_ISR_OUT_NOT_OURS, then the interrupt was + not generated by our NIC, and interrupt processing is complete. + If the value returned in FuncFlag is PXENV_UNDI_ISR_OUT_OURS, the protocol driver must start + a handler thread and send an end-of-interrupt (EOI) command to the PIC. Interrupt processing is + now complete. + The protocol driver strategy routine will call UNDI using this same API with FuncFlag equal to + PXENV_UNDI_ISR_IN_PROCESS. At this time UNDI must find the cause of this interrupt and + return the status in the FuncFlag. It first checks if there is a frame received and if so it returns the + first buffer pointer of that frame in the parameter block. + The protocol driver calls UNDI repeatedly with the FuncFlag equal to + PXENV_UNDI_ISR_IN_GET_NEXT to get all the buffers in a frame and also all the received + frames in the queue. On this call, UNDI must remember the previous buffer given to the protoco,l + remove it from the receive queue and recycle it. In case of a multi-buffered frame, if the previous + buffer is not the last buffer in the frame it must return the next buffer in the frame in the parameter + block. Otherwise it must return the first buffer in the next frame. + If there is no received frame pending to be processed, UNDI processes the transmit completes and + if there is no other interrupt status to be processed, UNDI re-enables the interrupt at the + NETWORK INTERFACE level and returns PXENV_UNDI_ISR_OUT_DONE in the FuncFlag. + IMPORTANT: It is possible for the protocol driver to be interrupted again while in the + strategy routine when the UNDI re-enables interrupts. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiIsr ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_ISR_T *PxeUndiTable + ) +; + +/** + PXE + STOP UNDI + Op-Code: PXENV_STOP_UNDI (0015h) + Input: Far pointer to a PXENV_STOP_UNDI_T parameter structure that has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This routine is responsible for unhooking the Int 1Ah service routine. + Note: This API service must be called only once at the end of UNDI Option ROM boot. One of the valid + status codes is PXENV_STATUS_KEEP. If this status is returned, UNDI must not be removed from + base memory. Also, UNDI must not be removed from base memory if BC is not removed from base + memory. + Service cannot be used in protected mode. + typedef struct { + PXENV_STATUS Status; + } PXENV_STOP_UNDI_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiStop ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_STOP_UNDI_T *PxeUndiTable + ) +; + +/** + PXE + UNDI GET STATE + Op-Code: PXENV_UNDI_GET_STATE (0015h) + Input: Far pointer to a PXENV_UNDI_GET_STATE_T parameter. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. The UNDI_STATE field in the parameter structure must be set to one of the valid state + constants + Description: This call can be used to obtain state of the UNDI engine in order to avoid issuing adverse call + sequences + typedef struct { + #define PXE_UNDI_GET_STATE_STARTED 1 + #define PXE_UNDI_GET_STATE_INITIALIZED 2 + #define PXE_UNDI_GET_STATE_OPENED 3 + PXENV_STATUS Status; + UINT8 UNDIstate; + } PXENV_UNDI_GET_STATE_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + State: See definitions of the state constants. + Note. UNDI implementation is responsible for maintaining + internal state machine. + UNDI ISR + Op-Code: PXENV_UNDI_ISR (0014h) + Input: Far pointer to a t_PXENV_UNDI_ISR parameter structure that has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This API function will be called at different levels of processing the interrupt. The FuncFlag field in + the parameter block indicates the operation to be performed for the call. This field is filled with the + status of that operation on return. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetState ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_STATE_T *PxeUndiTable + ) +; + +/** + Effect the Far Call into the PXE Layer + + Note: When using a 32-bit stack segment do not push 32-bit words onto the stack. The PXE API + services will not work, unless there are three 16-bit parameters pushed onto the stack. + push DS ;Far pointer to parameter structure + push offset pxe_data_call_struct ;is pushed onto stack. + push Index ;UINT16 is pushed onto stack. + call dword ptr (s_PXE ptr es:[di]).EntryPointSP + add sp, 6 ;Caller cleans up stack. + + @param SimpleNetworkDevice Device instance for simple network + @param Table Point to parameter/retun value table for legacy far call + @param TableSize The size of paramter/return value table + @param CallIndex The index of legacy call. + + @return EFI_STATUS +**/ +EFI_STATUS +MakePxeCall ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT VOID *Table, + IN UINTN TableSize, + IN UINT16 CallIndex + ) +; + +/** + Allocate buffer below 1M for real mode. + + @param NumPages The number pages want to be allocated. + @param Buffer On return, allocated buffer. + + @return Status of allocating pages. +**/ +EFI_STATUS +BiosSnp16AllocatePagesBelowOneMb ( + UINTN NumPages, + VOID **Buffer + ) +; + +#endif diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/ComponentName.c b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/ComponentName.c new file mode 100644 index 0000000000..a95d83459d --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/ComponentName.c @@ -0,0 +1,309 @@ +/** @file + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "BiosSnp16.h" + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosSnp16ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosSnp16ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gBiosSnp16ComponentName = { + BiosSnp16ComponentNameGetDriverName, + BiosSnp16ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gBiosSnp16ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) BiosSnp16ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) BiosSnp16ComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mBiosSnp16DriverNameTable[] = { + { + "eng;en", + L"BIOS[UNDI] Simple Network Protocol Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosSnp16ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mBiosSnp16DriverNameTable, + DriverName, + (BOOLEAN)(This == &gBiosSnp16ComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosSnp16ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Misc.c b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Misc.c new file mode 100644 index 0000000000..0b3543e12c --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Misc.c @@ -0,0 +1,956 @@ +/** @file + Helper Routines that use a PXE-enabled NIC option ROM. + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "BiosSnp16.h" + +#define TO_SEGMENT(x) ((UINT16) (RShiftU64 ((UINT32)(UINTN) (x), 4) & 0xF000)) +#define TO_OFFSET(x) ((UINT16) ((UINT32)(UINTN) (x) & 0xFFFF)) +#define PARAGRAPH_SIZE 0x10 +#define IVT_BASE 0x00000000 + +#pragma pack(1) +typedef struct { + UINT16 Signature; ///< 0xaa55 + UINT8 ROMlength; ///< size of this ROM in 512 byte blocks + UINT8 InitEntryPoint[4]; ///< a jump to the initialization routine + UINT8 Reserved[0xf]; ///< various + UINT16 PxeRomIdOffset; ///< offset of UNDI, $BC$, or BUSD ROM ID structure + UINT16 PcirHeaderOffset; ///< offset of PCI Expansion Header + UINT16 PnpHeaderOffset; ///< offset of Plug and Play Expansion Header +} OPTION_ROM_HEADER; +#pragma pack() + +UINT32 CachedVectorAddress[0x100]; + +/** + Cache Interrupt verctor address converted from IVT number. + + @param VectorNumber IVT number + + @retval EFI_SUCCESS Success to operation. +**/ +EFI_STATUS +CacheVectorAddress ( + UINT8 VectorNumber + ) +{ + UINT32 *Address; + + Address = (UINT32 *)(UINTN) (IVT_BASE + VectorNumber * 4); + CachedVectorAddress[VectorNumber] = *Address; + return EFI_SUCCESS; +} + +/** + Get interrupt vector address according to IVT number. + + @param VectorNumber Given IVT number + + @return cached interrupt vector address. +**/ +EFI_STATUS +RestoreCachedVectorAddress ( + UINT8 VectorNumber + ) +{ + UINT32 *Address; + + Address = (UINT32 *)(UINTN) (IVT_BASE + VectorNumber * 4); + *Address = CachedVectorAddress[VectorNumber]; + return EFI_SUCCESS; +} + +/** + Print Undi loader table. + + @param UndiLoaderStructure Point to Undi Loader table structure. + +**/ +VOID +Print_Undi_Loader_Table ( + VOID *UndiLoaderStructure + ) +{ + UNDI_LOADER_T *DisplayPointer; + + DisplayPointer = (UNDI_LOADER_T *) UndiLoaderStructure; + + DEBUG ((DEBUG_NET, "Before Parsing the table contents, the table itself lives\n")); + DEBUG ((DEBUG_NET, "\tat the address 0x%X\n\r", (UINT32)(UINTN) UndiLoaderStructure)); + + DEBUG ((DEBUG_NET, "\n\rStatus = 0x%X\n\r", DisplayPointer->Status)); + DEBUG ((DEBUG_NET, "\t_AX_= 0x%X\n\r", DisplayPointer->Ax)); + DEBUG ((DEBUG_NET, "\t_BX_= 0x%X\n\r", DisplayPointer->Bx)); + DEBUG ((DEBUG_NET, "\t_DX_= 0x%X\n\r", DisplayPointer->Dx)); + DEBUG ((DEBUG_NET, "\t_DI_= 0x%X\n\r", DisplayPointer->Di)); + DEBUG ((DEBUG_NET, "\t_ES_= 0x%X\n\r", DisplayPointer->Es)); + DEBUG ((DEBUG_NET, "\tUNDI_DS= 0x%X\n\r", DisplayPointer->Undi_Ds)); + DEBUG ((DEBUG_NET, "\tUNDI_CS= 0x%X\n\r", DisplayPointer->Undi_Cs)); + DEBUG ((DEBUG_NET, "\tPXEptr:SEG= 0x%X\n\r", (UINT16) DisplayPointer->PXEptr.Segment)); + DEBUG ((DEBUG_NET, "\tPXEptr:OFF= 0x%X\n\r", (UINT16) DisplayPointer->PXEptr.Offset)); + DEBUG ((DEBUG_NET, "\tPXENVptr:SEG= 0x%X\n\r", (UINT16) DisplayPointer->PXENVptr.Segment)); + DEBUG ((DEBUG_NET, "\tPXENVptr:OFF= 0x%X\n\r", (UINT16) DisplayPointer->PXENVptr.Offset)); +} + +/** + Simple table dumper. The ROMID table is necessary in order to effect + the "Early UNDI" trick. Herein, the UNDI layer can be loaded in the + pre-boot phase without having to download a Network Boot Program + across the wire. It is required in the implementation in that we + are not using PXE. + + @param RomIDStructure Point to RomID structure. + +**/ +VOID +Print_ROMID_Table ( + IN VOID *RomIDStructure + ) +{ + UNDI_ROMID_T *DisplayPointer; + + DisplayPointer = (UNDI_ROMID_T *) RomIDStructure; + + DEBUG ((DEBUG_NET, "Before Parsing the table contents, the table itself lives\n")); + DEBUG ((DEBUG_NET, "\tat the address 0x%X\n\r", (UINT32)(UINTN) RomIDStructure)); + + DEBUG ( + (DEBUG_NET, + "\n\rROMID %c%c%c%c\n\r", + DisplayPointer->Signature[0], + DisplayPointer->Signature[1], + DisplayPointer->Signature[2], + DisplayPointer->Signature[3]) + ); + + DEBUG ( + (DEBUG_NET, + "Length of this structure in bytes = 0x%X\n\r", + DisplayPointer->StructLength) + ); + DEBUG ( + (DEBUG_NET, + "Use to make byte checksum of this structure == zero is = 0x%X\n\r", + DisplayPointer->StructCksum) + ); + DEBUG ( + (DEBUG_NET, + "Structure format revision number= 0x%X\n\r", + DisplayPointer->StructRev) + ); + DEBUG ( + (DEBUG_NET, + "API Revision number = 0x%X 0x%X 0x%X\n\r", + DisplayPointer->UNDI_Rev[0], + DisplayPointer->UNDI_Rev[1], + DisplayPointer->UNDI_Rev[2]) + ); + DEBUG ( + (DEBUG_NET, + "Offset of UNDI loader routine in the option ROM image= 0x%X\n\r", + DisplayPointer->UNDI_Loader) + ); + DEBUG ((DEBUG_NET, "From the data above, the absolute entry point of the UNDI loader is\n\r")); + DEBUG ( + (DEBUG_NET, + "\tat address 0x%X\n\r", + (UINT32) (DisplayPointer->UNDI_Loader + ((UINT32) (UINTN)(DisplayPointer - 0x20) & 0xFFFF0))) + ); + DEBUG ((DEBUG_NET, "Minimum stack segment size, in bytes,\n\r")); + DEBUG ( + (DEBUG_NET, + "needed to load and run the UNDI= 0x%X \n\r", + DisplayPointer->StackSize) + ); + DEBUG ( + (DEBUG_NET, + "UNDI runtime code and data = 0x%X\n\r", + DisplayPointer->DataSize) + ); + DEBUG ( + (DEBUG_NET, + "Segment size = 0x%X\n\r", + DisplayPointer->CodeSize) + ); + DEBUG ( + (DEBUG_NET, + "\n\rBus Type = %c%c%c%c\n\r", + DisplayPointer->BusType[0], + DisplayPointer->BusType[1], + DisplayPointer->BusType[2], + DisplayPointer->BusType[3]) + ); +} + +/** + Print PXE table. + + @param PxeTable Point to PXE table structure + +**/ +VOID +Print_PXE_Table ( + IN VOID* PxeTable + ) +{ + PXE_T *DisplayPointer; + UINTN Index; + UINT8 *Dptr; + + DisplayPointer = (PXE_T *) PxeTable; + Dptr = (UINT8 *) PxeTable; + + DEBUG ((DEBUG_NET, "This is the PXE table at address 0x%X\n\r", PxeTable)); + + DEBUG ((DEBUG_NET, "A dump of the 0x%X bytes is:\n\r", sizeof (PXE_T))); + + for (Index = 0; Index < sizeof (PXE_T); Index++) { + if ((Index % 0x10) == 0) { + DEBUG ((DEBUG_NET, "\t\n\r")); + } + + DEBUG ((DEBUG_NET, " 0x%X ", *Dptr++)); + } + + DEBUG ((DEBUG_NET, "\n\r")); + DEBUG ( + (DEBUG_NET, + "\n\rPXE %c%c%c%c%c%c\n\r", + DisplayPointer->Signature[0], + DisplayPointer->Signature[1], + DisplayPointer->Signature[2], + DisplayPointer->Signature[3]) + ); + DEBUG ( + (DEBUG_NET, + "Length of this structure in bytes = 0x%X\n\r", + DisplayPointer->StructLength) + ); + DEBUG ( + (DEBUG_NET, + "Use to make byte checksum of this structure == zero is = 0x%X\n\r", + DisplayPointer->StructCksum) + ); + DEBUG ( + (DEBUG_NET, + "Structure format revision number = 0x%X\n\r", + DisplayPointer->StructRev) + ); + DEBUG ( + (DEBUG_NET, + "Must be zero, is equal to 0x%X\n\r", + DisplayPointer->Reserved1) + ); + DEBUG ( + (DEBUG_NET, + "Far pointer to UNDI ROMID = 0x%X\n\r", + (UINT32) (DisplayPointer->Undi.Segment << 0x4 | DisplayPointer->Undi.Offset)) + ); + DEBUG ( + (DEBUG_NET, + "Far pointer to base-code ROMID = 0x%X\n\r", + (UINT32) ((DisplayPointer->Base.Segment << 0x04) | DisplayPointer->Base.Offset)) + ); + DEBUG ((DEBUG_NET, "16bit stack segment API entry point. This will be seg:off in \n\r")); + DEBUG ( + (DEBUG_NET, + "real mode and sel:off in 16:16 protected mode = 0x%X:0x%X\n\r", + DisplayPointer->EntryPointSP.Segment, + DisplayPointer->EntryPointSP.Offset) + ); + + DEBUG ((DEBUG_NET, "\n\tNOTE to the implementer\n\tThis is the entry to use for call-ins\n\r")); + + DEBUG ((DEBUG_NET, "32bit stack Segment API entry point. This will be sel:off. \n\r")); + DEBUG ( + (DEBUG_NET, + "In real mode, sel == 0 = 0x%X:0x%X\n\r", + DisplayPointer->EntryPointESP.Segment, + DisplayPointer->EntryPointESP.Offset) + ); + DEBUG ( + (DEBUG_NET, + "Reserved2 value, must be zero, is equal to 0x%X\n\r", + DisplayPointer->Reserved2) + ); + DEBUG ( + (DEBUG_NET, + "Number of segment descriptors in this structur = 0x%X\n\r", + (UINT8) DisplayPointer->SegDescCnt) + ); + DEBUG ( + (DEBUG_NET, + "First segment descriptor in GDT assigned to PXE = 0x%X\n\r", + (UINT16) DisplayPointer->FirstSelector) + ); + DEBUG ( + (DEBUG_NET, + "The Stack is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r", + (UINT16) DisplayPointer->Stack.Seg_Addr, + (UINT32) DisplayPointer->Stack.Phy_Addr, + (UINT16) DisplayPointer->Stack.Seg_Size) + ); + DEBUG ( + (DEBUG_NET, + "The UNDIData is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r", + (UINT16) DisplayPointer->UNDIData.Seg_Addr, + (UINT32) DisplayPointer->UNDIData.Phy_Addr, + (UINT16) DisplayPointer->UNDIData.Seg_Size) + ); + DEBUG ( + (DEBUG_NET, + "The UNDICodeWrite is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r", + (UINT16) DisplayPointer->UNDICode.Seg_Addr, + (UINT32) DisplayPointer->UNDICode.Phy_Addr, + (UINT16) DisplayPointer->UNDICode.Seg_Size) + ); + DEBUG ( + (DEBUG_NET, + "The Stack is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r", + (UINT16) DisplayPointer->UNDICodeWrite.Seg_Addr, + (UINT32) DisplayPointer->UNDICodeWrite.Phy_Addr, + (UINT16) DisplayPointer->UNDICodeWrite.Seg_Size) + ); + DEBUG ( + (DEBUG_NET, + "The BC_Data is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r", + (UINT16) DisplayPointer->BC_Data.Seg_Addr, + (UINT32) DisplayPointer->BC_Data.Phy_Addr, + (UINT16) DisplayPointer->BC_Data.Seg_Size) + ); + DEBUG ( + (DEBUG_NET, + "The BC_Code is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r", + (UINT16) DisplayPointer->BC_Code.Seg_Addr, + (UINT32) DisplayPointer->BC_Code.Phy_Addr, + (UINT16) DisplayPointer->BC_Code.Seg_Size) + ); + DEBUG ( + (DEBUG_NET, + "The BC_CodeWrite is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r", + (UINT16) DisplayPointer->BC_CodeWrite.Seg_Addr, + (UINT32) DisplayPointer->BC_CodeWrite.Phy_Addr, + (UINT16) DisplayPointer->BC_CodeWrite.Seg_Size) + ); +} + +/** + Print PXENV table. + + @param PxenvTable Point to PXENV + +**/ +VOID +Print_PXENV_Table ( + IN VOID *PxenvTable + ) +{ + PXENV_T *DisplayPointer; + + DisplayPointer = (PXENV_T *) PxenvTable; + + DEBUG ( + (DEBUG_NET, + "\n\rPXENV+ %c%c%c%c%c%c\n\r", + DisplayPointer->Signature[0], + DisplayPointer->Signature[1], + DisplayPointer->Signature[2], + DisplayPointer->Signature[3], + DisplayPointer->Signature[4], + DisplayPointer->Signature[5]) + ); + + DEBUG ( + (DEBUG_NET, + "PXE version number. \n\r\tLSB is minor version. \n\r\tMSB is major version = 0x%X\n\r", + DisplayPointer->Version) + ); + DEBUG ( + (DEBUG_NET, + "Length of PXE-2.0 Entry Point structure in bytes = 0x%X\n\r", + DisplayPointer->StructLength) + ); + DEBUG ((DEBUG_NET, "Used to make structure checksum equal zero is now = 0x%X\n\r", DisplayPointer->StructCksum)); + DEBUG ((DEBUG_NET, "Real mode API entry point segment:Offset. = 0x%X\n\r", DisplayPointer->RMEntry)); + DEBUG ((DEBUG_NET, "Protected mode API entry point = 0x%X\n\r", DisplayPointer->PMEntryOff)); + DEBUG ((DEBUG_NET, " segment:Offset. This will always be zero. \n\r")); + DEBUG ((DEBUG_NET, "Protected mode API calls = 0x%X\n\r", DisplayPointer->PMEntrySeg)); + DEBUG ((DEBUG_NET, "Real mode stack segment = 0x%X\n\r", DisplayPointer->StackSeg)); + DEBUG ((DEBUG_NET, "Stack segment size in bytes = 0x%X\n\r", DisplayPointer->StackSize)); + DEBUG ((DEBUG_NET, "Real mode base-code code segment = 0x%X\n\r", DisplayPointer->BaseCodeSeg)); + DEBUG ((DEBUG_NET, "Base-code code segment size = 0x%X\n\r", DisplayPointer->BaseCodeSize)); + DEBUG ((DEBUG_NET, "Real mode base-code data segment = 0x%X\n\r", DisplayPointer->BaseDataSeg)); + DEBUG ((DEBUG_NET, "Base-code data segment size = 0x%X\n\r", DisplayPointer->BaseDataSize)); + + DEBUG ( + (DEBUG_NET, + "UNDI code segment size in bytes = 0x%X\n\r", + DisplayPointer->UNDICodeSize) + ); + DEBUG ( + (DEBUG_NET, + "Real mode segment:Offset pointer \n\r\tto PXE Runtime ID structure, address = 0x%X\n\r", + DisplayPointer->RuntimePtr) + ); + DEBUG ( + ( + DEBUG_NET, + "From above, we have a linear address of 0x%X\n\r", + (UINT32) + ( + ((UINT32)(UINTN)(DisplayPointer->RuntimePtr) & 0xFFFF) + + (((UINT32)(UINTN)(DisplayPointer->RuntimePtr) & 0xFFFF0000) >> 12) + ) + ) + ); +} + + +#define OPTION_ROM_PTR ((OPTION_ROM_HEADER *) RomAddress) + +/** + If available, launch the BaseCode from a NIC option ROM. + This should install the !PXE and PXENV+ structures in memory for + subsequent use. + + + @param SimpleNetworkDevice Simple network device instance + @param RomAddress The ROM base address for NIC rom. + + @retval EFI_NOT_FOUND The check sum does not match + @retval EFI_NOT_FOUND Rom ID offset is wrong + @retval EFI_NOT_FOUND No Rom ID structure is found +**/ +EFI_STATUS +LaunchBaseCode ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + UINTN RomAddress + ) +{ + EFI_STATUS Status; + EFI_IA32_REGISTER_SET InOutRegs; + UNDI_ROMID_T *RomIdTableAddress; + UNDI_LOADER_T *UndiLoaderTable; + UINT16 Segment; + UINT16 *StackPointer; + VOID *Buffer; + UINTN Size; + PXE_T *Pxe; + UINT32 RomLength; + UINTN PciSegment; + UINTN Bus; + UINTN Device; + UINTN Function; + BOOLEAN ThunkFailed; + + DEBUG ((DEBUG_NET, "\n\r\n\rCheck for the UNDI ROMID Signature\n\r")); + + // + // paranoia - check structures for validity + // + RomLength = OPTION_ROM_PTR->ROMlength << 9; + if (CalculateSum8 ((UINT8 *) RomAddress, RomLength) != 0) { + DEBUG ((DEBUG_ERROR, "ROM Header Checksum Error\n\r")); + return EFI_NOT_FOUND; + } + + RomIdTableAddress = (UNDI_ROMID_T *) (RomAddress + OPTION_ROM_PTR->PxeRomIdOffset); + + if ((UINTN) (OPTION_ROM_PTR->PxeRomIdOffset + RomIdTableAddress->StructLength) > RomLength) { + DEBUG ((DEBUG_ERROR, "ROM ID Offset Error\n\r")); + return EFI_NOT_FOUND; + } + // + // see if this is a header for an UNDI ROM ID structure (vs. a $BC$ or BUSD type) + // + if (CompareMem (RomIdTableAddress->Signature, UNDI_ROMID_SIG, sizeof RomIdTableAddress->Signature) != 0) { + DEBUG ((DEBUG_ERROR, "No ROM ID Structure found....\n\r")); + return EFI_NOT_FOUND; + // + // its not - keep looking + // + } + + if (CalculateSum8 ((UINT8 *) RomIdTableAddress, RomIdTableAddress->StructLength) != 0) { + DEBUG ((DEBUG_ERROR, "ROM ID Checksum Error\n\r")); + return EFI_NOT_FOUND; + } + + Print_ROMID_Table (RomIdTableAddress); + + DEBUG ( + (DEBUG_NET, + "The ROM ID is located at 0x%X\n\r", + RomIdTableAddress) + ); + + DEBUG ( + (DEBUG_NET, + "With an UNDI Loader located at 0x%X\n\r", + RomAddress + RomIdTableAddress->UNDI_Loader) + ); + + // + // found an UNDI ROM ID structure + // + SimpleNetworkDevice->Nii.ImageAddr = RomAddress; + SimpleNetworkDevice->Nii.ImageSize = RomLength; + SimpleNetworkDevice->Nii.MajorVer = RomIdTableAddress->UNDI_Rev[2]; + SimpleNetworkDevice->Nii.MinorVer = RomIdTableAddress->UNDI_Rev[1]; + + DEBUG ((DEBUG_NET, "Allocate area for the UNDI_LOADER_T structure\n\r")); + // + // Allocate 1 page below 1MB to put real mode thunk code in + // + // Undi Loader Table is a PXE Specification prescribed data structure + // that is used to transfer information into and out of the Undi layer. + // Note how it must be located below 1 MB. + // + SimpleNetworkDevice->UndiLoaderTablePages = EFI_SIZE_TO_PAGES (PARAGRAPH_SIZE + sizeof (UNDI_LOADER_T)); + Status = BiosSnp16AllocatePagesBelowOneMb ( + SimpleNetworkDevice->UndiLoaderTablePages, + &SimpleNetworkDevice->UndiLoaderTable + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "We had a failure in AllocatePages, status code = 0x%X\n", Status)); + return EFI_OUT_OF_RESOURCES; + } + + UndiLoaderTable = SimpleNetworkDevice->UndiLoaderTable; + + DEBUG ((DEBUG_NET, "Allocate area for the real-mode stack whose sole purpose\n\r")); + DEBUG ((DEBUG_NET, "in life right now is to store a SEG:OFFSET combo pair that\n\r")); + DEBUG ((DEBUG_NET, "points to an Undi_Loader_t table structure\n\r")); + + Size = 0x100; + Status = gBS->AllocatePool (EfiLoaderData, Size, &Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Now we want to put a pointer to the Under Loader Table in our MemPage + // Buffer. This will be the argument stack for the call into the Undi Loader + // + StackPointer = (UINT16 *) Buffer; + *StackPointer++ = TO_OFFSET (UndiLoaderTable); + // + // push the OFFSET + // + *StackPointer++ = TO_SEGMENT (UndiLoaderTable); + // + // push the SEGMENT + // + StackPointer = (UINT16 *) Buffer; + // + // reset the stack pointer + // + DEBUG ( + (DEBUG_NET, + "After the fixups, the stack pointer is 0x%X\n\r", + (UINT64)(UINTN) StackPointer) + ); + + // + // Allocate memory for the Deployed UNDI. + // The UNDI is essentially telling us how much space it needs, and + // it is up to the EFI driver to allocate sufficient, boot-time + // persistent resources for the call + // + SimpleNetworkDevice->DestinationDataSegmentPages = EFI_SIZE_TO_PAGES (RomIdTableAddress->DataSize); + Status = BiosSnp16AllocatePagesBelowOneMb ( + SimpleNetworkDevice->DestinationDataSegmentPages, + &SimpleNetworkDevice->DestinationDataSegment + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "We had a failure in AllocatePages, status code = 0x%X\n", Status)); + return Status; + } + + UndiLoaderTable->Undi_Ds = (UINT16) ((UINTN) SimpleNetworkDevice->DestinationDataSegment >> 4); + + // + // Allocate memory for the Deployed UNDI stack + // The UNDI is essentially telling us how much space it needs, and + // it is up to the EFI driver to allocate sufficient, boot-time + // persistent resources for the call + // + SimpleNetworkDevice->DestinationStackSegmentPages = EFI_SIZE_TO_PAGES (RomIdTableAddress->StackSize); + Status = BiosSnp16AllocatePagesBelowOneMb ( + SimpleNetworkDevice->DestinationStackSegmentPages, + &SimpleNetworkDevice->DestinationStackSegment + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "We had a failure in AllocatePages, status code = 0x%X\n", Status)); + return Status; + } + // + // Allocate memory for the Deployed UNDI. + // The UNDI is essentially telling us how much space it needs, and + // it is up to the EFI driver to allocate sufficient, boot-time + // persistent resources for the call + // + SimpleNetworkDevice->DestinationCodeSegmentPages = EFI_SIZE_TO_PAGES (RomIdTableAddress->CodeSize); + Status = BiosSnp16AllocatePagesBelowOneMb ( + SimpleNetworkDevice->DestinationCodeSegmentPages, + &SimpleNetworkDevice->DestinationCodeSegment + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "We had a failure in AllocatePages, status code = 0x%X\n", Status)); + return Status; + } + + UndiLoaderTable->Undi_Cs = (UINT16) ((UINTN) SimpleNetworkDevice->DestinationCodeSegment >> 4); + + // + // these are in the Input and Output Parameter to be sent to the UNDI Loader code + // + UndiLoaderTable->Status = 0xAA55; + // + // -------------------- Changed by Michael_Huang@3Com.com ----------------- + // UndiLoaderTable->_AX is AX value when UNDI ROM is initialized by BIOS, it is the PCI bus device + // function of the NIC. Please refer to PXE Spec for detail info. + // old code is: + // UndiLoaderTable->Ax = 0x0; + // ----------------------------------------------------------------------- + // + SimpleNetworkDevice->PciIo->GetLocation ( + SimpleNetworkDevice->PciIo, + &PciSegment, + &Bus, + &Device, + &Function + ); + UndiLoaderTable->Ax = (UINT16) ((Bus << 0x8) | (Device << 0x3) | (Function)); + UndiLoaderTable->Bx = 0x0; + UndiLoaderTable->Dx = 0x0; + UndiLoaderTable->Di = 0x0; + UndiLoaderTable->Es = 0x0; + + // + // set these OUT values to zero in order to ensure that + // uninitialized memory is not mistaken for display data + // + UndiLoaderTable->PXEptr.Offset = 0; + UndiLoaderTable->PXEptr.Segment = 0; + UndiLoaderTable->PXENVptr.Segment = 0; + UndiLoaderTable->PXENVptr.Offset = 0; + + DEBUG ( + (DEBUG_INIT, + "The NIC is located at Bus 0x%X, Device 0x%X, Function 0x%X\n\r", + Bus, + Device, + Function) + ); + + // + // These are the values that set up the ACTUAL IA32 machine state, whether in + // Real16 in EFI32 or the IVE for IA64 + // register values are unused except for CS:IP and SS:SP + // + InOutRegs.X.AX = 0; + InOutRegs.X.BX = 0; + InOutRegs.X.CX = 0; + InOutRegs.X.DX = 0; + InOutRegs.X.SI = 0; + InOutRegs.X.DI = 0; + InOutRegs.X.BP = 0; + InOutRegs.X.DS = 0; + InOutRegs.X.ES = 0; + // + // just to be clean + // + DEBUG ((DEBUG_NET, "The way this game works is that the SS:SP +4 should point\n\r")); + DEBUG ((DEBUG_NET, "to the contents of the UndiLoaderTable\n\r")); + DEBUG ( + (DEBUG_NET, + "The Undi Loader Table is at address = 0x%X\n\r", + (UINT32)(UINTN) UndiLoaderTable) + ); + DEBUG ( + (DEBUG_NET, + "The segment and offsets are 0x%X and 0x%X, resp\n", + TO_SEGMENT (UndiLoaderTable), + TO_OFFSET (UndiLoaderTable)) + ); + + DEBUG ( + (DEBUG_NET, + "The Linear Address of the UNDI Loader entry is 0x%X\n", + RomAddress + RomIdTableAddress->UNDI_Loader) + ); + + DEBUG ( + (DEBUG_NET, + "The Address offset of the UNDI Loader entry is 0x%X\n", + RomIdTableAddress->UNDI_Loader) + ); + + DEBUG ((DEBUG_NET, "Before the call, we have...\n\r")); + Print_Undi_Loader_Table (UndiLoaderTable); + + Segment = ((UINT16) (RShiftU64 (RomAddress, 4) & 0xFFFF)); + DEBUG ((DEBUG_NET, "The Segment of the call is 0x%X\n\r", Segment)); + + // + // make the call into the UNDI Code + // + DEBUG ((DEBUG_INIT, "Make the call into the UNDI code now\n\r")); + + DEBUG ((DEBUG_NET, "\nThe 20-BIt address of the Call, and the location \n\r")); + DEBUG ((DEBUG_NET, "\twhere we should be able to set a breakpoint is \n\r")); + DEBUG ( + (DEBUG_NET, + "\t\t0x%X, from SEG:OFF 0x%X:0x%X\n\r\n\r", + Segment * 0x10 + RomIdTableAddress->UNDI_Loader, + Segment, + RomIdTableAddress->UNDI_Loader) + ); + + ThunkFailed = SimpleNetworkDevice->LegacyBios->FarCall86 ( + SimpleNetworkDevice->LegacyBios, + Segment, // Input segment + (UINT16) RomIdTableAddress->UNDI_Loader, // Offset + &InOutRegs, // Ptr to Regs + Buffer, // Reference to Stack + Size // Size of the Stack + ); + if (ThunkFailed) { + return EFI_ABORTED; + } + + DEBUG ( + (DEBUG_NET, + "The return code UndiLoaderTable->Status is = 0x%X\n\r", + UndiLoaderTable->Status) + ); + DEBUG ( + (DEBUG_NET, + "This error code should match eax, which is = 0x%X\n\r", + InOutRegs.X.AX) + ); + + DEBUG ((DEBUG_NET, "Now returned from the UNDI code\n\r")); + + DEBUG ((DEBUG_NET, "After the call, we have...\n\r")); + Print_Undi_Loader_Table (UndiLoaderTable); + + DEBUG ((DEBUG_NET, "Display the PXENV+ and !PXE tables exported by NIC\n\r")); + Print_PXENV_Table ((VOID *)(UINTN)((UndiLoaderTable->PXENVptr.Segment << 4) | UndiLoaderTable->PXENVptr.Offset)); + Print_PXE_Table ((VOID *)(UINTN)((UndiLoaderTable->PXEptr.Segment << 4) + UndiLoaderTable->PXEptr.Offset)); + + Pxe = (PXE_T *)(UINTN)((UndiLoaderTable->PXEptr.Segment << 4) + UndiLoaderTable->PXEptr.Offset); + SimpleNetworkDevice->Nii.Id = (UINT64)(UINTN) Pxe; + + // + // FreePool (Buffer); + // paranoia - make sure a valid !PXE structure + // + if (CompareMem (Pxe->Signature, PXE_SIG, sizeof Pxe->Signature) != 0) { + DEBUG ((DEBUG_ERROR, "!PXE Structure not found....\n\r")); + return EFI_NOT_FOUND; + // + // its not - keep looking + // + } + + if (CalculateSum8 ((UINT8 *) Pxe, Pxe->StructLength) != 0) { + DEBUG ((DEBUG_ERROR, "!PXE Checksum Error\n\r")); + return EFI_NOT_FOUND; + } + + if (Pxe->StructLength < (UINT8 *) &Pxe->FirstSelector - (UINT8 *) Pxe->Signature) { + DEBUG ((DEBUG_ERROR, "!PXE Length Error\n\r")); + return EFI_NOT_FOUND; + } + + if ((((UINTN) Pxe->Undi.Segment) << 4) + Pxe->Undi.Offset != (UINTN) RomIdTableAddress) { + DEBUG ((DEBUG_ERROR, "!PXE RomId Address Error\n\r")); + return EFI_NOT_FOUND; + } + // + // This is the magic to bind the global PXE interface + // This dirtiness is for non-protocol shrouded access + // + SimpleNetworkDevice->PxeEntrySegment = Pxe->EntryPointSP.Segment; + + if (SimpleNetworkDevice->PxeEntrySegment == 0) { + DEBUG ((DEBUG_ERROR, "!PXE EntryPointSP segment Error\n\r")); + return EFI_NOT_FOUND; + } + + SimpleNetworkDevice->PxeEntryOffset = Pxe->EntryPointSP.Offset; + + DEBUG ( + ( + DEBUG_NET, "The entry point is 0x%X:0x%X\n\r", SimpleNetworkDevice->PxeEntrySegment, SimpleNetworkDevice-> + PxeEntryOffset + ) + ); + + return EFI_SUCCESS; +} + +/** + Effect the Far Call into the PXE Layer + + Note: When using a 32-bit stack segment do not push 32-bit words onto the stack. The PXE API + services will not work, unless there are three 16-bit parameters pushed onto the stack. + push DS ;Far pointer to parameter structure + push offset pxe_data_call_struct ;is pushed onto stack. + push Index ;UINT16 is pushed onto stack. + call dword ptr (s_PXE ptr es:[di]).EntryPointSP + add sp, 6 ;Caller cleans up stack. + + @param SimpleNetworkDevice Device instance for simple network + @param Table Point to parameter/retun value table for legacy far call + @param TableSize The size of paramter/return value table + @param CallIndex The index of legacy call. + + @return EFI_STATUS +**/ +EFI_STATUS +MakePxeCall ( + EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT VOID *Table, + IN UINTN TableSize, + IN UINT16 CallIndex + ) +{ + EFI_STATUS Status; + EFI_IA32_REGISTER_SET InOutRegs; + UINT16 *BPtr; + VOID *Buffer; + UINTN Size; + VOID *MemPageAddress; + UINTN Index; + BOOLEAN ThunkFailed; + + DEBUG ((DEBUG_NET, "MakePxeCall(CallIndex = %02x, Table = %X, TableSize = %d)\n", CallIndex, Table, TableSize)); + + if (SimpleNetworkDevice->PxeEntrySegment == 0 && SimpleNetworkDevice->PxeEntryOffset == 0) { + return EFI_DEVICE_ERROR; + } + + Status = EFI_SUCCESS; + + // + // Allocate a transient data structure for the argument table + // This table needs to have the input XXX_t structure copied into here. + // The PXE UNDI can only grab this table when it's below one-MB, and + // this implementation will not try to push this table on the stack + // (although this is a possible optimization path since EFI always allocates + // 4K as a minimum page size...............) + // + Status = BiosSnp16AllocatePagesBelowOneMb ( + TableSize / EFI_PAGE_SIZE + 1, + &MemPageAddress + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "We had a failure in AllocatePages, status code = 0x%X\n", Status)); + return Status; + } + // + // Copy the > 1MB pool table to a sub-1MB buffer + // + CopyMem (MemPageAddress, Table, TableSize); + + // + // Allocate space for IA-32 register context + // + ZeroMem (&InOutRegs, sizeof (InOutRegs)); + InOutRegs.X.ES = SimpleNetworkDevice->PxeEntrySegment; + InOutRegs.X.DI = SimpleNetworkDevice->PxeEntryOffset; + + // + // The game here is to build the stack which will subsequently + // get copied down below 1 MB by the FarCall primitive. + // This is now our working stack + // + Size = 6; + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + Size, + &Buffer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + BPtr = (UINT16 *) Buffer; + *BPtr++ = CallIndex; + // + // SP + 2 + // + *BPtr++ = TO_OFFSET (MemPageAddress); + *BPtr++ = TO_SEGMENT (MemPageAddress); + + DEBUG ((DEBUG_NET, "State before FarCall86\n")); + DEBUG ((DEBUG_NET, "The Buffer is at 0x%X\n\r", Buffer)); + BPtr = (UINT16 *) Buffer; + DEBUG ((DEBUG_NET, " Buffer = %04X %04X %04X", *BPtr, *(BPtr + 1), *(BPtr + 2))); + DEBUG ((DEBUG_NET, " MemPage = ")); + for (Index = 0; Index < TableSize; Index++) { + DEBUG ((DEBUG_NET, " %02x", *((UINT8 *) MemPageAddress + Index))); + } + + DEBUG ((DEBUG_NET, "\n")); + + ThunkFailed = SimpleNetworkDevice->LegacyBios->FarCall86 ( + SimpleNetworkDevice->LegacyBios, + SimpleNetworkDevice->PxeEntrySegment, // Input segment + SimpleNetworkDevice->PxeEntryOffset, + &InOutRegs, // Ptr to Regs + Buffer, // Reference to Stack + 6 // Size of the Stack + ); + if (ThunkFailed) { + return EFI_ABORTED; + } + + DEBUG ((DEBUG_NET, "State after FarCall86\n")); + DEBUG ((DEBUG_NET, "The Buffer is at 0x%X\n\r", Buffer)); + BPtr = (UINT16 *) Buffer; + DEBUG ((DEBUG_NET, " Buffer = %04X %04X %04X", *BPtr, *(BPtr + 1), *(BPtr + 2))); + DEBUG ((DEBUG_NET, " MemPage = ")); + for (Index = 0; Index < TableSize; Index++) { + DEBUG ((DEBUG_NET, " %02x", *((UINT8 *) MemPageAddress + Index))); + } + + DEBUG ((DEBUG_NET, "\n")); + + // + // Copy the sub 1MB table to > 1MB table + // + CopyMem (Table, MemPageAddress, TableSize); + + // + // For PXE UNDI call, AX contains the return status. + // Convert the PXE UNDI Status to EFI_STATUS type + // + if (InOutRegs.X.AX == PXENV_EXIT_SUCCESS) { + Status = EFI_SUCCESS; + } else { + Status = EFI_DEVICE_ERROR; + } + // + // Clean up house + // + gBS->FreePool (Buffer); + gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) MemPageAddress, TableSize / EFI_PAGE_SIZE + 1); + + return Status; +} diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Pxe.h b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Pxe.h new file mode 100644 index 0000000000..54503a840f --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Pxe.h @@ -0,0 +1,613 @@ +/** @file + These are PXE Specification 2.1-compliant data structures and defines. + + This file relies upon the existence of a PXE-compliant ROM + in memory, as defined by the Preboot Execution Environment + Specification (PXE), Version 2.1, located at + + http://developer.intel.com/ial/wfm/wfmspecs.htm + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _PXEDEF_H_ +#define _PXEDEF_H_ + +#pragma pack(1) + +// +// PXE structure signatures +// +#define BC_ROMID_SIG "$BC$" +#define UNDI_ROMID_SIG "UNDI" +#define BUSD_ROMID_SIG "BUSD" + +#define PXE_SIG "!PXE" +#define PXENV_SIG "PXENV+" + +#define BC_ROMID_REV 0x00 +#define UNDI_ROMID_REV 0x00 +#define BUSD_ROMID_REV 0x00 + +#define PXE_REV 0x00 +#define PXENV_REV 0x0201 + +#define PXENV_PTR SIGNATURE_32 ('P', 'X', 'E', 'N') +#define PXE_PTR SIGNATURE_32 ('!', 'P', 'X', 'E') +#define UNDI_ROMID_SIG_PTR SIGNATURE_32 ('U', 'N', 'D', 'I') + +typedef UINT16 SEGSEL; // Real mode segment or protected mode selector. +typedef UINT16 OFF16; // Unsigned 16bit offset. +typedef UINT32 ADDR32; + +// +// Bus types +// +#define PXENV_BUS_ISA 0 +#define PXENV_BUS_EISA 1 +#define PXENV_BUS_MCA 2 +#define PXENV_BUS_PCI 3 +#define PXENV_BUS_VESA 4 +#define PXENV_BUS_PCMCIA 5 + +// +// +// Result codes returned in AX by a PXENV API service. +// +#define PXENV_EXIT_SUCCESS 0x0000 +#define PXENV_EXIT_FAILURE 0x0001 + +// +// Status codes returned in the status word of PXENV API parameter structures. +// +// Generic API errors - these do not match up with the M0x or E0x messages +// that are reported by the loader. +// +#define PXENV_STATUS_SUCCESS 0x00 +#define PXENV_STATUS_FAILURE 0x01 +#define PXENV_STATUS_BAD_FUNC 0x02 +#define PXENV_STATUS_UNSUPPORTED 0x03 +#define PXENV_STATUS_KEEP_UNDI 0x04 +#define PXENV_STATUS_KEEP_ALL 0x05 +#define PXENV_STATUS_OUT_OF_RESOURCES 0x06 + +typedef enum { + PxeEnvStatus_Success, + PxeEnvStatus_Failure, + PxeEnvStatus_BadFunc, + PxeEnvStatus_Unsupported, + PxeEnvStatus_KeepUndi, + PxeEnvStatus_KeepAll +} EFI_PXE_STATUS; + +/* Driver errors (0x60 to 0x6F) */ + +// These errors are for UNDI compatible NIC drivers. +#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x60 +#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x61 +#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x62 +#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x63 +#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x64 +#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x65 +#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x66 +#define PXENV_STATUS_UNDI_BAD_MAC_ADDR 0x67 +#define PXENV_STATUS_UNDI_BAD_EEPROM_CKSUM 0x68 +#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x69 +#define PXENV_STATUS_UNDI_INVALID_STATE 0x6A +#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x6B +#define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x6C + +typedef struct { + UINT16 Seg_Addr; + UINT32 Phy_Addr; + UINT16 Seg_Size; +} NEWSEGDESC_T; + +typedef struct { + OFF16 Offset; + SEGSEL Segment; +} SEGOFF16; + +typedef struct { + UINT8 Signature[4]; ///< Structure signature is not NULL terminated. + UINT8 StructLength; ///< Length of this structure in bytes. + UINT8 StructCksum; ///< Use to make byte checksum of this structure == zero. + UINT8 StructRev; ///< Structure format revision number. + UINT8 UNDI_Rev[3]; ///< API revision number stored in Intel order. + // + // Revision 2.1.0 == 0x00, 0x01, 0x02 + // + UINT16 UNDI_Loader; ///< Offset of UNDI loader routine in the option ROM image. + UINT16 StackSize; ///< Minimum stack segment size, in bytes, needed to load and run the UNDI. + UINT16 DataSize; ///< UNDI runtime code and data + UINT16 CodeSize; ///< segment sizes. + UINT8 BusType[4]; ///< 'ISAR', 'EISA', 'PCIR', 'PCCR' +} UNDI_ROMID_T; + +typedef struct { + UINT8 Signature[4]; ///< Structure signature is not NULL terminated. + UINT8 StructLength; ///< Length of this structure in bytes. + UINT8 StructCksum; ///< Use to make byte checksum of this structure == zero. + UINT8 StructRev; ///< Structure format revision number. + UINT8 BC_Rev[3]; ///< API revision number stored in Intel order. + // + // Revision 2.1.0 == 0x00, 0x01, 0x02 + // + UINT16 BC_Loader; ///< Offset of base-code loader routine in the option ROM image. + UINT16 StackSize; ///< Minimum stack segment size (bytes) needed to load/run base-code. + UINT16 DataSize; ///< Base-code runtime code and data + UINT16 CodeSize; ///< segment sizes. +} BC_ROMID_T; + +typedef struct { + UINT8 Signature[4]; ///< Structure signature is not NULL terminated. + UINT8 StructLength; ///< Length of this structure in bytes. + UINT8 StructCksum; ///< Use to make byte checksum of this structure == zero. + UINT8 StructRev; ///< Structure format revision number. + UINT8 Reserved1; ///< must be zero + /// + /// UNDI_ROMID_T __FAR *UNDI;// Far pointer to UNDI ROMID + /// + SEGOFF16 Undi; + + /// + /// BC_ROMID_T __FAR *Base; // Far pointer to base-code ROMID + /// + SEGOFF16 Base; + + /// + /// UINT16 (__FAR __CDECL *EntryPointSP)(UINT16 func, VOID __FAR *param); + /// 16bit stack segment API entry point. This will be seg:off in + /// real mode and sel:off in 16:16 protected mode. + /// + SEGOFF16 EntryPointSP; + + /// + /// UINT16 (__FAR __CDECL *EntryPointESP)(UINT16 func, VOID __FAR *param); + /// 32bit stack segment API entry point. This will be sel:off. + /// In real mode, sel == 0 + /// + SEGOFF16 EntryPointESP; + /// + /// UINT16 (__FAR __CDECL *StatusCallout)(UINT16 param); + /// Address of DHCP/TFTP status callout routine. + /// + SEGOFF16 StatusCallout; + UINT8 Reserved2; ///< must be zero + UINT8 SegDescCnt; ///< Number of segment descriptors in this structure. + UINT16 FirstSelector; ///< First segment descriptor in GDT assigned to PXE. + NEWSEGDESC_T Stack; + NEWSEGDESC_T UNDIData; + NEWSEGDESC_T UNDICode; + NEWSEGDESC_T UNDICodeWrite; + NEWSEGDESC_T BC_Data; + NEWSEGDESC_T BC_Code; + NEWSEGDESC_T BC_CodeWrite; +} PXE_T; + +typedef struct { + CHAR8 Signature[6]; ///< "PXENV+" + UINT16 Version; ///< PXE version number. LSB is minor version. MSB is major version. + UINT8 StructLength; ///< Length of PXE-2.0 Entry Point structure in bytes. + UINT8 StructCksum; ///< Used to make structure checksum equal zero. + UINT32 RMEntry; ///< Real mode API entry point segment:offset. + UINT32 PMEntryOff; ///< Protected mode API entry point + UINT16 PMEntrySeg; ///< segment:offset. This will always be zero. Protected mode API calls + ///< must be made through the API entry points in the PXE Runtime ID structure. + + UINT16 StackSeg; ///< Real mode stack segment. + UINT16 StackSize; ///< Stack segment size in bytes. + UINT16 BaseCodeSeg; ///< Real mode base-code code segment. + UINT16 BaseCodeSize; ///< Base-code code segment size + UINT16 BaseDataSeg; ///< Real mode base-code data segment. + UINT16 BaseDataSize; ///< Base-code data segment size + UINT16 UNDIDataSeg; ///< Real mode UNDI data segment. + UINT16 UNDIDataSize; ///< UNDI data segment size in bytes. + UINT16 UNDICodeSeg; ///< Real mode UNDI code segment. + UINT16 UNDICodeSize; ///< UNDI code segment size in bytes. + PXE_T *RuntimePtr; ///< Real mode segment:offset pointer to PXE Runtime ID structure. +} PXENV_T; + +typedef struct { + OUT UINT16 Status; + IN OUT UINT16 Ax; + IN OUT UINT16 Bx; + IN OUT UINT16 Dx; + IN OUT UINT16 Di; + IN OUT UINT16 Es; + IN OUT UINT16 Undi_Ds; + IN OUT UINT16 Undi_Cs; + OUT SEGOFF16 PXEptr; + OUT SEGOFF16 PXENVptr; +} UNDI_LOADER_T; + +// +// Put in some UNDI-specific arguments +// +#define PXENV_START_UNDI 0x0000 +#define PXENV_UNDI_STARTUP 0x0001 +#define PXENV_UNDI_CLEANUP 0x0002 +#define PXENV_UNDI_INITIALIZE 0x0003 +#define PXENV_UNDI_RESET_NIC 0x0004 +#define PXENV_UNDI_SHUTDOWN 0x0005 +#define PXENV_UNDI_OPEN 0x0006 +#define PXENV_UNDI_CLOSE 0x0007 +#define PXENV_UNDI_TRANSMIT 0x0008 +#define PXENV_UNDI_SET_MCAST_ADDR 0x0009 +#define PXENV_UNDI_SET_STATION_ADDR 0x000A +#define PXENV_UNDI_SET_PACKET_FILTER 0x000B +#define PXENV_UNDI_GET_INFORMATION 0x000C +#define PXENV_UNDI_GET_STATISTICS 0x000D +#define PXENV_UNDI_CLEAR_STATISTICS 0x000E +#define PXENV_UNDI_INITIATE_DIAGS 0x000F +#define PXENV_UNDI_FORCE_INTERRUPT 0x0010 +#define PXENV_UNDI_GET_MCAST_ADDR 0x0011 +#define PXENV_UNDI_GET_NIC_TYPE 0x0012 +#define PXENV_UNDI_GET_NDIS_INFO 0x0013 +#define PXENV_UNDI_ISR 0x0014 +#define PXENV_STOP_UNDI 0x0015 +#define PXENV_UNDI_GET_STATE 0x0016 + +#define ADDR_LEN 16 +#define MAXNUM_MCADDR 8 +#define IPLEN 4 ///< length of an IP address +#define XMT_DESTADDR 0x0000 ///< destination address given +#define XMT_BROADCAST 0x0001 ///< use broadcast address + +typedef struct { + UINT16 MCastAddrCount; ///< In: Number of multi-cast + + /* addresses. */ + UINT8 MCastAddr[MAXNUM_MCADDR][ADDR_LEN]; /* In: */ + + /* list of multi-cast addresses. */ + + /* Each address can take up to */ + + /* ADDR_LEN bytes and a maximum */ + + /* of MAXNUM_MCADDR address can */ + + /* be provided*/ +} PXENV_UNDI_MCAST_ADDR_T; + +/* Definitions of TFTP API parameter structures. + */ +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + IN UINT16 Ax; ///< In: These register fields must be + IN UINT16 Bx; ///< filled in with the same data + IN UINT16 Dx; ///< that was passed to the MLID + IN UINT16 Di; ///< option ROM boot code by the + IN UINT16 Es; ///< system BIOS. +} PXENV_START_UNDI_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx +} PXENV_UNDI_STARTUP_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx +} PXENV_UNDI_CLEANUP_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + + /// + /// This is an input parameter and is a 32-bit physical address of + /// a memory copy of the driver module in the protocol.ini file + /// obtained from the Protocol Manager driver(refer to NDIS 2.0 + /// specifications). This parameter is basically supported for + /// the universal NDIS driver to pass the information contained in + /// protocol.ini file to the NIC driver for any specific + /// configuration of the NIC. (Note that the module + /// identification in the protocol.ini file was done by NDIS + /// itself.) This value can be NULL for for any other application + /// interfacing to the Universal NIC Driver. + /// + IN UINT32 ProtocolIni; + UINT8 Reserved[8]; +} PXENV_UNDI_INITIALIZE_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + IN PXENV_UNDI_MCAST_ADDR_T R_Mcast_Buf; ///< multicast address list + /* see note below */ +} PXENV_UNDI_RESET_T; + +/*++ + Note: The NIC driver does not remember the multicast + addresses provided in any call. So the application must + provide the multicast address list with all the calls that + reset the receive unit of the adapter. + --*/ +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx +} PXENV_UNDI_SHUTDOWN_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + + /// + /// This is an input parameter and is adapter specific. This is + /// supported for Universal NDIS 2.0 driver to pass down the Open + /// flags provided by the protocol driver (See NDIS 2.0 + /// specifications). This can be zero. + /// + IN UINT16 OpenFlag; ///< In: See description below + IN UINT16 PktFilter; ///< In: Filter for receiving + + /* packet. It takes the following */ + + /* values, multiple values can be */ + + /* ORed together. */ +#define FLTR_DIRECTED 0x0001 ///< directed/multicast +#define FLTR_BRDCST 0x0002 ///< broadcast packets +#define FLTR_PRMSCS 0x0004 ///< any packet on LAN +#define FLTR_SRC_RTG 0x0008 ///< source routing packet + IN PXENV_UNDI_MCAST_ADDR_T McastBuffer; /* In: */ + /* See t_PXENV_UNDI_MCAST_ADDR. */ +} PXENV_UNDI_OPEN_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx +} PXENV_UNDI_CLOSE_T; + +#define MAX_DATA_BLKS 8 + +typedef struct { + IN UINT16 ImmedLength; ///< In: Data buffer length in + + /* bytes. */ + UINT16 XmitOffset; ///< 16-bit segment & offset of the + UINT16 XmitSegment; ///< immediate data buffer. + UINT16 DataBlkCount; ///< In: Number of data blocks. + struct DataBlk { + UINT8 TDPtrType; ///< 0 => 32 bit Phys pointer in TDDataPtr, not supported in this version of LSA + ///< 1 => seg:offser in TDDataPtr which can be a real mode or 16-bit protected mode pointer + UINT8 TDRsvdByte; ///< Reserved, must be zero. + UINT16 TDDataLen; ///< Data block length in bytes. + UINT16 TDDataPtrOffset; ///< Far pointer to data buffer. + UINT16 TDDataPtrSegment; ///< Far pointer to data buffer. + } DataBlock[MAX_DATA_BLKS]; +} +PXENV_UNDI_TBD_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + + /// + /// This is the protocol of the upper layer that is calling + /// NICTransmit call. If the upper layer has filled the media + /// header this field must be 0. + /// + IN UINT8 Protocol; +#define P_UNKNOWN 0 +#define P_IP 1 +#define P_ARP 2 +#define P_RARP 3 + + /// + /// If this flag is 0, the NIC driver expects a pointer to the + /// destination media address in the field DestMediaAddr. If 1, + /// the NIC driver fills the broadcast address for the + /// destination. + /// + IN UINT8 XmitFlag; +#define XMT_DESTADDR 0x0000 ///< destination address given +#define XMT_BROADCAST 0x0001 ///< use broadcast address + + /// + /// This is a pointer to the hardware address of the destination + /// media. It can be null if the destination is not known in + /// which case the XmitFlag contains 1 for broadcast. Destination + /// media address must be obtained by the upper level protocol + /// (with Address Resolution Protocol) and NIC driver does not do + /// any address resolution. + /// + IN UINT16 DestAddrOffset; ///< 16-bit segment & offset of the + IN UINT16 DestAddrSegment; ///< destination media address + + + IN UINT16 TBDOffset; ///< 16-bit segment & offset of the + IN UINT16 TBDSegment; ///< transmit buffer descriptor of type + + /// XmitBufferDesc + IN UINT32 Reserved[2]; +} PXENV_UNDI_TRANSMIT_T; + + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + IN PXENV_UNDI_MCAST_ADDR_T McastBuffer; ///< In: +} PXENV_UNDI_SET_MCAST_ADDR_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + IN UINT8 StationAddress[ADDR_LEN]; ///< new address to be set +} PXENV_UNDI_SET_STATION_ADDR_T; + +typedef struct s_PXENV_UNDI_SET_PACKET_FILTER { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + IN UINT8 Filter; ///< In: Receive filter value. +} PXENV_UNDI_SET_PACKET_FILTER_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + OUT UINT16 BaseIo; ///< Out: Adapter's Base IO + OUT UINT16 IntNumber; ///< Out: IRQ number + OUT UINT16 MaxTranUnit; ///< Out: MTU + OUT UINT16 HwType; ///< Out: type of protocol at hardware level + +#define ETHER_TYPE 1 +#define EXP_ETHER_TYPE 2 +#define IEEE_TYPE 6 +#define ARCNET_TYPE 7 + /*++ + other numbers can be obtained from rfc1010 for "Assigned + Numbers". This number may not be validated by the application + and hence adding new numbers to the list should be fine at any + time. + --*/ + OUT UINT16 HwAddrLen; ///< Out: actual length of hardware address + OUT UINT8 CurrentNodeAddress[ADDR_LEN]; ///< Out: Current hardware address + OUT UINT8 PermNodeAddress[ADDR_LEN]; ///< Out: Permanent hardware address + OUT UINT16 ROMAddress; ///< Out: ROM address + OUT UINT16 RxBufCt; ///< Out: receive Queue length + OUT UINT16 TxBufCt; ///< Out: Transmit Queue length +} PXENV_UNDI_GET_INFORMATION_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + OUT UINT32 XmtGoodFrames; ///< Out: No. of good transmissions + OUT UINT32 RcvGoodFrames; ///< Out: No. of good frames received + OUT UINT32 RcvCRCErrors; ///< Out: No. of frames with CRC error + OUT UINT32 RcvResourceErrors; ///< Out: no. of frames discarded + /* Out: receive Queue full */ +} PXENV_UNDI_GET_STATISTICS_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx +} PXENV_UNDI_CLEAR_STATISTICS_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx +} PXENV_UNDI_INITIATE_DIAGS_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx +} PXENV_UNDI_FORCE_INTERRUPT_T; + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + IN UINT32 InetAddr; ///< In: IP Multicast Address + OUT UINT8 MediaAddr[ADDR_LEN]; ///< Out: corresponding hardware + /* multicast address */ +} PXENV_UNDI_GET_MCAST_ADDR_T; + +typedef struct { + OUT UINT16 Vendor_ID; ///< OUT: + OUT UINT16 Dev_ID; ///< OUT: + OUT UINT8 Base_Class; ///< OUT: + OUT UINT8 Sub_Class; ///< OUT: + OUT UINT8 Prog_Intf; ///< OUT: program interface + OUT UINT8 Rev; ///< OUT: Revision number + OUT UINT16 BusDevFunc; ///< OUT: Bus, Device & Function numbers + OUT UINT16 SubVendor_ID; ///< OUT: + OUT UINT16 SubDevice_ID; ///< OUT: +} PCI_INFO_T; + +typedef struct { + OUT UINT32 EISA_Dev_ID; ///< Out: + OUT UINT8 Base_Class; ///< OUT: + OUT UINT8 Sub_Class; ///< OUT: + OUT UINT8 Prog_Intf; ///< OUT: program interface + OUT UINT16 CardSelNum; ///< OUT: Card Selector Number + OUT UINT8 Reserved; ///< to make it 10 bytes +} PNP_INFO_T; + + +typedef union { + PCI_INFO_T Pci; + PNP_INFO_T Pnp; +} PCI_PNP_INFO_T; + +typedef struct { + OUT UINT16 Status; ///< OUT: PXENV_STATUS_xxx + OUT UINT8 NicType; ///< OUT: 2=PCI, 3=PnP + PCI_PNP_INFO_T PciPnpInfo; +} PXENV_UNDI_GET_NIC_TYPE_T; + +typedef struct { + OUT UINT16 Status; ///< OUT: PXENV_STATUS_xxx + OUT UINT8 IfaceType[16]; ///< OUT: Type name of MAC, AsciiZ + + /* format. This is used by the */ + + /* Universal NDIS Driver to fill */ + + /* the driver type in it's MAC */ + + /* Service specific */ + + /* characteristic table */ + OUT UINT32 LinkSpeed; ///< OUT: + OUT UINT32 ServiceFlags; ///< OUT: as defined in NDIS Spec 2.0X + OUT UINT32 Reserved[4]; ///< OUT: will be filled with 0s till defined +} PXENV_UNDI_GET_NDIS_INFO_T; + +typedef struct { + OUT UINT16 Status; ///< OUT: PXENV_STATUS_xxx + IN OUT UINT16 FuncFlag; ///< In: PXENV_UNDI_ISR_IN_xxx + + /* Out: PXENV_UNDI_ISR_OUT_xxx */ + OUT UINT16 BufferLength; + OUT UINT16 FrameLength; + OUT UINT16 FrameHeaderLength; + OUT UINT16 FrameOffset; + OUT UINT16 FrameSegSel; + OUT UINT8 ProtType; + OUT UINT8 PktType; +} PXENV_UNDI_ISR_T; + +#define PXENV_UNDI_ISR_IN_START 1 /* This function must be first */ + +/* when an interrupt is received. */ + +/* It will tell us if the intr */ + +/* was generated by our device. */ +#define PXENV_UNDI_ISR_IN_PROCESS 2 /* Call to start processing one of */ + +/* our interrupts. */ +#define PXENV_UNDI_ISR_IN_GET_NEXT 3 /* Call to start/continue receiving */ + +/* data from receive buffer(s). */ + +/*++ + + Possible responses from PXENV_UNDI_ISR_IN_START + + --*/ +#define PXENV_UNDI_ISR_OUT_OURS 0 ///< This is our interrupt. Deal with it. +#define PXENV_UNDI_ISR_OUT_NOT_OURS 1 ///< This is not our interrupt. + +/*++ + + Possible responses from PXENV_UNDI_ISR_IN_PROCESS and + PXENV_UNDI_ISR_IN_PROCESS + +--*/ +#define PXENV_UNDI_ISR_OUT_DONE 0 ///< We are done processing this interrupt. +#define PXENV_UNDI_ISR_OUT_TRANSMIT 2 ///< We completed a transmit interrupt. +#define PXENV_UNDI_ISR_OUT_RECEIVE 3 ///< Get data from receive buffer. + +#define PXENV_UNDI_ISR_OUT_BUSY 4 /* ? */ + +typedef struct { + UINT16 Status; ///< Out: PXENV_STATUS_xxx +} PXENV_STOP_UNDI_T; + +#define PXENV_UNDI_STARTED 1 ///< not even initialized +#define PXENV_UNDI_INITIALIZED 2 ///< initialized and closed (not opened) +#define PXENV_UNDI_OPENED 3 ///< initialized & opened + +typedef struct { + OUT UINT16 Status; ///< Out: PXENV_STATUS_xxx + UINT16 UNDI_State; +} PXENV_UNDI_GET_STATE_T; + +#pragma pack() + +#endif diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/PxeUndi.c b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/PxeUndi.c new file mode 100644 index 0000000000..d35fc3f430 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/PxeUndi.c @@ -0,0 +1,1254 @@ +/** @file + Wrapper routines that use a PXE-enabled NIC option ROM to + supply internal routines for an EFI SNI (Simple Network + Interface) Protocol. + + This file relies upon the existence of a PXE-compliant ROM + in memory, as defined by the Preboot Execution Environment + Specification (PXE), Version 2.1, located at + + http://developer.intel.com/ial/wfm/wfmspecs.htm + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "BiosSnp16.h" + +/** + PXE + START UNDI + Op-Code: PXENV_START_UNDI (0000h) + Input: Far pointer to a PXENV_START_UNDI_T parameter structure that has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This service is used to pass the BIOS parameter registers to the UNDI driver. The UNDI driver is + responsible for saving the information it needs to communicate with the hardware. + This service is also responsible for hooking the Int 1Ah service routine + Note: This API service must be called only once during UNDI Option ROM boot. + The UNDI driver is responsible for saving this information and using it every time + PXENV_UNDI_STARTUP is called. + Service cannot be used in protected mode. + typedef struct { + PXENV_STATUS Status; + UINT16 AX; + UINT16 BX; + UINT16 DX; + UINT16 DI; + UINT16 ES; + } PXENV_START_UNDI_T; + Set before calling API service + AX, BX, DX, DI, ES: BIOS initialization parameter registers. These + fields should contain the same information passed to the option ROM + initialization routine by the Host System BIOS. Information about the + contents of these registers can be found in the [PnP], [PCI] and + [BBS] specifications. + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeStartUndi ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_START_UNDI_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_START_UNDI_T), + PXENV_START_UNDI + ); +} + +/** + PXE + UNDI STARTUP + Op-Code: PXENV_UNDI_STARTUP (0001h) + Input: Far pointer to a PXENV_UNDI_STARTUP_T parameter structure that has been initialized by the + caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This API is responsible for initializing the contents of the UNDI code & data segment for proper + operation. Information from the !PXE structure and the first PXENV_START_UNDI API call is used + to complete this initialization. The rest of the UNDI APIs will not be available until this call has + been completed. + Note: PXENV_UNDI_STARTUP must not be called again without first calling + PXENV_UNDI_SHUTDOWN. + PXENV_UNDI_STARTUP and PXENV_UNDI_SHUTDOWN are no longer responsible for + chaining interrupt 1Ah. This must be done by the PXENV_START_UNDI and + PXENV_STOP_UNDI API calls. + This service cannot be used in protected mode. + typedef struct + { + PXENV_STATUS Status; + } PXENV_UNDI_STARTUP_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiStartup ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_STARTUP_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_STARTUP_T), + PXENV_UNDI_STARTUP + ); +} + +/** + PXE + UNDI CLEANUP + Op-Code: PXENV_UNDI_CLEANUP (0002h) + Input: Far pointer to a PXENV_UNDI_CLEANUP_T parameter structure. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field + in the parameter structure must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This call will prepare the network adapter driver to be unloaded from memory. This call must be + made just before unloading the Universal NIC Driver. The rest of the API will not be available + after this call executes. + This service cannot be used in protected mode. + typedef struct { + PXENX_STATUS Status; + } PXENV_UNDI_CLEANUP_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiCleanup ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_CLEANUP_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_CLEANUP_T), + PXENV_UNDI_CLEANUP + ); +} + +/** + PXE + UNDI INITIALIZE + Op-Code: PXENV_UNDI_INITIALIZE (0003h) + Input: Far pointer to a PXENV_UNDI_INITIALIZE_T parameter structure that has been initialized by the + caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call resets the adapter and programs it with default parameters. The default parameters used + are those supplied to the most recent UNDI_STARTUP call. This routine does not enable the + receive and transmit units of the network adapter to readily receive or transmit packets. The + application must call PXENV_UNDI_OPEN to logically connect the network adapter to the network. + This call must be made by an application to establish an interface to the network adapter driver. + Note: When the PXE code makes this call to initialize the network adapter, it passes a NULL pointer for + the Protocol field in the parameter structure. + typedef struct { + PXENV_STATUS Status; + ADDR32 ProtocolIni; + UINT8 reserved[8]; + } PXENV_UNDI_INITIALIZE_T; + Set before calling API service + ProtocolIni: Physical address of a memory copy of the driver + module from the protocol.ini file obtained from the protocol manager + driver (refer to the NDIS 2.0 specification). This parameter is + supported for the universal NDIS driver to pass the information + contained in the protocol.ini file to the NIC driver for any specific + configuration of the NIC. (Note that the module identification in the + protocol.ini file was done by NDIS.) This value can be NULL for any + other application interfacing to the universal NIC driver + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiInitialize ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_INITIALIZE_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_INITIALIZE_T), + PXENV_UNDI_INITIALIZE + ); +} + +/** + Wrapper routine for reset adapter. + + PXE + UNDI RESET ADAPTER + Op-Code: PXENV_UNDI_RESET_ADAPTER (0004h) + Input: Far pointer to a PXENV_UNDI_RESET_ADAPTER_t parameter structure that has been initialized + by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call resets and reinitializes the network adapter with the same set of parameters supplied to + Initialize Routine. Unlike Initialize, this call opens the adapter that is, it connects logically to the + network. This routine cannot be used to replace Initialize or Shutdown calls. + typedef struct { + PXENV_STATUS Status; + PXENV_UNDI_MCAST_ADDRESS_t R_Mcast_Buf; + } PXENV_UNDI_RESET_T; + + #define MAXNUM_MCADDR 8 + + typedef struct { + UINT16 MCastAddrCount; + MAC_ADDR McastAddr[MAXNUM_MCADDR]; + } PXENV_UNDI_MCAST_ADDRESS_t; + + Set before calling API service + R_Mcast_Buf: This is a structure of MCastAddrCount and + McastAddr. + MCastAddrCount: Number of multicast MAC addresses in the + buffer. + McastAddr: List of up to MAXNUM_MCADDR multicast MAC + addresses. + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance. + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + @param RxFilter Filter setting mask value for PXE recive . + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiResetNic ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_RESET_T *PxeUndiTable, + IN UINT16 RxFilter + ) +{ + PXENV_UNDI_OPEN_T Open; + PXENV_UNDI_CLOSE_T Close; + UINTN Status; + + Status = MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_RESET_T), + PXENV_UNDI_RESET_NIC + ); + if (!EFI_ERROR(Status)) { + return Status; + } + + Close.Status = PXENV_STATUS_SUCCESS; + + Status = MakePxeCall ( + SimpleNetworkDevice, + &Close, + sizeof (Close), + PXENV_UNDI_CLOSE + ); + if (EFI_ERROR(Status)) { + return EFI_DEVICE_ERROR; + } + + Status = MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_RESET_T), + PXENV_UNDI_RESET_NIC + ); + if (EFI_ERROR(Status)) { + return EFI_DEVICE_ERROR; + } + + Open.Status = PXENV_STATUS_SUCCESS; + Open.OpenFlag = 0; + Open.PktFilter = RxFilter; + CopyMem ( + &Open.McastBuffer, + &PxeUndiTable->R_Mcast_Buf, + sizeof (PXENV_UNDI_MCAST_ADDR_T) + ); + + + Status = MakePxeCall ( + SimpleNetworkDevice, + &Open, + sizeof (Open), + PXENV_UNDI_OPEN + ); + if (EFI_ERROR(Status)) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + PXE + UNDI SHUTDOWN + Op-Code: PXENV_UNDI_SHUTDOWN (0005h) + Input: Far pointer to a PXENV_UNDI_SHUTDOWN_T parameter. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call resets the network adapter and leaves it in a safe state for another driver to program it. + Note: The contents of the PXENV_UNDI_STARTUP parameter structure need to be saved by the + Universal NIC Driver in case PXENV_UNDI_INITIALIZE is called again. + typedef struct + { + PXENV_STATUS Status; + } PXENV_UNDI_SHUTDOWN_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiShutdown ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_SHUTDOWN_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_SHUTDOWN_T), + PXENV_UNDI_SHUTDOWN + ); +} + +/** + PXE + UNDI OPEN + Op-Code: PXENV_UNDI_OPEN (0006h) + Input: Far pointer to a PXENV_UNDI_OPEN_T parameter structure that has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call activates the adapter network connection and sets the adapter ready to accept packets + for transmit and receive. + typedef struct { + PXENV_STATUS Status; + UINT16 OpenFlag; + UINT16 PktFilter; + #define FLTR_DIRECTED 0x0001 + #define FLTR_BRDCST 0x0002 + #define FLTR_PRMSCS 0x0004 + #define FLTR_SRC_RTG 0x0008 + PXENV_UNDI_MCAST_ADDRESS_t R_Mcast_Buf; + } PXENV_UNDI_OPEN_T; + Set before calling API service + OpenFlag: This is an adapter specific input parameter. This is + supported for the universal NDIS 2.0 driver to pass in the open flags + provided by the protocol driver. (See the NDIS 2.0 specification.) + This can be zero. + PktFilter: Filter for receiving packets. This can be one, or more, of + the FLTR_xxx constants. Multiple values are arithmetically or-ed + together. + directed packets are packets that may come to your MAC address + or the multicast MAC address. + R_Mcast_Buf: See definition in UNDI RESET ADAPTER (0004h). + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiOpen ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_OPEN_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_OPEN_T), + PXENV_UNDI_OPEN + ); +} + +/** + PXE + UNDI CLOSE + Op-Code: PXENV_UNDI_CLOSE (0007h) + Input: Far pointer to a PXENV_UNDI_CLOSE_T parameter. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call disconnects the network adapter from the network. Packets cannot be transmitted or + received until the network adapter is open again. + typedef struct { + PXENV_STATUS Status; + } PXENV_UNDI_CLOSE_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiClose ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_CLOSE_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_CLOSE_T), + PXENV_UNDI_CLOSE + ); +} + +/** + PXE + UNDI TRANSMIT PACKET + Op-Code: PXENV_UNDI_TRANSMIT (0008h) + Input: Far pointer to a PXENV_UNDI_TRANSMIT_T parameter structure that + has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. + The status code must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This call transmits a buffer to the network. The media header + for the packet can be filled by the calling protocol, but it might not be. + The network adapter driver will fill it if required by the values in the + parameter block. The packet is buffered for transmission provided there is + an available buffer, and the function returns PXENV_EXIT_SUCCESS. If no + buffer is available the function returns PXENV_EXIT_FAILURE with a status + code of PXE_UNDI_STATUS__OUT OF_RESOURCE. The number of buffers is + implementation-dependent. An interrupt is generated on completion of the + transmission of one or more packets. A call to PXENV_UNDI_TRANSMIT is + permitted in the context of a transmit complete interrupt. + + typedef struct { + PXENV_STATUS Status; + UINT8 Protocol; + #define P_UNKNOWN 0 + #define P_IP 1 + #define P_ARP 2 + #define P_RARP 3 + UINT8 XmitFlag; + #define XMT_DESTADDR 0x0000 + #define XMT_BROADCAST 0x0001 + SEGOFF16 DestAddr; + SEGOFF16 TBD; + UINT32 Reserved[2]; + } t_PXENV_UNDI_TRANSMIT; + + #define MAX_DATA_BLKS 8 + + typedef struct { + UINT16 ImmedLength; + SEGOFF16 Xmit; + UINT16 DataBlkCount; + struct DataBlk { + UINT8 TDPtrType; + UINT8 TDRsvdByte; + UINT16 TDDataLen; + SEGOFF16 TDDataPtr; + } DataBlock[MAX_DATA_BLKS]; + } PXENV_UNDI_TBD_T + + Set before calling API service + Protocol: This is the protocol of the upper layer that is calling UNDI + TRANSMIT call. If the upper layer has filled the media header, this + field must be P_UNKNOWN. + XmitFlag: If this flag is XMT_DESTADDR, the NIC driver expects a + pointer to the destination media address in the field DestAddr. If + XMT_BROADCAST, the NIC driver fills the broadcast address for the + destination. + TBD: Segment:Offset address of the transmit buffer descriptor. + ImmedLength: Length of the immediate transmit buffer: Xmit. + Xmit: Segment:Offset of the immediate transmit buffer. + DataBlkCount: Number of blocks in this transmit buffer. + TDPtrType: + 0 => 32-bit physical address in TDDataPtr (not supported in this + version of PXE) + 1 => segment:offset in TDDataPtr which can be a real mode or 16-bit + protected mode pointer + TDRsvdByte: Reserved must be zero. + TDDatalen: Data block length in bytes. + TDDataPtr: Segment:Offset of the transmit block. + DataBlock: Array of transmit data blocks. + Returned from API service + Status: See the PXENV_STATUS_xxx constants + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiTransmit ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_TRANSMIT_T *PxeUndiTable + ) +{ + EFI_STATUS Status; + + Status = MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_TRANSMIT_T), + PXENV_UNDI_TRANSMIT + ); + if (Status == EFI_SUCCESS) { + return EFI_SUCCESS; + } + + switch (PxeUndiTable->Status) { + case PXENV_STATUS_OUT_OF_RESOURCES: + return EFI_NOT_READY; + + default: + return EFI_DEVICE_ERROR; + } +} + +/** + PXE + UNDI SET MULTICAST ADDRESS + Op-Code: PXENV_UNDI_SET_MCAST_ADDRESS (0009h) + Input: Far pointer to a PXENV_TFTP_SET_MCAST_ADDRESS_t parameter structure that has been + initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call changes the current list of multicast addresses to the input list and resets the network + adapter to accept it. If the number of multicast addresses is zero, multicast is disabled. + typedef struct { + PXENV_STATUS Status; + PXENV_UNDI_MCAST_ADDRESS_t R_Mcast_Buf; + } PXENV_UNDI_SET_MCAST_ADDR_T; + Set before calling API service + R_Mcast_Buf: See description in the UNDI RESET ADAPTER + (0004h) API. + Returned from API service + Status: See the PXENV_STATUS_xxx constants + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiSetMcastAddr ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_SET_MCAST_ADDR_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_SET_MCAST_ADDR_T), + PXENV_UNDI_SET_MCAST_ADDR + ); +} + +/** + PXE + UNDI SET STATION ADDRESS + Op-Code: PXENV_UNDI_SET_STATION_ADDRESS (000Ah) + Input: Far pointer to a PXENV_UNDI_SET_STATION_ADDRESS_t parameter structure that has been + initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call sets the MAC address to be the input value and is called before opening the network + adapter. Later, the open call uses this variable as a temporary MAC address to program the + adapter individual address registers. + typedef struct { + PXENV_STATUS Status; + MAC_ADDR StationAddress; + } PXENV_UNDI_SET_STATION_ADDR_T; + Set before calling API service + StationAddress: Temporary MAC address to be used for + transmit and receive. + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiSetStationAddr ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_SET_STATION_ADDR_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_SET_STATION_ADDR_T), + PXENV_UNDI_SET_STATION_ADDR + ); +} + +/** + PXE + UNDI SET PACKET FILTER + Op-Code: PXENV_UNDI_SET_PACKET_FILTER (000Bh) + Input: Far pointer to a PXENV_UNDI_SET_PACKET_FILTER_T parameter structure that has been + initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call resets the adapter's receive unit to accept a new filter, different from the one provided with + the open call. + typedef struct { + PXENV_STATUS Status; + UINT8 filter; + } PXENV_UNDI_SET_PACKET_FILTER_T; + Set before calling API service + Filter: See the receive filter values in the UNDI OPEN + (0006h) API description. + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiSetPacketFilter ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_SET_PACKET_FILTER_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_SET_PACKET_FILTER_T), + PXENV_UNDI_SET_PACKET_FILTER + ); +} + +/** + PXE + UNDI GET INFORMATION + Op-Code: PXENV_UNDI_GET_INFORMATION (000Ch) + Input: Far pointer to a PXENV_UNDI_GET_INFORMATION_T parameter structure that has been + initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This call copies the network adapter variables, including the MAC address, into the input buffer. + Note: The PermNodeAddress field must be valid after PXENV_START_UNDI and + PXENV_UNDI_STARTUP have been issued. All other fields must be valid after + PXENV_START_UNDI, PXENV_UNDI_STARTUP and PXENV_UNDI_INITIALIZE have been + called. + typedef struct { + PXENV_STATUS Status; + UINT16 BaseIo; + UINT16 IntNumber; + UINT16 MaxTranUnit; + UINT16 HwType; + #define ETHER_TYPE 1 + #define EXP_ETHER_TYPE 2 + #define IEEE_TYPE 6 + #define ARCNET_TYPE 7 + UINT16 HwAddrLen; + MAC_ADDR CurrentNodeAddress; + MAC_ADDR PermNodeAddress; + SEGSEL ROMAddress; + UINT16 RxBufCt; + UINT16 TxBufCt; + } PXENV_UNDI_GET_INFORMATION_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + BaseIO: Adapter base I/O address. + IntNumber: Adapter IRQ number. + MaxTranUnit: Adapter maximum transmit unit. + HWType: Type of protocol at the hardware level. + HWAddrLen: Length of the hardware address. + CurrentNodeAddress: Current hardware address. + PermNodeAddress: Permanent hardware address. + ROMAddress: Real mode ROM segment address. + RxBufCnt: Receive queue length. + TxBufCnt: Transmit queue length. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetInformation ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_INFORMATION_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_GET_INFORMATION_T), + PXENV_UNDI_GET_INFORMATION + ); +} + +/** + PXE + UNDI GET STATISTICS + Op-Code: PXENV_UNDI_GET_STATISTICS (000Dh) + Input: Far pointer to a PXENV_UNDI_GET_STATISTICS_T parameter structure that has been initialized + by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call reads statistical information from the network adapter, and returns. + typedef struct { + PXENV_STATUS Status; + UINT32 XmtGoodFrames; + UINT32 RcvGoodFrames; + UINT32 RcvCRCErrors; + UINT32 RcvResourceErrors; + } PXENV_UNDI_GET_STATISTICS_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + XmtGoodFrames: Number of successful transmissions. + RcvGoodFrames: Number of good frames received. + RcvCRCErrors: Number of frames received with CRC + error. + RcvResourceErrors: Number of frames discarded + because receive queue was full. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetStatistics ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_STATISTICS_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_GET_STATISTICS_T), + PXENV_UNDI_GET_STATISTICS + ); +} + +/** + PXE + UNDI CLEAR STATISTICS + Op-Code: PXENV_UNDI_CLEAR_STATISTICS (000Eh) + Input: Far pointer to a PXENV_UNDI_CLEAR_STATISTICS_T parameter. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This call clears the statistical information from the network adapter. + typedef struct { + PXENV_STATUS Status; + } PXENV_UNDI_CLEAR_STATISTICS_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiClearStatistics ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_CLEAR_STATISTICS_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_CLEAR_STATISTICS_T), + PXENV_UNDI_CLEAR_STATISTICS + ); +} + +/** + PXE + UNDI INITIATE DIAGS + Op-Code: PXENV_UNDI_INITIATE_DIAGS (000Fh) + Input: Far pointer to a PXENV_UNDI_INITIATE_DIAGS_T parameter. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the + PXENV_STATUS_xxx constants. + Description: This call can be used to initiate the run-time diagnostics. It causes the network adapter to run + hardware diagnostics and to update its status information. + typedef struct { + PXENV_STATUS Status; + } PXENV_UNDI_INITIATE_DIAGS_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiInitiateDiags ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_INITIATE_DIAGS_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_INITIATE_DIAGS_T), + PXENV_UNDI_INITIATE_DIAGS + ); +} + +/** + PXE + UNDI FORCE INTERRUPT + Op-Code: PXENV_UNDI_FORCE_INTERRUPT (0010h) + Input: Far pointer to a PXENV_UNDI_FORCE_INTERRUPT_T parameter structure that has been + initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call forces the network adapter to generate an interrupt. When a receive interrupt occurs, the + network adapter driver usually queues the packet and calls the application's callback receive + routine with a pointer to the packet received. Then, the callback routine either can copy the packet + to its buffer or can decide to delay the copy to a later time. If the packet is not immediately copied, + the network adapter driver does not remove it from the input queue. When the application wants to + copy the packet, it can call the PXENV_UNDI_FORCE_INTERRUPT routine to simulate the receive + interrupt. + typedef struct { + PXENV_STATUS Status; + } PXENV_UNDI_FORCE_INTERRUPT_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiForceInterrupt ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_FORCE_INTERRUPT_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_FORCE_INTERRUPT_T), + PXENV_UNDI_FORCE_INTERRUPT + ); +} + +/** + PXE + UNDI GET MULTICAST ADDRESS + Op-Code: PXENV_UNDI_GET_MCAST_ADDRESS (0011h) + Input: Far pointer to a PXENV_GET_MCAST_ADDRESS_t parameter structure that has been initialized + by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This call converts the given IP multicast address to a hardware multicast address. + typedef struct { + PXENV_STATUS Status; + IP4 InetAddr; + MAC_ADDR MediaAddr; + } PXENV_UNDI_GET_MCAST_ADDR_T; + Set before calling API service + InetAddr: IP multicast address. + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + MediaAddr: MAC multicast address. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetMcastAddr ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_MCAST_ADDR_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_GET_MCAST_ADDR_T), + PXENV_UNDI_GET_MCAST_ADDR + ); +} + +/** + PXE + UNDI GET NIC TYPE + Op-Code: PXENV_UNDI_GET_NIC_TYPE (0012h) + Input: Far pointer to a PXENV_UNDI_GET_NIC_TYPE parameter structure that has been initialized by + the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. If the PXENV_EXIT_SUCCESS is returned the parameter structure must contain the + NIC information. + Description: This call, if successful, provides the NIC-specific information necessary to identify the network + adapter that is used to boot the system. + Note: The application first gets the DHCPDISCOVER packet using GET_CACHED_INFO and checks if + the UNDI is supported before making this call. If the UNDI is not supported, the NIC-specific + information can be obtained from the DHCPDISCOVER packet itself. + PXENV_START_UNDI, PXENV_UNDI_STARTUP and PXENV_UNDI_INITIALIZE must be called + before the information provided is valid. + typedef { + PXENV_STATUS Status; + UINT8 NicType; + #define PCI_NIC 2 + #define PnP_NIC 3 + #define CardBus_NIC 4 + Union { + Struct { + UINT16 Vendor_ID; + UINT16 Dev_ID; + UINT8 Base_Class; + UINT8 Sub_Class; + UINT8 Prog_Intf; + UINT8 Rev; + UINT16 BusDevFunc; + UINT16 SubVendor_ID; + UINT16 SubDevice_ID; + } pci, cardbus; + struct { + UINT32 EISA_Dev_ID; + UINT8 Base_Class; + UINT8 Sub_Class; + UINT8 Prog_Intf; + UINT16 CardSelNum; + } pnp; + } info; + } PXENV_UNDI_GET_NIC_TYPE_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + NICType: Type of NIC information stored in the parameter + structure. + Info: Information about the fields in this union can be found + in the [PnP] and [PCI] specifications + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetNicType ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_NIC_TYPE_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_GET_NIC_TYPE_T), + PXENV_UNDI_GET_NIC_TYPE + ); +} + +/** + PXE + UNDI GET IFACE INFO + Op-Code: PXENV_UNDI_GET_IFACE_INFO (0013h) + Input: Far pointer to a PXENV_UNDI_GET_IFACE_INFO_t parameter structure that has been initialized + by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. If the PXENV_EXIT_SUCCESS is returned, the parameter structure must contain the + interface specific information. + Description: This call, if successful, provides the network interface specific information such as the interface + type at the link layer (Ethernet, Tokenring) and the link speed. This information can be used in the + universal drivers such as NDIS or Miniport to communicate to the upper protocol modules. + Note: UNDI follows the NDIS2 specification in giving this information. It is the responsibility of the + universal driver to translate/convert this information into a format that is required in its specification + or to suit the expectation of the upper level protocol modules. + PXENV_START_UNDI, PXENV_UNDI_STARTUP and PXENV_UNDI_INITIALIZE must be called + before the information provided is valid. + typedef struct { + PXENV_STATUS Status + UINT8 IfaceType[16]; + UINT32 LinkSpeed; + UINT32 ServiceFlags; + UINT32 Reserved[4]; + } PXENV_UNDI_GET_NDIS_INFO_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + IfaceType: Name of MAC type in ASCIIZ format. This is + used by the universal NDIS driver to specify its driver type + to the protocol driver. + LinkSpeed: Defined in the NDIS 2.0 specification. + ServiceFlags: Defined in the NDIS 2.0 specification. + Reserved: Must be zero. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetNdisInfo ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_NDIS_INFO_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_GET_NDIS_INFO_T), + PXENV_UNDI_GET_NDIS_INFO + ); +} + +/** + PXE + UNDI ISR + Op-Code: PXENV_UNDI_ISR (0014h) + Input: Far pointer to a PXENV_UNDI_ISR_T parameter structure that has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This API function will be called at different levels of processing the interrupt. The FuncFlag field in + the parameter block indicates the operation to be performed for the call. This field is filled with the + status of that operation on return. + Note: Interrupt Service Routine Operation: + In this design the UNDI does not hook the interrupt for the Network Interface. Instead, the + application or the protocol driver hooks the interrupt and calls UNDI with the PXENV_UNDI_ISR + API call for interrupt verification (PXENV_UNDI_ISR_IN_START) and processing + (PXENV_UNDI_ISR_IN_PROCESS and PXENV_UNDI_ISR_GET_NEXT). + When the Network Interface HW generates an interrupt the protocol driver interrupt service + routine (ISR) gets control and takes care of the interrupt processing at the PIC level. The ISR then + calls the UNDI using the PXENV_UNDI_ISR API with the value PXENV_UNDI_ISR_IN_START for + the FuncFlag parameter. At this time UNDI must disable the interrupts at the Network Interface + level and read any status values required to further process the interrupt. UNDI must return as + quickly as possible with one of the two values, PXENV_UNDI_ISR_OUT_OURS or + PXENV_UNDI_ISR_OUT_NOT_OURS, for the parameter FuncFlag depending on whether the + interrupt was generated by this particular Network Interface or not. + If the value returned in FuncFlag is PXENV_UNDI_ISR_OUT_NOT_OURS, then the interrupt was + not generated by our NIC, and interrupt processing is complete. + If the value returned in FuncFlag is PXENV_UNDI_ISR_OUT_OURS, the protocol driver must start + a handler thread and send an end-of-interrupt (EOI) command to the PIC. Interrupt processing is + now complete. + The protocol driver strategy routine will call UNDI using this same API with FuncFlag equal to + PXENV_UNDI_ISR_IN_PROCESS. At this time UNDI must find the cause of this interrupt and + return the status in the FuncFlag. It first checks if there is a frame received and if so it returns the + first buffer pointer of that frame in the parameter block. + The protocol driver calls UNDI repeatedly with the FuncFlag equal to + PXENV_UNDI_ISR_IN_GET_NEXT to get all the buffers in a frame and also all the received + frames in the queue. On this call, UNDI must remember the previous buffer given to the protoco,l + remove it from the receive queue and recycle it. In case of a multi-buffered frame, if the previous + buffer is not the last buffer in the frame it must return the next buffer in the frame in the parameter + block. Otherwise it must return the first buffer in the next frame. + If there is no received frame pending to be processed, UNDI processes the transmit completes and + if there is no other interrupt status to be processed, UNDI re-enables the interrupt at the + NETWORK INTERFACE level and returns PXENV_UNDI_ISR_OUT_DONE in the FuncFlag. + IMPORTANT: It is possible for the protocol driver to be interrupted again while in the + strategy routine when the UNDI re-enables interrupts. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiIsr ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_ISR_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_ISR_T), + PXENV_UNDI_ISR + ); +} + +/** + PXE + STOP UNDI + Op-Code: PXENV_STOP_UNDI (0015h) + Input: Far pointer to a PXENV_STOP_UNDI_T parameter structure that has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This routine is responsible for unhooking the Int 1Ah service routine. + Note: This API service must be called only once at the end of UNDI Option ROM boot. One of the valid + status codes is PXENV_STATUS_KEEP. If this status is returned, UNDI must not be removed from + base memory. Also, UNDI must not be removed from base memory if BC is not removed from base + memory. + Service cannot be used in protected mode. + typedef struct { + PXENV_STATUS Status; + } PXENV_STOP_UNDI_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiStop ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_STOP_UNDI_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_STOP_UNDI_T), + PXENV_STOP_UNDI + ); +} + +/** + PXE + UNDI GET STATE + Op-Code: PXENV_UNDI_GET_STATE (0015h) + Input: Far pointer to a PXENV_UNDI_GET_STATE_T parameter. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. The UNDI_STATE field in the parameter structure must be set to one of the valid state + constants + Description: This call can be used to obtain state of the UNDI engine in order to avoid issuing adverse call + sequences + typedef struct { + #define PXE_UNDI_GET_STATE_STARTED 1 + #define PXE_UNDI_GET_STATE_INITIALIZED 2 + #define PXE_UNDI_GET_STATE_OPENED 3 + PXENV_STATUS Status; + UINT8 UNDIstate; + } PXENV_UNDI_GET_STATE_T; + Set before calling API service + N/A + Returned from API service + Status: See the PXENV_STATUS_xxx constants. + State: See definitions of the state constants. + Note. UNDI implementation is responsible for maintaining + internal state machine. + UNDI ISR + Op-Code: PXENV_UNDI_ISR (0014h) + Input: Far pointer to a t_PXENV_UNDI_ISR parameter structure that has been initialized by the caller. + Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be returned in AX. The status field in + the parameter structure must be set to one of the values represented by the PXENV_STATUS_xxx + constants. + Description: This API function will be called at different levels of processing the interrupt. The FuncFlag field in + the parameter block indicates the operation to be performed for the call. This field is filled with the + status of that operation on return. + + @param SimpleNetworkDevice Device instance + @param PxeUndiTable Point to structure which hold paramter and return value + for option ROM call. + + @return Return value of PXE option ROM far call. +**/ +EFI_STATUS +PxeUndiGetState ( + IN EFI_SIMPLE_NETWORK_DEV *SimpleNetworkDevice, + IN OUT PXENV_UNDI_GET_STATE_T *PxeUndiTable + ) +{ + return MakePxeCall ( + SimpleNetworkDevice, + PxeUndiTable, + sizeof (PXENV_UNDI_GET_STATE_T), + PXENV_UNDI_GET_STATE + ); +} diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Snp16Dxe.inf b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Snp16Dxe.inf new file mode 100644 index 0000000000..c37c4abb86 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Snp16Dxe.inf @@ -0,0 +1,67 @@ +## @file +# Thunk wrapper UEFI driver to produce EFI SNP protocol based on legacy 16 NIC ROM. +# +# Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions +# of the BSD License which accompanies this distribution. The +# full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + BASE_NAME = BiosSnp16 + FILE_GUID = D0CAA91E-2DE4-4b0d-B3DC-09C67E854E34 + MODULE_TYPE = UEFI_DRIVER + INF_VERSION = 0x00010005 + VERSION_STRING = 1.0 + + ENTRY_POINT = BiosSnp16DriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gBiosSnp16DriverBinding +# COMPONENT_NAME = gBiosSnp16ComponentName +# + +[Sources] + BiosSnp16.h + BiosSnp16.c + Misc.c + Pxe.h + PxeUndi.c + ComponentName.c + + +[Libraryclasses] + UefiDriverEntryPoint + DebugLib + BaseMemoryLib + UefiBootServicesTableLib + UefiLib + BaseLib + DevicePathLib + MemoryAllocationLib + +[Guids] + gEfiEventExitBootServicesGuid + +[Protocols] + gEfiNetworkInterfaceIdentifierProtocolGuid + gEfiDevicePathProtocolGuid + gEfiSimpleNetworkProtocolGuid + gEfiPciIoProtocolGuid + gEfiLegacyBiosProtocolGuid + +[Packages] + MdePkg/MdePkg.dec + IntelFrameworkPkg/IntelFrameworkPkg.dec + diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/BiosVideo.c b/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/BiosVideo.c new file mode 100644 index 0000000000..76591e7a47 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/BiosVideo.c @@ -0,0 +1,3164 @@ +/** @file + ConsoleOut Routines that speak VGA. + +Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "BiosVideo.h" + +// +// EFI Driver Binding Protocol Instance +// +EFI_DRIVER_BINDING_PROTOCOL gBiosVideoDriverBinding = { + BiosVideoDriverBindingSupported, + BiosVideoDriverBindingStart, + BiosVideoDriverBindingStop, + 0x3, + NULL, + NULL +}; + +// +// Global lookup tables for VGA graphics modes +// +UINT8 mVgaLeftMaskTable[] = { 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; + +UINT8 mVgaRightMaskTable[] = { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + +UINT8 mVgaBitMaskTable[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + +EFI_GRAPHICS_OUTPUT_BLT_PIXEL mVgaColorToGraphicsOutputColor[] = { + { 0x00, 0x00, 0x00, 0x00 }, + { 0x98, 0x00, 0x00, 0x00 }, + { 0x00, 0x98, 0x00, 0x00 }, + { 0x98, 0x98, 0x00, 0x00 }, + { 0x00, 0x00, 0x98, 0x00 }, + { 0x98, 0x00, 0x98, 0x00 }, + { 0x00, 0x98, 0x98, 0x00 }, + { 0x98, 0x98, 0x98, 0x00 }, + { 0x10, 0x10, 0x10, 0x00 }, + { 0xff, 0x10, 0x10, 0x00 }, + { 0x10, 0xff, 0x10, 0x00 }, + { 0xff, 0xff, 0x10, 0x00 }, + { 0x10, 0x10, 0xff, 0x00 }, + { 0xf0, 0x10, 0xff, 0x00 }, + { 0x10, 0xff, 0xff, 0x00 }, + { 0xff, 0xff, 0xff, 0x00 } +}; + +// +// Standard timing defined by VESA EDID +// +VESA_BIOS_EXTENSIONS_EDID_TIMING mEstablishedEdidTiming[] = { + // + // Established Timing I + // + {800, 600, 60}, + {800, 600, 56}, + {640, 480, 75}, + {640, 480, 72}, + {640, 480, 67}, + {640, 480, 60}, + {720, 400, 88}, + {720, 400, 70}, + // + // Established Timing II + // + {1280, 1024, 75}, + {1024, 768, 75}, + {1024, 768, 70}, + {1024, 768, 60}, + {1024, 768, 87}, + {832, 624, 75}, + {800, 600, 75}, + {800, 600, 72}, + // + // Established Timing III + // + {1152, 870, 75} +}; + +/** + Supported. + + @param This Pointer to driver binding protocol + @param Controller Controller handle to connect + @param RemainingDevicePath A pointer to the remaining portion of a device + path + + @retval EFI_STATUS EFI_SUCCESS:This controller can be managed by this + driver, Otherwise, this controller cannot be + managed by this driver + +**/ +EFI_STATUS +EFIAPI +BiosVideoDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + EFI_DEV_PATH *Node; + + // + // See if the Legacy BIOS Protocol is available + // + Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + return Status; + } + + if (Status == EFI_ALREADY_STARTED) { + // + // If VgaMiniPort protocol is installed, EFI_ALREADY_STARTED indicates failure, + // because VgaMiniPort protocol is installed on controller handle directly. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiVgaMiniPortProtocolGuid, + NULL, + NULL, + NULL, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + } + // + // See if this is a PCI Graphics Controller by looking at the Command register and + // Class Code Register + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + Status = EFI_UNSUPPORTED; + if (Pci.Hdr.ClassCode[2] == 0x03 || (Pci.Hdr.ClassCode[2] == 0x00 && Pci.Hdr.ClassCode[1] == 0x01)) { + + Status = EFI_SUCCESS; + // + // If this is a graphics controller, + // go further check RemainingDevicePath validation + // + if (RemainingDevicePath != NULL) { + Node = (EFI_DEV_PATH *) RemainingDevicePath; + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, return EFI_SUCCESS + // + if (!IsDevicePathEnd (Node)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // check its validation + // + if (Node->DevPath.Type != ACPI_DEVICE_PATH || + Node->DevPath.SubType != ACPI_ADR_DP || + DevicePathNodeLength(&Node->DevPath) != sizeof(ACPI_ADR_DEVICE_PATH)) { + Status = EFI_UNSUPPORTED; + } + } + } + } + +Done: + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Install Graphics Output Protocol onto VGA device handles. + + @param This Pointer to driver binding protocol + @param Controller Controller handle to connect + @param RemainingDevicePath A pointer to the remaining portion of a device + path + + @return EFI_STATUS + +**/ +EFI_STATUS +EFIAPI +BiosVideoDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + UINTN Flags; + UINT64 OriginalPciAttributes; + UINT64 Supports; + BOOLEAN PciAttributesSaved; + + // + // Initialize local variables + // + PciIo = NULL; + ParentDevicePath = NULL; + + // + // + // See if the Legacy BIOS Protocol is available + // + Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the IO Abstraction(s) needed + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + return Status; + } + + PciAttributesSaved = FALSE; + // + // Save original PCI attributes + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationGet, + 0, + &OriginalPciAttributes + ); + + if (EFI_ERROR (Status)) { + goto Done; + } + PciAttributesSaved = TRUE; + + // + // Get supported PCI attributes + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Supports &= (EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16); + if (Supports == 0 || Supports == (EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16)) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + // + // Prepare for status code + // + Status = gBS->HandleProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_PC_ENABLE, + ParentDevicePath + ); + // + // Enable the device and make sure VGA cycles are being forwarded to this VGA device + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | Supports, + NULL + ); + if (EFI_ERROR (Status)) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_RESOURCE_CONFLICT, + ParentDevicePath + ); + goto Done; + } + // + // Check to see if there is a legacy option ROM image associated with this PCI device + // + Status = LegacyBios->CheckPciRom ( + LegacyBios, + Controller, + NULL, + NULL, + &Flags + ); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Post the legacy option ROM if it is available. + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_P_PC_RESET, + ParentDevicePath + ); + Status = LegacyBios->InstallPciRom ( + LegacyBios, + Controller, + NULL, + &Flags, + NULL, + NULL, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_CONTROLLER_ERROR, + ParentDevicePath + ); + goto Done; + } + + if (RemainingDevicePath != NULL) { + if (IsDevicePathEnd (RemainingDevicePath) && + (FeaturePcdGet (PcdBiosVideoCheckVbeEnable) || FeaturePcdGet (PcdBiosVideoCheckVgaEnable))) { + // + // If RemainingDevicePath is the End of Device Path Node, + // don't create any child device and return EFI_SUCESS + Status = EFI_SUCCESS; + goto Done; + } + } + + // + // Create child handle and install GraphicsOutputProtocol on it + // + Status = BiosVideoChildHandleInstall ( + This, + Controller, + PciIo, + LegacyBios, + ParentDevicePath, + RemainingDevicePath, + OriginalPciAttributes + ); + +Done: + if ((EFI_ERROR (Status)) && (Status != EFI_ALREADY_STARTED)) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_PC_DISABLE, + ParentDevicePath + ); + + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_NOT_DETECTED, + ParentDevicePath + ); + if (PciAttributesSaved) { + // + // Restore original PCI attributes + // + PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + OriginalPciAttributes, + NULL + ); + } + // + // Release PCI I/O Protocols on the controller handle. + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + return Status; +} + + +/** + Stop. + + @param This Pointer to driver binding protocol + @param Controller Controller handle to connect + @param NumberOfChildren Number of children handle created by this driver + @param ChildHandleBuffer Buffer containing child handle created + + @retval EFI_SUCCESS Driver disconnected successfully from controller + @retval EFI_UNSUPPORTED Cannot find BIOS_VIDEO_DEV structure + +**/ +EFI_STATUS +EFIAPI +BiosVideoDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + BOOLEAN AllChildrenStopped; + UINTN Index; + + AllChildrenStopped = TRUE; + + if (NumberOfChildren == 0) { + // + // Close PCI I/O protocol on the controller handle + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; + } + + for (Index = 0; Index < NumberOfChildren; Index++) { + Status = BiosVideoChildHandleUninstall (This, Controller, ChildHandleBuffer[Index]); + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Install child handles if the Handle supports MBR format. + + @param This Calling context. + @param ParentHandle Parent Handle + @param ParentPciIo Parent PciIo interface + @param ParentLegacyBios Parent LegacyBios interface + @param ParentDevicePath Parent Device Path + @param RemainingDevicePath Remaining Device Path + @param OriginalPciAttributes Original PCI Attributes + + @retval EFI_SUCCESS If a child handle was added + @retval other A child handle was not added + +**/ +EFI_STATUS +BiosVideoChildHandleInstall ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ParentHandle, + IN EFI_PCI_IO_PROTOCOL *ParentPciIo, + IN EFI_LEGACY_BIOS_PROTOCOL *ParentLegacyBios, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath, + IN UINT64 OriginalPciAttributes + ) +{ + EFI_STATUS Status; + BIOS_VIDEO_DEV *BiosVideoPrivate; + PCI_TYPE00 Pci; + ACPI_ADR_DEVICE_PATH AcpiDeviceNode; + BOOLEAN ProtocolInstalled; + + // + // Allocate the private device structure for video device + // + BiosVideoPrivate = (BIOS_VIDEO_DEV *) AllocateZeroPool ( + sizeof (BIOS_VIDEO_DEV) + ); + if (NULL == BiosVideoPrivate) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // See if this is a VGA compatible controller or not + // + Status = ParentPciIo->Pci.Read ( + ParentPciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_CONTROLLER_ERROR, + ParentDevicePath + ); + goto Done; + } + BiosVideoPrivate->VgaCompatible = FALSE; + if (Pci.Hdr.ClassCode[2] == 0x00 && Pci.Hdr.ClassCode[1] == 0x01) { + BiosVideoPrivate->VgaCompatible = TRUE; + } + + if (Pci.Hdr.ClassCode[2] == 0x03 && Pci.Hdr.ClassCode[1] == 0x00 && Pci.Hdr.ClassCode[0] == 0x00) { + BiosVideoPrivate->VgaCompatible = TRUE; + } + + if (PcdGetBool (PcdBiosVideoSetTextVgaModeEnable)) { + // + // Create EXIT_BOOT_SERIVES Event + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + BiosVideoNotifyExitBootServices, + BiosVideoPrivate, + &gEfiEventExitBootServicesGuid, + &BiosVideoPrivate->ExitBootServicesEvent + ); + if (EFI_ERROR (Status)) { + goto Done; + } + } + + // + // Initialize the child private structure + // + BiosVideoPrivate->Signature = BIOS_VIDEO_DEV_SIGNATURE; + + // + // Fill in Graphics Output specific mode structures + // + BiosVideoPrivate->HardwareNeedsStarting = TRUE; + BiosVideoPrivate->ModeData = NULL; + BiosVideoPrivate->LineBuffer = NULL; + BiosVideoPrivate->VgaFrameBuffer = NULL; + BiosVideoPrivate->VbeFrameBuffer = NULL; + + // + // Fill in the Graphics Output Protocol + // + BiosVideoPrivate->GraphicsOutput.QueryMode = BiosVideoGraphicsOutputQueryMode; + BiosVideoPrivate->GraphicsOutput.SetMode = BiosVideoGraphicsOutputSetMode; + + + // + // Allocate buffer for Graphics Output Protocol mode information + // + BiosVideoPrivate->GraphicsOutput.Mode = (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *) AllocatePool ( + sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE) + ); + if (NULL == BiosVideoPrivate->GraphicsOutput.Mode) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + BiosVideoPrivate->GraphicsOutput.Mode->Info = (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *) AllocatePool ( + sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION) + ); + if (NULL == BiosVideoPrivate->GraphicsOutput.Mode->Info) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // Assume that Graphics Output Protocol will be produced until proven otherwise + // + BiosVideoPrivate->ProduceGraphicsOutput = TRUE; + + // + // Set Gop Device Path, here RemainingDevicePath will not be one End of Device Path Node. + // + if ((RemainingDevicePath == NULL) || (!IsDevicePathEnd (RemainingDevicePath))) { + if (RemainingDevicePath == NULL) { + ZeroMem (&AcpiDeviceNode, sizeof (ACPI_ADR_DEVICE_PATH)); + AcpiDeviceNode.Header.Type = ACPI_DEVICE_PATH; + AcpiDeviceNode.Header.SubType = ACPI_ADR_DP; + AcpiDeviceNode.ADR = ACPI_DISPLAY_ADR (1, 0, 0, 1, 0, ACPI_ADR_DISPLAY_TYPE_VGA, 0, 0); + SetDevicePathNodeLength (&AcpiDeviceNode.Header, sizeof (ACPI_ADR_DEVICE_PATH)); + + BiosVideoPrivate->GopDevicePath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &AcpiDeviceNode + ); + } else { + BiosVideoPrivate->GopDevicePath = AppendDevicePathNode (ParentDevicePath, RemainingDevicePath); + } + + // + // Creat child handle and device path protocol firstly + // + BiosVideoPrivate->Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &BiosVideoPrivate->Handle, + &gEfiDevicePathProtocolGuid, + BiosVideoPrivate->GopDevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + goto Done; + } + } + + // + // Fill in the VGA Mini Port Protocol fields + // + BiosVideoPrivate->VgaMiniPort.SetMode = BiosVideoVgaMiniPortSetMode; + BiosVideoPrivate->VgaMiniPort.VgaMemoryOffset = 0xb8000; + BiosVideoPrivate->VgaMiniPort.CrtcAddressRegisterOffset = 0x3d4; + BiosVideoPrivate->VgaMiniPort.CrtcDataRegisterOffset = 0x3d5; + BiosVideoPrivate->VgaMiniPort.VgaMemoryBar = EFI_PCI_IO_PASS_THROUGH_BAR; + BiosVideoPrivate->VgaMiniPort.CrtcAddressRegisterBar = EFI_PCI_IO_PASS_THROUGH_BAR; + BiosVideoPrivate->VgaMiniPort.CrtcDataRegisterBar = EFI_PCI_IO_PASS_THROUGH_BAR; + + // + // Child handle need to consume the Legacy Bios protocol + // + BiosVideoPrivate->LegacyBios = ParentLegacyBios; + + // + // When check for VBE, PCI I/O protocol is needed, so use parent's protocol interface temporally + // + BiosVideoPrivate->PciIo = ParentPciIo; + BiosVideoPrivate->OriginalPciAttributes = OriginalPciAttributes; + + // + // Check for VESA BIOS Extensions for modes that are compatible with Graphics Output + // + if (FeaturePcdGet (PcdBiosVideoCheckVbeEnable)) { + Status = BiosVideoCheckForVbe (BiosVideoPrivate); + DEBUG ((EFI_D_INFO, "BiosVideoCheckForVbe - %r\n", Status)); + } else { + Status = EFI_UNSUPPORTED; + } + if (EFI_ERROR (Status)) { + // + // The VESA BIOS Extensions are not compatible with Graphics Output, so check for support + // for the standard 640x480 16 color VGA mode + // + DEBUG ((EFI_D_INFO, "VgaCompatible - %x\n", BiosVideoPrivate->VgaCompatible)); + if (BiosVideoPrivate->VgaCompatible) { + if (FeaturePcdGet (PcdBiosVideoCheckVgaEnable)) { + Status = BiosVideoCheckForVga (BiosVideoPrivate); + DEBUG ((EFI_D_INFO, "BiosVideoCheckForVga - %r\n", Status)); + } else { + Status = EFI_UNSUPPORTED; + } + } + + if (EFI_ERROR (Status)) { + // + // Neither VBE nor the standard 640x480 16 color VGA mode are supported, so do + // not produce the Graphics Output protocol. Instead, produce the VGA MiniPort Protocol. + // + BiosVideoPrivate->ProduceGraphicsOutput = FALSE; + + // + // INT services are available, so on the 80x25 and 80x50 text mode are supported + // + BiosVideoPrivate->VgaMiniPort.MaxMode = 2; + } + } + + ProtocolInstalled = FALSE; + + if (BiosVideoPrivate->ProduceGraphicsOutput) { + // + // Creat child handle and install Graphics Output Protocol,EDID Discovered/Active Protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &BiosVideoPrivate->Handle, + &gEfiGraphicsOutputProtocolGuid, + &BiosVideoPrivate->GraphicsOutput, + &gEfiEdidDiscoveredProtocolGuid, + &BiosVideoPrivate->EdidDiscovered, + &gEfiEdidActiveProtocolGuid, + &BiosVideoPrivate->EdidActive, + NULL + ); + + if (!EFI_ERROR (Status)) { + // + // Open the Parent Handle for the child + // + Status = gBS->OpenProtocol ( + ParentHandle, + &gEfiPciIoProtocolGuid, + (VOID **) &BiosVideoPrivate->PciIo, + This->DriverBindingHandle, + BiosVideoPrivate->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto Done; + } + ProtocolInstalled = TRUE; + } + } + + if (!ProtocolInstalled) { + // + // Install VGA Mini Port Protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ParentHandle, + &gEfiVgaMiniPortProtocolGuid, + &BiosVideoPrivate->VgaMiniPort, + NULL + ); + } + +Done: + if (EFI_ERROR (Status)) { + if ((BiosVideoPrivate != NULL) && (BiosVideoPrivate->ExitBootServicesEvent != NULL)) { + gBS->CloseEvent (BiosVideoPrivate->ExitBootServicesEvent); + } + // + // Free private data structure + // + BiosVideoDeviceReleaseResource (BiosVideoPrivate); + } + + return Status; +} + + +/** + Deregister an video child handle and free resources. + + @param This Protocol instance pointer. + @param Controller Video controller handle + @param Handle Video child handle + + @return EFI_STATUS + +**/ +EFI_STATUS +BiosVideoChildHandleUninstall ( + EFI_DRIVER_BINDING_PROTOCOL *This, + EFI_HANDLE Controller, + EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_IA32_REGISTER_SET Regs; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_VGA_MINI_PORT_PROTOCOL *VgaMiniPort; + BIOS_VIDEO_DEV *BiosVideoPrivate; + EFI_PCI_IO_PROTOCOL *PciIo; + + BiosVideoPrivate = NULL; + GraphicsOutput = NULL; + PciIo = NULL; + Status = EFI_UNSUPPORTED; + + Status = gBS->OpenProtocol ( + Handle, + &gEfiGraphicsOutputProtocolGuid, + (VOID **) &GraphicsOutput, + This->DriverBindingHandle, + Handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS (GraphicsOutput); + } + + if (EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + Handle, + &gEfiVgaMiniPortProtocolGuid, + (VOID **) &VgaMiniPort, + This->DriverBindingHandle, + Handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_VGA_MINI_PORT_THIS (VgaMiniPort); + } + } + + if (BiosVideoPrivate == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Set the 80x25 Text VGA Mode + // + Regs.H.AH = 0x00; + Regs.H.AL = 0x03; + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + + Regs.H.AH = 0x11; + Regs.H.AL = 0x14; + Regs.H.BL = 0; + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + + // + // Restore original PCI attributes + // + Status = BiosVideoPrivate->PciIo->Attributes ( + BiosVideoPrivate->PciIo, + EfiPciIoAttributeOperationSet, + BiosVideoPrivate->OriginalPciAttributes, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Close PCI I/O protocol that opened by child handle + // + Status = gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Handle + ); + + // + // Uninstall protocols on child handle + // + if (BiosVideoPrivate->ProduceGraphicsOutput) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + BiosVideoPrivate->Handle, + &gEfiDevicePathProtocolGuid, + BiosVideoPrivate->GopDevicePath, + &gEfiGraphicsOutputProtocolGuid, + &BiosVideoPrivate->GraphicsOutput, + &gEfiEdidDiscoveredProtocolGuid, + &BiosVideoPrivate->EdidDiscovered, + &gEfiEdidActiveProtocolGuid, + &BiosVideoPrivate->EdidActive, + NULL + ); + } + if (!BiosVideoPrivate->ProduceGraphicsOutput) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiVgaMiniPortProtocolGuid, + &BiosVideoPrivate->VgaMiniPort, + NULL + ); + } + + if (EFI_ERROR (Status)) { + gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + return Status; + } + + if (PcdGetBool (PcdBiosVideoSetTextVgaModeEnable)) { + // + // Close EXIT_BOOT_SERIVES Event + // + gBS->CloseEvent (BiosVideoPrivate->ExitBootServicesEvent); + } + + // + // Release all allocated resources + // + BiosVideoDeviceReleaseResource (BiosVideoPrivate); + + return EFI_SUCCESS; +} + + +/** + Release resource for biso video instance. + + @param BiosVideoPrivate Video child device private data structure + +**/ +VOID +BiosVideoDeviceReleaseResource ( + BIOS_VIDEO_DEV *BiosVideoPrivate + ) +{ + if (BiosVideoPrivate == NULL) { + return ; + } + + // + // Release all the resourses occupied by the BIOS_VIDEO_DEV + // + + // + // Free VGA Frame Buffer + // + if (BiosVideoPrivate->VgaFrameBuffer != NULL) { + FreePool (BiosVideoPrivate->VgaFrameBuffer); + } + // + // Free VBE Frame Buffer + // + if (BiosVideoPrivate->VbeFrameBuffer != NULL) { + FreePool (BiosVideoPrivate->VbeFrameBuffer); + } + // + // Free line buffer + // + if (BiosVideoPrivate->LineBuffer != NULL) { + FreePool (BiosVideoPrivate->LineBuffer); + } + // + // Free mode data + // + if (BiosVideoPrivate->ModeData != NULL) { + FreePool (BiosVideoPrivate->ModeData); + } + // + // Free memory allocated below 1MB + // + if (BiosVideoPrivate->PagesBelow1MB != 0) { + gBS->FreePages (BiosVideoPrivate->PagesBelow1MB, BiosVideoPrivate->NumberOfPagesBelow1MB); + } + + if (BiosVideoPrivate->VbeSaveRestorePages != 0) { + gBS->FreePages (BiosVideoPrivate->VbeSaveRestoreBuffer, BiosVideoPrivate->VbeSaveRestorePages); + } + + // + // Free graphics output protocol occupied resource + // + if (BiosVideoPrivate->GraphicsOutput.Mode != NULL) { + if (BiosVideoPrivate->GraphicsOutput.Mode->Info != NULL) { + FreePool (BiosVideoPrivate->GraphicsOutput.Mode->Info); + } + FreePool (BiosVideoPrivate->GraphicsOutput.Mode); + } + // + // Free EDID discovered protocol occupied resource + // + if (BiosVideoPrivate->EdidDiscovered.Edid != NULL) { + FreePool (BiosVideoPrivate->EdidDiscovered.Edid); + } + // + // Free EDID active protocol occupied resource + // + if (BiosVideoPrivate->EdidActive.Edid != NULL) { + FreePool (BiosVideoPrivate->EdidActive.Edid); + } + + if (BiosVideoPrivate->GopDevicePath!= NULL) { + FreePool (BiosVideoPrivate->GopDevicePath); + } + + FreePool (BiosVideoPrivate); + + return ; +} + + +/** + Generate a search key for a specified timing data. + + @param EdidTiming Pointer to EDID timing + + @return The 32 bit unique key for search. + +**/ +UINT32 +CalculateEdidKey ( + VESA_BIOS_EXTENSIONS_EDID_TIMING *EdidTiming + ) +{ + UINT32 Key; + + // + // Be sure no conflicts for all standard timing defined by VESA. + // + Key = (EdidTiming->HorizontalResolution * 2) + EdidTiming->VerticalResolution; + return Key; +} + + +/** + Parse the Established Timing and Standard Timing in EDID data block. + + @param EdidBuffer Pointer to EDID data block + @param ValidEdidTiming Valid EDID timing information + + @retval TRUE The EDID data is valid. + @retval FALSE The EDID data is invalid. + +**/ +BOOLEAN +ParseEdidData ( + UINT8 *EdidBuffer, + VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING *ValidEdidTiming + ) +{ + UINT8 CheckSum; + UINT32 Index; + UINT32 ValidNumber; + UINT32 TimingBits; + UINT8 *BufferIndex; + UINT16 HorizontalResolution; + UINT16 VerticalResolution; + UINT8 AspectRatio; + UINT8 RefreshRate; + VESA_BIOS_EXTENSIONS_EDID_TIMING TempTiming; + VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK *EdidDataBlock; + + EdidDataBlock = (VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK *) EdidBuffer; + + // + // Check the checksum of EDID data + // + CheckSum = 0; + for (Index = 0; Index < VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE; Index ++) { + CheckSum = (UINT8) (CheckSum + EdidBuffer[Index]); + } + if (CheckSum != 0) { + return FALSE; + } + + ValidNumber = 0; + gBS->SetMem (ValidEdidTiming, sizeof (VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING), 0); + + if ((EdidDataBlock->EstablishedTimings[0] != 0) || + (EdidDataBlock->EstablishedTimings[1] != 0) || + (EdidDataBlock->EstablishedTimings[2] != 0) + ) { + // + // Established timing data + // + TimingBits = EdidDataBlock->EstablishedTimings[0] | + (EdidDataBlock->EstablishedTimings[1] << 8) | + ((EdidDataBlock->EstablishedTimings[2] & 0x80) << 9) ; + for (Index = 0; Index < VESA_BIOS_EXTENSIONS_EDID_ESTABLISHED_TIMING_MAX_NUMBER; Index ++) { + if ((TimingBits & 0x1) != 0) { + ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&mEstablishedEdidTiming[Index]); + ValidNumber ++; + } + TimingBits = TimingBits >> 1; + } + } else { + // + // If no Established timing data, read the standard timing data + // + BufferIndex = &EdidDataBlock->StandardTimingIdentification[0]; + for (Index = 0; Index < 8; Index ++) { + if ((BufferIndex[0] != 0x1) && (BufferIndex[1] != 0x1)){ + // + // A valid Standard Timing + // + HorizontalResolution = (UINT16) (BufferIndex[0] * 8 + 248); + AspectRatio = (UINT8) (BufferIndex[1] >> 6); + switch (AspectRatio) { + case 0: + VerticalResolution = (UINT16) (HorizontalResolution / 16 * 10); + break; + case 1: + VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3); + break; + case 2: + VerticalResolution = (UINT16) (HorizontalResolution / 5 * 4); + break; + case 3: + VerticalResolution = (UINT16) (HorizontalResolution / 16 * 9); + break; + default: + VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3); + break; + } + RefreshRate = (UINT8) ((BufferIndex[1] & 0x1f) + 60); + TempTiming.HorizontalResolution = HorizontalResolution; + TempTiming.VerticalResolution = VerticalResolution; + TempTiming.RefreshRate = RefreshRate; + ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&TempTiming); + ValidNumber ++; + } + BufferIndex += 2; + } + } + + ValidEdidTiming->ValidNumber = ValidNumber; + return TRUE; +} + + +/** + Search a specified Timing in all the valid EDID timings. + + @param ValidEdidTiming All valid EDID timing information. + @param EdidTiming The Timing to search for. + + @retval TRUE Found. + @retval FALSE Not found. + +**/ +BOOLEAN +SearchEdidTiming ( + VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING *ValidEdidTiming, + VESA_BIOS_EXTENSIONS_EDID_TIMING *EdidTiming + ) +{ + UINT32 Index; + UINT32 Key; + + Key = CalculateEdidKey (EdidTiming); + + for (Index = 0; Index < ValidEdidTiming->ValidNumber; Index ++) { + if (Key == ValidEdidTiming->Key[Index]) { + return TRUE; + } + } + + return FALSE; +} + + +/** + Check for VBE device. + + @param BiosVideoPrivate Pointer to BIOS_VIDEO_DEV structure + + @retval EFI_SUCCESS VBE device found + +**/ +EFI_STATUS +BiosVideoCheckForVbe ( + IN OUT BIOS_VIDEO_DEV *BiosVideoPrivate + ) +{ + EFI_STATUS Status; + EFI_IA32_REGISTER_SET Regs; + UINT16 *ModeNumberPtr; + BOOLEAN ModeFound; + BOOLEAN EdidFound; + BIOS_VIDEO_MODE_DATA *ModeBuffer; + BIOS_VIDEO_MODE_DATA *CurrentModeData; + UINTN PreferMode; + UINTN ModeNumber; + VESA_BIOS_EXTENSIONS_EDID_TIMING Timing; + VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING ValidEdidTiming; + EFI_EDID_OVERRIDE_PROTOCOL *EdidOverride; + UINT32 EdidAttributes; + BOOLEAN EdidOverrideFound; + UINTN EdidOverrideDataSize; + UINT8 *EdidOverrideDataBlock; + UINTN EdidActiveDataSize; + UINT8 *EdidActiveDataBlock; + + EdidFound = TRUE; + EdidOverrideFound = FALSE; + EdidOverrideDataBlock = NULL; + EdidActiveDataSize = 0; + EdidActiveDataBlock = NULL; + + // + // Allocate buffer under 1MB for VBE data structures + // + BiosVideoPrivate->NumberOfPagesBelow1MB = EFI_SIZE_TO_PAGES ( + sizeof (VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK) + + sizeof (VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK) + + sizeof (VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK) + + sizeof (VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK) + ); + + BiosVideoPrivate->PagesBelow1MB = 0x00100000 - 1; + + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiBootServicesData, + BiosVideoPrivate->NumberOfPagesBelow1MB, + &BiosVideoPrivate->PagesBelow1MB + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ZeroMem (&ValidEdidTiming, sizeof (VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING)); + + // + // Fill in the VBE related data structures + // + BiosVideoPrivate->VbeInformationBlock = (VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK *) (UINTN) (BiosVideoPrivate->PagesBelow1MB); + BiosVideoPrivate->VbeModeInformationBlock = (VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK *) (BiosVideoPrivate->VbeInformationBlock + 1); + BiosVideoPrivate->VbeEdidDataBlock = (VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK *) (BiosVideoPrivate->VbeModeInformationBlock + 1); + BiosVideoPrivate->VbeCrtcInformationBlock = (VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK *) (BiosVideoPrivate->VbeEdidDataBlock + 1); + BiosVideoPrivate->VbeSaveRestorePages = 0; + BiosVideoPrivate->VbeSaveRestoreBuffer = 0; + + // + // Test to see if the Video Adapter is compliant with VBE 3.0 + // + gBS->SetMem (&Regs, sizeof (Regs), 0); + Regs.X.AX = VESA_BIOS_EXTENSIONS_RETURN_CONTROLLER_INFORMATION; + gBS->SetMem (BiosVideoPrivate->VbeInformationBlock, sizeof (VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK), 0); + BiosVideoPrivate->VbeInformationBlock->VESASignature = VESA_BIOS_EXTENSIONS_VBE2_SIGNATURE; + Regs.X.ES = EFI_SEGMENT ((UINTN) BiosVideoPrivate->VbeInformationBlock); + Regs.X.DI = EFI_OFFSET ((UINTN) BiosVideoPrivate->VbeInformationBlock); + + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + + Status = EFI_DEVICE_ERROR; + + // + // See if the VESA call succeeded + // + if (Regs.X.AX != VESA_BIOS_EXTENSIONS_STATUS_SUCCESS) { + return Status; + } + // + // Check for 'VESA' signature + // + if (BiosVideoPrivate->VbeInformationBlock->VESASignature != VESA_BIOS_EXTENSIONS_VESA_SIGNATURE) { + return Status; + } + // + // Check to see if this is VBE 2.0 or higher + // + if (BiosVideoPrivate->VbeInformationBlock->VESAVersion < VESA_BIOS_EXTENSIONS_VERSION_2_0) { + return Status; + } + + EdidFound = FALSE; + EdidAttributes = 0xff; + EdidOverrideDataSize = 0; + + // + // Find EDID Override protocol firstly, this protocol is installed by platform if needed. + // + Status = gBS->LocateProtocol ( + &gEfiEdidOverrideProtocolGuid, + NULL, + (VOID **) &EdidOverride + ); + if (!EFI_ERROR (Status)) { + // + // Allocate double size of VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE to avoid overflow + // + EdidOverrideDataBlock = AllocatePool (sizeof (VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE * 2)); + if (NULL == EdidOverrideDataBlock) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Status = EdidOverride->GetEdid ( + EdidOverride, + BiosVideoPrivate->Handle, + &EdidAttributes, + &EdidOverrideDataSize, + (UINT8 **) &EdidOverrideDataBlock + ); + if (!EFI_ERROR (Status) && + EdidAttributes == 0 && + EdidOverrideDataSize != 0) { + // + // Succeeded to get EDID Override Data + // + EdidOverrideFound = TRUE; + } + } + + if (!EdidOverrideFound || EdidAttributes == EFI_EDID_OVERRIDE_DONT_OVERRIDE) { + // + // If EDID Override data doesn't exist or EFI_EDID_OVERRIDE_DONT_OVERRIDE returned, + // read EDID information through INT10 call + // + + gBS->SetMem (&Regs, sizeof (Regs), 0); + Regs.X.AX = VESA_BIOS_EXTENSIONS_EDID; + Regs.X.BX = 1; + Regs.X.CX = 0; + Regs.X.DX = 0; + Regs.X.ES = EFI_SEGMENT ((UINTN) BiosVideoPrivate->VbeEdidDataBlock); + Regs.X.DI = EFI_OFFSET ((UINTN) BiosVideoPrivate->VbeEdidDataBlock); + + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + // + // See if the VESA call succeeded + // + if (Regs.X.AX == VESA_BIOS_EXTENSIONS_STATUS_SUCCESS) { + // + // Set EDID Discovered Data + // + BiosVideoPrivate->EdidDiscovered.SizeOfEdid = VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE; + BiosVideoPrivate->EdidDiscovered.Edid = (UINT8 *) AllocateCopyPool ( + VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE, + BiosVideoPrivate->VbeEdidDataBlock + ); + + if (NULL == BiosVideoPrivate->EdidDiscovered.Edid) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + EdidFound = TRUE; + } + } + + if (EdidFound) { + EdidActiveDataSize = VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE; + EdidActiveDataBlock = BiosVideoPrivate->EdidDiscovered.Edid; + } else if (EdidOverrideFound) { + EdidActiveDataSize = EdidOverrideDataSize; + EdidActiveDataBlock = EdidOverrideDataBlock; + EdidFound = TRUE; + } + + if (EdidFound) { + // + // Parse EDID data structure to retrieve modes supported by monitor + // + if (ParseEdidData ((UINT8 *) EdidActiveDataBlock, &ValidEdidTiming)) { + // + // Copy EDID Override Data to EDID Active Data + // + BiosVideoPrivate->EdidActive.SizeOfEdid = (UINT32) EdidActiveDataSize; + BiosVideoPrivate->EdidActive.Edid = (UINT8 *) AllocateCopyPool ( + EdidActiveDataSize, + EdidActiveDataBlock + ); + if (NULL == BiosVideoPrivate->EdidActive.Edid) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + } + } else { + BiosVideoPrivate->EdidActive.SizeOfEdid = 0; + BiosVideoPrivate->EdidActive.Edid = NULL; + EdidFound = FALSE; + } + + // + // Walk through the mode list to see if there is at least one mode the is compatible with the EDID mode + // + ModeNumberPtr = (UINT16 *) + ( + (((UINTN) BiosVideoPrivate->VbeInformationBlock->VideoModePtr & 0xffff0000) >> 12) | + ((UINTN) BiosVideoPrivate->VbeInformationBlock->VideoModePtr & 0x0000ffff) + ); + + PreferMode = 0; + ModeNumber = 0; + + for (; *ModeNumberPtr != VESA_BIOS_EXTENSIONS_END_OF_MODE_LIST; ModeNumberPtr++) { + // + // Make sure this is a mode number defined by the VESA VBE specification. If it isn'tm then skip this mode number. + // + if ((*ModeNumberPtr & VESA_BIOS_EXTENSIONS_MODE_NUMBER_VESA) == 0) { + continue; + } + // + // Get the information about the mode + // + gBS->SetMem (&Regs, sizeof (Regs), 0); + Regs.X.AX = VESA_BIOS_EXTENSIONS_RETURN_MODE_INFORMATION; + Regs.X.CX = *ModeNumberPtr; + gBS->SetMem (BiosVideoPrivate->VbeModeInformationBlock, sizeof (VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK), 0); + Regs.X.ES = EFI_SEGMENT ((UINTN) BiosVideoPrivate->VbeModeInformationBlock); + Regs.X.DI = EFI_OFFSET ((UINTN) BiosVideoPrivate->VbeModeInformationBlock); + + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + + // + // See if the call succeeded. If it didn't, then try the next mode. + // + if (Regs.X.AX != VESA_BIOS_EXTENSIONS_STATUS_SUCCESS) { + continue; + } + // + // See if the mode supports color. If it doesn't then try the next mode. + // + if ((BiosVideoPrivate->VbeModeInformationBlock->ModeAttributes & VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_COLOR) == 0) { + continue; + } + // + // See if the mode supports graphics. If it doesn't then try the next mode. + // + if ((BiosVideoPrivate->VbeModeInformationBlock->ModeAttributes & VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_GRAPHICS) == 0) { + continue; + } + // + // See if the mode supports a linear frame buffer. If it doesn't then try the next mode. + // + if ((BiosVideoPrivate->VbeModeInformationBlock->ModeAttributes & VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER) == 0) { + continue; + } + // + // See if the mode supports 32 bit color. If it doesn't then try the next mode. + // 32 bit mode can be implemented by 24 Bits Per Pixels. Also make sure the + // number of bits per pixel is a multiple of 8 or more than 32 bits per pixel + // + if (BiosVideoPrivate->VbeModeInformationBlock->BitsPerPixel < 24) { + continue; + } + + if (BiosVideoPrivate->VbeModeInformationBlock->BitsPerPixel > 32) { + continue; + } + + if ((BiosVideoPrivate->VbeModeInformationBlock->BitsPerPixel % 8) != 0) { + continue; + } + // + // See if the physical base pointer for the linear mode is valid. If it isn't then try the next mode. + // + if (BiosVideoPrivate->VbeModeInformationBlock->PhysBasePtr == 0) { + continue; + } + + if (EdidFound && (ValidEdidTiming.ValidNumber > 0)) { + // + // EDID exist, check whether this mode match with any mode in EDID + // + Timing.HorizontalResolution = BiosVideoPrivate->VbeModeInformationBlock->XResolution; + Timing.VerticalResolution = BiosVideoPrivate->VbeModeInformationBlock->YResolution; + if (!SearchEdidTiming (&ValidEdidTiming, &Timing)) { + continue; + } + } + + // + // Select a reasonable mode to be set for current display mode + // + ModeFound = FALSE; + + if (BiosVideoPrivate->VbeModeInformationBlock->XResolution == 1024 && + BiosVideoPrivate->VbeModeInformationBlock->YResolution == 768 + ) { + ModeFound = TRUE; + } + if (BiosVideoPrivate->VbeModeInformationBlock->XResolution == 800 && + BiosVideoPrivate->VbeModeInformationBlock->YResolution == 600 + ) { + ModeFound = TRUE; + PreferMode = ModeNumber; + } + if (BiosVideoPrivate->VbeModeInformationBlock->XResolution == 640 && + BiosVideoPrivate->VbeModeInformationBlock->YResolution == 480 + ) { + ModeFound = TRUE; + } + + if ((!EdidFound) && (!ModeFound)) { + // + // When no EDID exist, only select three possible resolutions, i.e. 1024x768, 800x600, 640x480 + // + continue; + } + + // + // Add mode to the list of available modes + // + ModeNumber ++; + ModeBuffer = (BIOS_VIDEO_MODE_DATA *) AllocatePool ( + ModeNumber * sizeof (BIOS_VIDEO_MODE_DATA) + ); + if (NULL == ModeBuffer) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + if (ModeNumber > 1) { + CopyMem ( + ModeBuffer, + BiosVideoPrivate->ModeData, + (ModeNumber - 1) * sizeof (BIOS_VIDEO_MODE_DATA) + ); + } + + if (BiosVideoPrivate->ModeData != NULL) { + FreePool (BiosVideoPrivate->ModeData); + } + + CurrentModeData = &ModeBuffer[ModeNumber - 1]; + CurrentModeData->VbeModeNumber = *ModeNumberPtr; + if (BiosVideoPrivate->VbeInformationBlock->VESAVersion >= VESA_BIOS_EXTENSIONS_VERSION_3_0) { + CurrentModeData->BytesPerScanLine = BiosVideoPrivate->VbeModeInformationBlock->LinBytesPerScanLine; + CurrentModeData->Red.Position = BiosVideoPrivate->VbeModeInformationBlock->LinRedFieldPosition; + CurrentModeData->Red.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->LinRedMaskSize) - 1); + CurrentModeData->Blue.Position = BiosVideoPrivate->VbeModeInformationBlock->LinBlueFieldPosition; + CurrentModeData->Blue.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->LinBlueMaskSize) - 1); + CurrentModeData->Green.Position = BiosVideoPrivate->VbeModeInformationBlock->LinGreenFieldPosition; + CurrentModeData->Green.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->LinGreenMaskSize) - 1); + CurrentModeData->Reserved.Position = BiosVideoPrivate->VbeModeInformationBlock->LinRsvdFieldPosition; + CurrentModeData->Reserved.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->LinRsvdMaskSize) - 1); + } else { + CurrentModeData->BytesPerScanLine = BiosVideoPrivate->VbeModeInformationBlock->BytesPerScanLine; + CurrentModeData->Red.Position = BiosVideoPrivate->VbeModeInformationBlock->RedFieldPosition; + CurrentModeData->Red.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->RedMaskSize) - 1); + CurrentModeData->Blue.Position = BiosVideoPrivate->VbeModeInformationBlock->BlueFieldPosition; + CurrentModeData->Blue.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->BlueMaskSize) - 1); + CurrentModeData->Green.Position = BiosVideoPrivate->VbeModeInformationBlock->GreenFieldPosition; + CurrentModeData->Green.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->GreenMaskSize) - 1); + CurrentModeData->Reserved.Position = BiosVideoPrivate->VbeModeInformationBlock->RsvdFieldPosition; + CurrentModeData->Reserved.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->RsvdMaskSize) - 1); + } + + CurrentModeData->PixelFormat = PixelBitMask; + if ((BiosVideoPrivate->VbeModeInformationBlock->BitsPerPixel == 32) && + (CurrentModeData->Red.Mask == 0xff) && (CurrentModeData->Green.Mask == 0xff) && (CurrentModeData->Blue.Mask == 0xff)) { + if ((CurrentModeData->Red.Position == 0) && (CurrentModeData->Green.Position == 8) && (CurrentModeData->Blue.Position == 16)) { + CurrentModeData->PixelFormat = PixelRedGreenBlueReserved8BitPerColor; + } else if ((CurrentModeData->Blue.Position == 0) && (CurrentModeData->Green.Position == 8) && (CurrentModeData->Red.Position == 16)) { + CurrentModeData->PixelFormat = PixelBlueGreenRedReserved8BitPerColor; + } + } + + CurrentModeData->PixelBitMask.RedMask = ((UINT32) CurrentModeData->Red.Mask) << CurrentModeData->Red.Position; + CurrentModeData->PixelBitMask.GreenMask = ((UINT32) CurrentModeData->Green.Mask) << CurrentModeData->Green.Position; + CurrentModeData->PixelBitMask.BlueMask = ((UINT32) CurrentModeData->Blue.Mask) << CurrentModeData->Blue.Position; + CurrentModeData->PixelBitMask.ReservedMask = ((UINT32) CurrentModeData->Reserved.Mask) << CurrentModeData->Reserved.Position; + CurrentModeData->FrameBufferSize = BiosVideoPrivate->VbeInformationBlock->TotalMemory * 64 * 1024; + + CurrentModeData->LinearFrameBuffer = (VOID *) (UINTN)BiosVideoPrivate->VbeModeInformationBlock->PhysBasePtr; + CurrentModeData->HorizontalResolution = BiosVideoPrivate->VbeModeInformationBlock->XResolution; + CurrentModeData->VerticalResolution = BiosVideoPrivate->VbeModeInformationBlock->YResolution; + + CurrentModeData->BitsPerPixel = BiosVideoPrivate->VbeModeInformationBlock->BitsPerPixel; + + BiosVideoPrivate->ModeData = ModeBuffer; + } + // + // Check to see if we found any modes that are compatible with GRAPHICS OUTPUT + // + if (ModeNumber == 0) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + // + // Assign Gop's Blt function + // + BiosVideoPrivate->GraphicsOutput.Blt = BiosVideoGraphicsOutputVbeBlt; + + BiosVideoPrivate->GraphicsOutput.Mode->MaxMode = (UINT32) ModeNumber; + // + // Current mode is unknow till now, set it to an invalid mode. + // + BiosVideoPrivate->GraphicsOutput.Mode->Mode = GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER; + + // + // Find the best mode to initialize + // + Status = BiosVideoGraphicsOutputSetMode (&BiosVideoPrivate->GraphicsOutput, (UINT32) PreferMode); + if (EFI_ERROR (Status)) { + for (PreferMode = 0; PreferMode < ModeNumber; PreferMode ++) { + Status = BiosVideoGraphicsOutputSetMode ( + &BiosVideoPrivate->GraphicsOutput, + (UINT32) PreferMode + ); + if (!EFI_ERROR (Status)) { + break; + } + } + if (PreferMode == ModeNumber) { + // + // None mode is set successfully. + // + goto Done; + } + } + +Done: + // + // If there was an error, then free the mode structure + // + if (EFI_ERROR (Status)) { + if (BiosVideoPrivate->ModeData != NULL) { + FreePool (BiosVideoPrivate->ModeData); + BiosVideoPrivate->ModeData = NULL; + BiosVideoPrivate->MaxMode = 0; + } + if (BiosVideoPrivate->GraphicsOutput.Mode != NULL) { + if (BiosVideoPrivate->GraphicsOutput.Mode->Info != NULL) { + FreePool (BiosVideoPrivate->GraphicsOutput.Mode->Info); + BiosVideoPrivate->GraphicsOutput.Mode->Info = NULL; + } + FreePool (BiosVideoPrivate->GraphicsOutput.Mode); + BiosVideoPrivate->GraphicsOutput.Mode= NULL; + } + if (EdidOverrideDataBlock != NULL) { + FreePool (EdidOverrideDataBlock); + } + } + + return Status; +} + + +/** + Check for VGA device. + + @param BiosVideoPrivate Pointer to BIOS_VIDEO_DEV structure + + @retval EFI_SUCCESS Standard VGA device found + +**/ +EFI_STATUS +BiosVideoCheckForVga ( + IN OUT BIOS_VIDEO_DEV *BiosVideoPrivate + ) +{ + EFI_STATUS Status; + BIOS_VIDEO_MODE_DATA *ModeBuffer; + + Status = EFI_UNSUPPORTED; + + // + // Assign Gop's Blt function + // + BiosVideoPrivate->GraphicsOutput.Blt = BiosVideoGraphicsOutputVgaBlt; + + // + // Add mode to the list of available modes + // caller should guarantee that Mode has been allocated. + // + ASSERT (BiosVideoPrivate->GraphicsOutput.Mode != NULL); + BiosVideoPrivate->GraphicsOutput.Mode->MaxMode = 1; + + ModeBuffer = (BIOS_VIDEO_MODE_DATA *) AllocatePool ( + sizeof (BIOS_VIDEO_MODE_DATA) + ); + if (NULL == ModeBuffer) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + ModeBuffer->VbeModeNumber = 0x0012; + ModeBuffer->BytesPerScanLine = 640; + ModeBuffer->LinearFrameBuffer = (VOID *) (UINTN) (0xa0000); + ModeBuffer->HorizontalResolution = 640; + ModeBuffer->VerticalResolution = 480; + ModeBuffer->PixelFormat = PixelBltOnly; + ModeBuffer->BitsPerPixel = 8; + ModeBuffer->ColorDepth = 32; + ModeBuffer->RefreshRate = 60; + + BiosVideoPrivate->ModeData = ModeBuffer; + + // + // Test to see if the Video Adapter support the 640x480 16 color mode + // + BiosVideoPrivate->GraphicsOutput.Mode->Mode = GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER; + Status = BiosVideoGraphicsOutputSetMode (&BiosVideoPrivate->GraphicsOutput, 0); + +Done: + // + // If there was an error, then free the mode structure + // + if (EFI_ERROR (Status)) { + if (BiosVideoPrivate->ModeData != NULL) { + FreePool (BiosVideoPrivate->ModeData); + BiosVideoPrivate->ModeData = NULL; + } + if (BiosVideoPrivate->GraphicsOutput.Mode != NULL) { + if (BiosVideoPrivate->GraphicsOutput.Mode->Info != NULL) { + FreePool (BiosVideoPrivate->GraphicsOutput.Mode->Info); + BiosVideoPrivate->GraphicsOutput.Mode->Info = NULL; + } + FreePool (BiosVideoPrivate->GraphicsOutput.Mode); + BiosVideoPrivate->GraphicsOutput.Mode = NULL; + } + } + return Status; +} + +// +// Graphics Output Protocol Member Functions for VESA BIOS Extensions +// + +/** + Graphics Output protocol interface to get video mode. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to return information on. + @param SizeOfInfo A pointer to the size, in bytes, of the Info + buffer. + @param Info Caller allocated buffer that returns information + about ModeNumber. + + @retval EFI_SUCCESS Mode information returned. + @retval EFI_DEVICE_ERROR A hardware error occurred trying to retrieve the + video mode. + @retval EFI_NOT_STARTED Video display is not initialized. Call SetMode () + @retval EFI_INVALID_PARAMETER One of the input args was NULL. + +**/ +EFI_STATUS +EFIAPI +BiosVideoGraphicsOutputQueryMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber, + OUT UINTN *SizeOfInfo, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info + ) +{ + BIOS_VIDEO_DEV *BiosVideoPrivate; + BIOS_VIDEO_MODE_DATA *ModeData; + + BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS (This); + + if (BiosVideoPrivate->HardwareNeedsStarting) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_OUTPUT_ERROR, + BiosVideoPrivate->GopDevicePath + ); + return EFI_NOT_STARTED; + } + + if (This == NULL || Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) { + return EFI_INVALID_PARAMETER; + } + + *Info = (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *) AllocatePool ( + sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION) + ); + if (NULL == *Info) { + return EFI_OUT_OF_RESOURCES; + } + + *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + + ModeData = &BiosVideoPrivate->ModeData[ModeNumber]; + (*Info)->Version = 0; + (*Info)->HorizontalResolution = ModeData->HorizontalResolution; + (*Info)->VerticalResolution = ModeData->VerticalResolution; + (*Info)->PixelFormat = ModeData->PixelFormat; + CopyMem (&((*Info)->PixelInformation), &(ModeData->PixelBitMask), sizeof(ModeData->PixelBitMask)); + + (*Info)->PixelsPerScanLine = (ModeData->BytesPerScanLine * 8) / ModeData->BitsPerPixel; + + return EFI_SUCCESS; +} + +/** + Worker function to set video mode. + + @param BiosVideoPrivate Instance of BIOS_VIDEO_DEV. + @param ModeData The mode data to be set. + @param DevicePath Pointer to Device Path Protocol. + + @retval EFI_SUCCESS Graphics mode was changed. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the + request. + @retval EFI_UNSUPPORTED ModeNumber is not supported by this device. + +**/ +EFI_STATUS +BiosVideoSetModeWorker ( + IN BIOS_VIDEO_DEV *BiosVideoPrivate, + IN BIOS_VIDEO_MODE_DATA *ModeData, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_STATUS Status; + EFI_IA32_REGISTER_SET Regs; + + if (BiosVideoPrivate->LineBuffer != NULL) { + FreePool (BiosVideoPrivate->LineBuffer); + } + + if (BiosVideoPrivate->VgaFrameBuffer != NULL) { + FreePool (BiosVideoPrivate->VgaFrameBuffer); + } + + if (BiosVideoPrivate->VbeFrameBuffer != NULL) { + FreePool (BiosVideoPrivate->VbeFrameBuffer); + } + + BiosVideoPrivate->LineBuffer = (UINT8 *) AllocatePool ( + ModeData->BytesPerScanLine + ); + if (NULL == BiosVideoPrivate->LineBuffer) { + return EFI_OUT_OF_RESOURCES; + } + // + // Clear all registers + // + ZeroMem (&Regs, sizeof (Regs)); + + if (ModeData->VbeModeNumber < 0x100) { + // + // Allocate a working buffer for BLT operations to the VGA frame buffer + // + BiosVideoPrivate->VgaFrameBuffer = (UINT8 *) AllocatePool (4 * 480 * 80); + if (NULL == BiosVideoPrivate->VgaFrameBuffer) { + return EFI_OUT_OF_RESOURCES; + } + // + // Set VGA Mode + // + Regs.X.AX = ModeData->VbeModeNumber; + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + + } else { + // + // Allocate a working buffer for BLT operations to the VBE frame buffer + // + BiosVideoPrivate->VbeFrameBuffer = + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) AllocatePool ( + ModeData->BytesPerScanLine * ModeData->VerticalResolution + ); + if (NULL == BiosVideoPrivate->VbeFrameBuffer) { + return EFI_OUT_OF_RESOURCES; + } + // + // Set VBE mode + // + Regs.X.AX = VESA_BIOS_EXTENSIONS_SET_MODE; + Regs.X.BX = (UINT16) (ModeData->VbeModeNumber | VESA_BIOS_EXTENSIONS_MODE_NUMBER_LINEAR_FRAME_BUFFER); + ZeroMem (BiosVideoPrivate->VbeCrtcInformationBlock, sizeof (VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK)); + Regs.X.ES = EFI_SEGMENT ((UINTN) BiosVideoPrivate->VbeCrtcInformationBlock); + Regs.X.DI = EFI_OFFSET ((UINTN) BiosVideoPrivate->VbeCrtcInformationBlock); + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + + // + // Check to see if the call succeeded + // + if (Regs.X.AX != VESA_BIOS_EXTENSIONS_STATUS_SUCCESS) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_OUTPUT_ERROR, + DevicePath + ); + return EFI_DEVICE_ERROR; + } + // + // Initialize the state of the VbeFrameBuffer + // + Status = BiosVideoPrivate->PciIo->Mem.Read ( + BiosVideoPrivate->PciIo, + EfiPciIoWidthUint32, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) (UINTN) ModeData->LinearFrameBuffer, + (ModeData->BytesPerScanLine * ModeData->VerticalResolution) >> 2, + BiosVideoPrivate->VbeFrameBuffer + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + Graphics Output protocol interface to set video mode. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to be set. + + @retval EFI_SUCCESS Graphics mode was changed. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the + request. + @retval EFI_UNSUPPORTED ModeNumber is not supported by this device. + +**/ +EFI_STATUS +EFIAPI +BiosVideoGraphicsOutputSetMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL * This, + IN UINT32 ModeNumber + ) +{ + EFI_STATUS Status; + BIOS_VIDEO_DEV *BiosVideoPrivate; + BIOS_VIDEO_MODE_DATA *ModeData; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS (This); + + ModeData = &BiosVideoPrivate->ModeData[ModeNumber]; + + if (ModeNumber >= This->Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + + if (ModeNumber == This->Mode->Mode) { + // + // Clear screen to black + // + ZeroMem (&Background, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + BiosVideoGraphicsOutputVbeBlt ( + This, + &Background, + EfiBltVideoFill, + 0, + 0, + 0, + 0, + ModeData->HorizontalResolution, + ModeData->VerticalResolution, + 0 + ); + return EFI_SUCCESS; + } + + Status = BiosVideoSetModeWorker (BiosVideoPrivate, ModeData, BiosVideoPrivate->GopDevicePath); + if (EFI_ERROR (Status)) { + return Status; + } + + This->Mode->Mode = ModeNumber; + This->Mode->Info->Version = 0; + This->Mode->Info->HorizontalResolution = ModeData->HorizontalResolution; + This->Mode->Info->VerticalResolution = ModeData->VerticalResolution; + This->Mode->Info->PixelFormat = ModeData->PixelFormat; + CopyMem (&(This->Mode->Info->PixelInformation), &(ModeData->PixelBitMask), sizeof (ModeData->PixelBitMask)); + This->Mode->Info->PixelsPerScanLine = (ModeData->BytesPerScanLine * 8) / ModeData->BitsPerPixel; + This->Mode->SizeOfInfo = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + + // + // Frame BufferSize remain unchanged + // + This->Mode->FrameBufferBase = (EFI_PHYSICAL_ADDRESS) (UINTN) ModeData->LinearFrameBuffer; + This->Mode->FrameBufferSize = ModeData->FrameBufferSize; + + BiosVideoPrivate->HardwareNeedsStarting = FALSE; + + return EFI_SUCCESS; +} + +/** + Update physical frame buffer, copy 4 bytes block, then copy remaining bytes. + + @param PciIo The pointer of EFI_PCI_IO_PROTOCOL + @param VbeBuffer The data to transfer to screen + @param MemAddress Physical frame buffer base address + @param DestinationX The X coordinate of the destination for BltOperation + @param DestinationY The Y coordinate of the destination for BltOperation + @param TotalBytes The total bytes of copy + @param VbePixelWidth Bytes per pixel + @param BytesPerScanLine Bytes per scan line + +**/ +VOID +CopyVideoBuffer ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 *VbeBuffer, + IN VOID *MemAddress, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN TotalBytes, + IN UINT32 VbePixelWidth, + IN UINTN BytesPerScanLine + ) +{ + UINTN FrameBufferAddr; + UINTN CopyBlockNum; + UINTN RemainingBytes; + UINTN UnalignedBytes; + EFI_STATUS Status; + + FrameBufferAddr = (UINTN) MemAddress + (DestinationY * BytesPerScanLine) + DestinationX * VbePixelWidth; + + // + // If TotalBytes is less than 4 bytes, only start byte copy. + // + if (TotalBytes < 4) { + Status = PciIo->Mem.Write ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) FrameBufferAddr, + TotalBytes, + VbeBuffer + ); + ASSERT_EFI_ERROR (Status); + return; + } + + // + // If VbeBuffer is not 4-byte aligned, start byte copy. + // + UnalignedBytes = (4 - ((UINTN) VbeBuffer & 0x3)) & 0x3; + + if (UnalignedBytes != 0) { + Status = PciIo->Mem.Write ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) FrameBufferAddr, + UnalignedBytes, + VbeBuffer + ); + ASSERT_EFI_ERROR (Status); + FrameBufferAddr += UnalignedBytes; + VbeBuffer += UnalignedBytes; + } + + // + // Calculate 4-byte block count and remaining bytes. + // + CopyBlockNum = (TotalBytes - UnalignedBytes) >> 2; + RemainingBytes = (TotalBytes - UnalignedBytes) & 3; + + // + // Copy 4-byte block and remaining bytes to physical frame buffer. + // + if (CopyBlockNum != 0) { + Status = PciIo->Mem.Write ( + PciIo, + EfiPciIoWidthUint32, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) FrameBufferAddr, + CopyBlockNum, + VbeBuffer + ); + ASSERT_EFI_ERROR (Status); + } + + if (RemainingBytes != 0) { + FrameBufferAddr += (CopyBlockNum << 2); + VbeBuffer += (CopyBlockNum << 2); + Status = PciIo->Mem.Write ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) FrameBufferAddr, + RemainingBytes, + VbeBuffer + ); + ASSERT_EFI_ERROR (Status); + } +} + +/** + Worker function to block transfer for VBE device. + + @param BiosVideoPrivate Instance of BIOS_VIDEO_DEV + @param BltBuffer The data to transfer to screen + @param BltOperation The operation to perform + @param SourceX The X coordinate of the source for BltOperation + @param SourceY The Y coordinate of the source for BltOperation + @param DestinationX The X coordinate of the destination for + BltOperation + @param DestinationY The Y coordinate of the destination for + BltOperation + @param Width The width of a rectangle in the blt rectangle in + pixels + @param Height The height of a rectangle in the blt rectangle in + pixels + @param Delta Not used for EfiBltVideoFill and + EfiBltVideoToVideo operation. If a Delta of 0 is + used, the entire BltBuffer will be operated on. If + a subrectangle of the BltBuffer is used, then + Delta represents the number of bytes in a row of + the BltBuffer. + @param Mode Mode data. + + @retval EFI_INVALID_PARAMETER Invalid parameter passed in + @retval EFI_SUCCESS Blt operation success + +**/ +EFI_STATUS +BiosVideoVbeBltWorker ( + IN BIOS_VIDEO_DEV *BiosVideoPrivate, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta, + IN BIOS_VIDEO_MODE_DATA *Mode + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_TPL OriginalTPL; + UINTN DstY; + UINTN SrcY; + UINTN DstX; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; + VOID *MemAddress; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *VbeFrameBuffer; + UINTN BytesPerScanLine; + UINTN Index; + UINT8 *VbeBuffer; + UINT8 *VbeBuffer1; + UINT8 *BltUint8; + UINT32 VbePixelWidth; + UINT32 Pixel; + UINTN TotalBytes; + + PciIo = BiosVideoPrivate->PciIo; + + VbeFrameBuffer = BiosVideoPrivate->VbeFrameBuffer; + MemAddress = Mode->LinearFrameBuffer; + BytesPerScanLine = Mode->BytesPerScanLine; + VbePixelWidth = Mode->BitsPerPixel / 8; + BltUint8 = (UINT8 *) BltBuffer; + TotalBytes = Width * VbePixelWidth; + + if (((UINTN) BltOperation) >= EfiGraphicsOutputBltOperationMax) { + return EFI_INVALID_PARAMETER; + } + + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + // + // We need to fill the Virtual Screen buffer with the blt data. + // The virtual screen is upside down, as the first row is the bootom row of + // the image. + // + if (BltOperation == EfiBltVideoToBltBuffer) { + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + if (SourceY + Height > Mode->VerticalResolution) { + return EFI_INVALID_PARAMETER; + } + + if (SourceX + Width > Mode->HorizontalResolution) { + return EFI_INVALID_PARAMETER; + } + } else { + // + // BltBuffer to Video: Source is BltBuffer, destination is Video + // + if (DestinationY + Height > Mode->VerticalResolution) { + return EFI_INVALID_PARAMETER; + } + + if (DestinationX + Width > Mode->HorizontalResolution) { + return EFI_INVALID_PARAMETER; + } + } + // + // If Delta is zero, then the entire BltBuffer is being used, so Delta + // is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size, + // the number of bytes in each row can be computed. + // + if (Delta == 0) { + Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + } + // + // We have to raise to TPL Notify, so we make an atomic write the frame buffer. + // We would not want a timer based event (Cursor, ...) to come in while we are + // doing this operation. + // + OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY); + + switch (BltOperation) { + case EfiBltVideoToBltBuffer: + for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) { + Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) (BltUint8 + DstY * Delta + DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + // + // Shuffle the packed bytes in the hardware buffer to match EFI_GRAPHICS_OUTPUT_BLT_PIXEL + // + VbeBuffer = ((UINT8 *) VbeFrameBuffer + (SrcY * BytesPerScanLine + SourceX * VbePixelWidth)); + for (DstX = DestinationX; DstX < (Width + DestinationX); DstX++) { + Pixel = VbeBuffer[0] | VbeBuffer[1] << 8 | VbeBuffer[2] << 16 | VbeBuffer[3] << 24; + Blt->Red = (UINT8) ((Pixel >> Mode->Red.Position) & Mode->Red.Mask); + Blt->Blue = (UINT8) ((Pixel >> Mode->Blue.Position) & Mode->Blue.Mask); + Blt->Green = (UINT8) ((Pixel >> Mode->Green.Position) & Mode->Green.Mask); + Blt->Reserved = 0; + Blt++; + VbeBuffer += VbePixelWidth; + } + + } + break; + + case EfiBltVideoToVideo: + for (Index = 0; Index < Height; Index++) { + if (DestinationY <= SourceY) { + SrcY = SourceY + Index; + DstY = DestinationY + Index; + } else { + SrcY = SourceY + Height - Index - 1; + DstY = DestinationY + Height - Index - 1; + } + + VbeBuffer = ((UINT8 *) VbeFrameBuffer + DstY * BytesPerScanLine + DestinationX * VbePixelWidth); + VbeBuffer1 = ((UINT8 *) VbeFrameBuffer + SrcY * BytesPerScanLine + SourceX * VbePixelWidth); + + gBS->CopyMem ( + VbeBuffer, + VbeBuffer1, + TotalBytes + ); + + // + // Update physical frame buffer. + // + CopyVideoBuffer ( + PciIo, + VbeBuffer, + MemAddress, + DestinationX, + DstY, + TotalBytes, + VbePixelWidth, + BytesPerScanLine + ); + } + break; + + case EfiBltVideoFill: + VbeBuffer = (UINT8 *) ((UINTN) VbeFrameBuffer + (DestinationY * BytesPerScanLine) + DestinationX * VbePixelWidth); + Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) BltUint8; + // + // Shuffle the RGB fields in EFI_GRAPHICS_OUTPUT_BLT_PIXEL to match the hardware buffer + // + Pixel = ((Blt->Red & Mode->Red.Mask) << Mode->Red.Position) | + ( + (Blt->Green & Mode->Green.Mask) << + Mode->Green.Position + ) | + ((Blt->Blue & Mode->Blue.Mask) << Mode->Blue.Position); + + for (Index = 0; Index < Width; Index++) { + gBS->CopyMem ( + VbeBuffer, + &Pixel, + VbePixelWidth + ); + VbeBuffer += VbePixelWidth; + } + + VbeBuffer = (UINT8 *) ((UINTN) VbeFrameBuffer + (DestinationY * BytesPerScanLine) + DestinationX * VbePixelWidth); + for (DstY = DestinationY + 1; DstY < (Height + DestinationY); DstY++) { + gBS->CopyMem ( + (VOID *) ((UINTN) VbeFrameBuffer + (DstY * BytesPerScanLine) + DestinationX * VbePixelWidth), + VbeBuffer, + TotalBytes + ); + } + + for (DstY = DestinationY; DstY < (Height + DestinationY); DstY++) { + // + // Update physical frame buffer. + // + CopyVideoBuffer ( + PciIo, + VbeBuffer, + MemAddress, + DestinationX, + DstY, + TotalBytes, + VbePixelWidth, + BytesPerScanLine + ); + } + break; + + case EfiBltBufferToVideo: + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) (BltUint8 + (SrcY * Delta) + (SourceX) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + VbeBuffer = ((UINT8 *) VbeFrameBuffer + (DstY * BytesPerScanLine + DestinationX * VbePixelWidth)); + for (DstX = DestinationX; DstX < (Width + DestinationX); DstX++) { + // + // Shuffle the RGB fields in EFI_GRAPHICS_OUTPUT_BLT_PIXEL to match the hardware buffer + // + Pixel = ((Blt->Red & Mode->Red.Mask) << Mode->Red.Position) | + ((Blt->Green & Mode->Green.Mask) << Mode->Green.Position) | + ((Blt->Blue & Mode->Blue.Mask) << Mode->Blue.Position); + gBS->CopyMem ( + VbeBuffer, + &Pixel, + VbePixelWidth + ); + Blt++; + VbeBuffer += VbePixelWidth; + } + + VbeBuffer = ((UINT8 *) VbeFrameBuffer + (DstY * BytesPerScanLine + DestinationX * VbePixelWidth)); + + // + // Update physical frame buffer. + // + CopyVideoBuffer ( + PciIo, + VbeBuffer, + MemAddress, + DestinationX, + DstY, + TotalBytes, + VbePixelWidth, + BytesPerScanLine + ); + } + break; + + default: ; + } + + gBS->RestoreTPL (OriginalTPL); + + return EFI_SUCCESS; +} + +/** + Graphics Output protocol instance to block transfer for VBE device. + + @param This Pointer to Graphics Output protocol instance + @param BltBuffer The data to transfer to screen + @param BltOperation The operation to perform + @param SourceX The X coordinate of the source for BltOperation + @param SourceY The Y coordinate of the source for BltOperation + @param DestinationX The X coordinate of the destination for + BltOperation + @param DestinationY The Y coordinate of the destination for + BltOperation + @param Width The width of a rectangle in the blt rectangle in + pixels + @param Height The height of a rectangle in the blt rectangle in + pixels + @param Delta Not used for EfiBltVideoFill and + EfiBltVideoToVideo operation. If a Delta of 0 is + used, the entire BltBuffer will be operated on. If + a subrectangle of the BltBuffer is used, then + Delta represents the number of bytes in a row of + the BltBuffer. + + @retval EFI_INVALID_PARAMETER Invalid parameter passed in + @retval EFI_SUCCESS Blt operation success + +**/ +EFI_STATUS +EFIAPI +BiosVideoGraphicsOutputVbeBlt ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + BIOS_VIDEO_DEV *BiosVideoPrivate; + BIOS_VIDEO_MODE_DATA *Mode; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS (This); + Mode = &BiosVideoPrivate->ModeData[This->Mode->Mode]; + + return BiosVideoVbeBltWorker ( + BiosVideoPrivate, + BltBuffer, + BltOperation, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + Delta, + Mode + ); +} + +/** + Write graphics controller registers. + + @param PciIo Pointer to PciIo protocol instance of the + controller + @param Address Register address + @param Data Data to be written to register + + @return None + +**/ +VOID +WriteGraphicsController ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINTN Address, + IN UINTN Data + ) +{ + Address = Address | (Data << 8); + PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint16, + EFI_PCI_IO_PASS_THROUGH_BAR, + VGA_GRAPHICS_CONTROLLER_ADDRESS_REGISTER, + 1, + &Address + ); +} + + +/** + Read the four bit plane of VGA frame buffer. + + @param PciIo Pointer to PciIo protocol instance of the + controller + @param HardwareBuffer Hardware VGA frame buffer address + @param MemoryBuffer Memory buffer address + @param WidthInBytes Number of bytes in a line to read + @param Height Height of the area to read + + @return None + +**/ +VOID +VgaReadBitPlanes ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 *HardwareBuffer, + UINT8 *MemoryBuffer, + UINTN WidthInBytes, + UINTN Height + ) +{ + UINTN BitPlane; + UINTN Rows; + UINTN FrameBufferOffset; + UINT8 *Source; + UINT8 *Destination; + + // + // Program the Mode Register Write mode 0, Read mode 0 + // + WriteGraphicsController ( + PciIo, + VGA_GRAPHICS_CONTROLLER_MODE_REGISTER, + VGA_GRAPHICS_CONTROLLER_READ_MODE_0 | VGA_GRAPHICS_CONTROLLER_WRITE_MODE_0 + ); + + for (BitPlane = 0, FrameBufferOffset = 0; + BitPlane < VGA_NUMBER_OF_BIT_PLANES; + BitPlane++, FrameBufferOffset += VGA_BYTES_PER_BIT_PLANE + ) { + // + // Program the Read Map Select Register to select the correct bit plane + // + WriteGraphicsController ( + PciIo, + VGA_GRAPHICS_CONTROLLER_READ_MAP_SELECT_REGISTER, + BitPlane + ); + + Source = HardwareBuffer; + Destination = MemoryBuffer + FrameBufferOffset; + + for (Rows = 0; Rows < Height; Rows++, Source += VGA_BYTES_PER_SCAN_LINE, Destination += VGA_BYTES_PER_SCAN_LINE) { + PciIo->Mem.Read ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) (UINTN) Source, + WidthInBytes, + (VOID *) Destination + ); + } + } +} + + +/** + Internal routine to convert VGA color to Grahpics Output color. + + @param MemoryBuffer Buffer containing VGA color + @param CoordinateX The X coordinate of pixel on screen + @param CoordinateY The Y coordinate of pixel on screen + @param BltBuffer Buffer to contain converted Grahpics Output color + + @return None + +**/ +VOID +VgaConvertToGraphicsOutputColor ( + UINT8 *MemoryBuffer, + UINTN CoordinateX, + UINTN CoordinateY, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer + ) +{ + UINTN Mask; + UINTN Bit; + UINTN Color; + + MemoryBuffer += ((CoordinateY << 6) + (CoordinateY << 4) + (CoordinateX >> 3)); + Mask = mVgaBitMaskTable[CoordinateX & 0x07]; + for (Bit = 0x01, Color = 0; Bit < 0x10; Bit <<= 1, MemoryBuffer += VGA_BYTES_PER_BIT_PLANE) { + if ((*MemoryBuffer & Mask) != 0) { + Color |= Bit; + } + } + + *BltBuffer = mVgaColorToGraphicsOutputColor[Color]; +} + +/** + Internal routine to convert Grahpics Output color to VGA color. + + @param BltBuffer buffer containing Grahpics Output color + + @return Converted VGA color + +**/ +UINT8 +VgaConvertColor ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer + ) +{ + UINT8 Color; + + Color = (UINT8) ((BltBuffer->Blue >> 7) | ((BltBuffer->Green >> 6) & 0x02) | ((BltBuffer->Red >> 5) & 0x04)); + if ((BltBuffer->Red + BltBuffer->Green + BltBuffer->Blue) > 0x180) { + Color |= 0x08; + } + + return Color; +} + + +/** + Grahpics Output protocol instance to block transfer for VGA device. + + @param This Pointer to Grahpics Output protocol instance + @param BltBuffer The data to transfer to screen + @param BltOperation The operation to perform + @param SourceX The X coordinate of the source for BltOperation + @param SourceY The Y coordinate of the source for BltOperation + @param DestinationX The X coordinate of the destination for + BltOperation + @param DestinationY The Y coordinate of the destination for + BltOperation + @param Width The width of a rectangle in the blt rectangle in + pixels + @param Height The height of a rectangle in the blt rectangle in + pixels + @param Delta Not used for EfiBltVideoFill and + EfiBltVideoToVideo operation. If a Delta of 0 is + used, the entire BltBuffer will be operated on. If + a subrectangle of the BltBuffer is used, then + Delta represents the number of bytes in a row of + the BltBuffer. + + @retval EFI_INVALID_PARAMETER Invalid parameter passed in + @retval EFI_SUCCESS Blt operation success + +**/ +EFI_STATUS +EFIAPI +BiosVideoGraphicsOutputVgaBlt ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + BIOS_VIDEO_DEV *BiosVideoPrivate; + EFI_TPL OriginalTPL; + UINT8 *MemAddress; + UINTN BytesPerScanLine; + UINTN Bit; + UINTN Index; + UINTN Index1; + UINTN StartAddress; + UINTN Bytes; + UINTN Offset; + UINT8 LeftMask; + UINT8 RightMask; + UINTN Address; + UINTN AddressFix; + UINT8 *Address1; + UINT8 *SourceAddress; + UINT8 *DestinationAddress; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT8 Data; + UINT8 PixelColor; + UINT8 *VgaFrameBuffer; + UINTN SourceOffset; + UINTN SourceWidth; + UINTN Rows; + UINTN Columns; + UINTN CoordinateX; + UINTN CoordinateY; + UINTN CurrentMode; + + if (This == NULL || ((UINTN) BltOperation) >= EfiGraphicsOutputBltOperationMax) { + return EFI_INVALID_PARAMETER; + } + + BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS (This); + + CurrentMode = This->Mode->Mode; + PciIo = BiosVideoPrivate->PciIo; + MemAddress = BiosVideoPrivate->ModeData[CurrentMode].LinearFrameBuffer; + BytesPerScanLine = BiosVideoPrivate->ModeData[CurrentMode].BytesPerScanLine >> 3; + VgaFrameBuffer = BiosVideoPrivate->VgaFrameBuffer; + + + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + // + // We need to fill the Virtual Screen buffer with the blt data. + // The virtual screen is upside down, as the first row is the bootom row of + // the image. + // + if (BltOperation == EfiBltVideoToBltBuffer) { + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + if (SourceY + Height > BiosVideoPrivate->ModeData[CurrentMode].VerticalResolution) { + return EFI_INVALID_PARAMETER; + } + + if (SourceX + Width > BiosVideoPrivate->ModeData[CurrentMode].HorizontalResolution) { + return EFI_INVALID_PARAMETER; + } + } else { + // + // BltBuffer to Video: Source is BltBuffer, destination is Video + // + if (DestinationY + Height > BiosVideoPrivate->ModeData[CurrentMode].VerticalResolution) { + return EFI_INVALID_PARAMETER; + } + + if (DestinationX + Width > BiosVideoPrivate->ModeData[CurrentMode].HorizontalResolution) { + return EFI_INVALID_PARAMETER; + } + } + // + // If Delta is zero, then the entire BltBuffer is being used, so Delta + // is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size, + // the number of bytes in each row can be computed. + // + if (Delta == 0) { + Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + } + // + // We have to raise to TPL Notify, so we make an atomic write the frame buffer. + // We would not want a timer based event (Cursor, ...) to come in while we are + // doing this operation. + // + OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY); + + // + // Compute some values we need for VGA + // + switch (BltOperation) { + case EfiBltVideoToBltBuffer: + + SourceOffset = (SourceY << 6) + (SourceY << 4) + (SourceX >> 3); + SourceWidth = ((SourceX + Width - 1) >> 3) - (SourceX >> 3) + 1; + + // + // Read all the pixels in the 4 bit planes into a memory buffer that looks like the VGA buffer + // + VgaReadBitPlanes ( + PciIo, + MemAddress + SourceOffset, + VgaFrameBuffer + SourceOffset, + SourceWidth, + Height + ); + + // + // Convert VGA Bit Planes to a Graphics Output 32-bit color value + // + BltBuffer += (DestinationY * (Delta >> 2) + DestinationX); + for (Rows = 0, CoordinateY = SourceY; Rows < Height; Rows++, CoordinateY++, BltBuffer += (Delta >> 2)) { + for (Columns = 0, CoordinateX = SourceX; Columns < Width; Columns++, CoordinateX++, BltBuffer++) { + VgaConvertToGraphicsOutputColor (VgaFrameBuffer, CoordinateX, CoordinateY, BltBuffer); + } + + BltBuffer -= Width; + } + + break; + + case EfiBltVideoToVideo: + // + // Check for an aligned Video to Video operation + // + if ((SourceX & 0x07) == 0x00 && (DestinationX & 0x07) == 0x00 && (Width & 0x07) == 0x00) { + // + // Program the Mode Register Write mode 1, Read mode 0 + // + WriteGraphicsController ( + PciIo, + VGA_GRAPHICS_CONTROLLER_MODE_REGISTER, + VGA_GRAPHICS_CONTROLLER_READ_MODE_0 | VGA_GRAPHICS_CONTROLLER_WRITE_MODE_1 + ); + + SourceAddress = (UINT8 *) (MemAddress + (SourceY << 6) + (SourceY << 4) + (SourceX >> 3)); + DestinationAddress = (UINT8 *) (MemAddress + (DestinationY << 6) + (DestinationY << 4) + (DestinationX >> 3)); + Bytes = Width >> 3; + for (Index = 0, Offset = 0; Index < Height; Index++, Offset += BytesPerScanLine) { + PciIo->CopyMem ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) (UINTN) (DestinationAddress + Offset), + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) (UINTN) (SourceAddress + Offset), + Bytes + ); + } + } else { + SourceOffset = (SourceY << 6) + (SourceY << 4) + (SourceX >> 3); + SourceWidth = ((SourceX + Width - 1) >> 3) - (SourceX >> 3) + 1; + + // + // Read all the pixels in the 4 bit planes into a memory buffer that looks like the VGA buffer + // + VgaReadBitPlanes ( + PciIo, + MemAddress + SourceOffset, + VgaFrameBuffer + SourceOffset, + SourceWidth, + Height + ); + } + + break; + + case EfiBltVideoFill: + StartAddress = (UINTN) (MemAddress + (DestinationY << 6) + (DestinationY << 4) + (DestinationX >> 3)); + Bytes = ((DestinationX + Width - 1) >> 3) - (DestinationX >> 3); + LeftMask = mVgaLeftMaskTable[DestinationX & 0x07]; + RightMask = mVgaRightMaskTable[(DestinationX + Width - 1) & 0x07]; + if (Bytes == 0) { + LeftMask = (UINT8) (LeftMask & RightMask); + RightMask = 0; + } + + if (LeftMask == 0xff) { + StartAddress--; + Bytes++; + LeftMask = 0; + } + + if (RightMask == 0xff) { + Bytes++; + RightMask = 0; + } + + PixelColor = VgaConvertColor (BltBuffer); + + // + // Program the Mode Register Write mode 2, Read mode 0 + // + WriteGraphicsController ( + PciIo, + VGA_GRAPHICS_CONTROLLER_MODE_REGISTER, + VGA_GRAPHICS_CONTROLLER_READ_MODE_0 | VGA_GRAPHICS_CONTROLLER_WRITE_MODE_2 + ); + + // + // Program the Data Rotate/Function Select Register to replace + // + WriteGraphicsController ( + PciIo, + VGA_GRAPHICS_CONTROLLER_DATA_ROTATE_REGISTER, + VGA_GRAPHICS_CONTROLLER_FUNCTION_REPLACE + ); + + if (LeftMask != 0) { + // + // Program the BitMask register with the Left column mask + // + WriteGraphicsController ( + PciIo, + VGA_GRAPHICS_CONTROLLER_BIT_MASK_REGISTER, + LeftMask + ); + + for (Index = 0, Address = StartAddress; Index < Height; Index++, Address += BytesPerScanLine) { + // + // Read data from the bit planes into the latches + // + PciIo->Mem.Read ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) (UINTN) Address, + 1, + &Data + ); + // + // Write the lower 4 bits of PixelColor to the bit planes in the pixels enabled by BitMask + // + PciIo->Mem.Write ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) (UINTN) Address, + 1, + &PixelColor + ); + } + } + + if (Bytes > 1) { + // + // Program the BitMask register with the middle column mask of 0xff + // + WriteGraphicsController ( + PciIo, + VGA_GRAPHICS_CONTROLLER_BIT_MASK_REGISTER, + 0xff + ); + + for (Index = 0, Address = StartAddress + 1; Index < Height; Index++, Address += BytesPerScanLine) { + PciIo->Mem.Write ( + PciIo, + EfiPciIoWidthFillUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) (UINTN) Address, + Bytes - 1, + &PixelColor + ); + } + } + + if (RightMask != 0) { + // + // Program the BitMask register with the Right column mask + // + WriteGraphicsController ( + PciIo, + VGA_GRAPHICS_CONTROLLER_BIT_MASK_REGISTER, + RightMask + ); + + for (Index = 0, Address = StartAddress + Bytes; Index < Height; Index++, Address += BytesPerScanLine) { + // + // Read data from the bit planes into the latches + // + PciIo->Mem.Read ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) (UINTN) Address, + 1, + &Data + ); + // + // Write the lower 4 bits of PixelColor to the bit planes in the pixels enabled by BitMask + // + PciIo->Mem.Write ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) (UINTN) Address, + 1, + &PixelColor + ); + } + } + break; + + case EfiBltBufferToVideo: + StartAddress = (UINTN) (MemAddress + (DestinationY << 6) + (DestinationY << 4) + (DestinationX >> 3)); + LeftMask = mVgaBitMaskTable[DestinationX & 0x07]; + + // + // Program the Mode Register Write mode 2, Read mode 0 + // + WriteGraphicsController ( + PciIo, + VGA_GRAPHICS_CONTROLLER_MODE_REGISTER, + VGA_GRAPHICS_CONTROLLER_READ_MODE_0 | VGA_GRAPHICS_CONTROLLER_WRITE_MODE_2 + ); + + // + // Program the Data Rotate/Function Select Register to replace + // + WriteGraphicsController ( + PciIo, + VGA_GRAPHICS_CONTROLLER_DATA_ROTATE_REGISTER, + VGA_GRAPHICS_CONTROLLER_FUNCTION_REPLACE + ); + + for (Index = 0, Address = StartAddress; Index < Height; Index++, Address += BytesPerScanLine) { + for (Index1 = 0; Index1 < Width; Index1++) { + BiosVideoPrivate->LineBuffer[Index1] = VgaConvertColor (&BltBuffer[(SourceY + Index) * (Delta >> 2) + SourceX + Index1]); + } + AddressFix = Address; + + for (Bit = 0; Bit < 8; Bit++) { + // + // Program the BitMask register with the Left column mask + // + WriteGraphicsController ( + PciIo, + VGA_GRAPHICS_CONTROLLER_BIT_MASK_REGISTER, + LeftMask + ); + + for (Index1 = Bit, Address1 = (UINT8 *) AddressFix; Index1 < Width; Index1 += 8, Address1++) { + // + // Read data from the bit planes into the latches + // + PciIo->Mem.Read ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) (UINTN) Address1, + 1, + &Data + ); + + PciIo->Mem.Write ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) (UINTN) Address1, + 1, + &BiosVideoPrivate->LineBuffer[Index1] + ); + } + + LeftMask = (UINT8) (LeftMask >> 1); + if (LeftMask == 0) { + LeftMask = 0x80; + AddressFix++; + } + } + } + + break; + + default: ; + } + + gBS->RestoreTPL (OriginalTPL); + + return EFI_SUCCESS; +} + +// +// VGA Mini Port Protocol Functions +// + +/** + VgaMiniPort protocol interface to set mode. + + @param This Pointer to VgaMiniPort protocol instance + @param ModeNumber The index of the mode + + @retval EFI_UNSUPPORTED The requested mode is not supported + @retval EFI_SUCCESS The requested mode is set successfully + +**/ +EFI_STATUS +EFIAPI +BiosVideoVgaMiniPortSetMode ( + IN EFI_VGA_MINI_PORT_PROTOCOL *This, + IN UINTN ModeNumber + ) +{ + BIOS_VIDEO_DEV *BiosVideoPrivate; + EFI_IA32_REGISTER_SET Regs; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Make sure the ModeNumber is a valid value + // + if (ModeNumber >= This->MaxMode) { + return EFI_UNSUPPORTED; + } + // + // Get the device structure for this device + // + BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_VGA_MINI_PORT_THIS (This); + + switch (ModeNumber) { + case 0: + // + // Set the 80x25 Text VGA Mode + // + Regs.H.AH = 0x00; + Regs.H.AL = 0x83; + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + + Regs.H.AH = 0x11; + Regs.H.AL = 0x14; + Regs.H.BL = 0; + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + break; + + case 1: + // + // Set the 80x50 Text VGA Mode + // + Regs.H.AH = 0x00; + Regs.H.AL = 0x83; + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + Regs.H.AH = 0x11; + Regs.H.AL = 0x12; + Regs.H.BL = 0; + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + break; + + default: + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Event handler for Exit Boot Service. + + @param Event The event that be siganlled when exiting boot service. + @param Context Pointer to instance of BIOS_VIDEO_DEV. + +**/ +VOID +EFIAPI +BiosVideoNotifyExitBootServices ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + BIOS_VIDEO_DEV *BiosVideoPrivate; + EFI_IA32_REGISTER_SET Regs; + + BiosVideoPrivate = (BIOS_VIDEO_DEV *)Context; + + // + // Set the 80x25 Text VGA Mode + // + Regs.H.AH = 0x00; + Regs.H.AL = 0x03; + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + + Regs.H.AH = 0x00; + Regs.H.AL = 0x83; + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); + + Regs.H.AH = 0x11; + Regs.H.AL = 0x04; + Regs.H.BL = 0; + BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs); +} + +/** + The user Entry Point for module UefiBiosVideo. The user code starts with this function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +BiosVideoEntryPoint( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gBiosVideoDriverBinding, + ImageHandle, + &gBiosVideoComponentName, + &gBiosVideoComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + // + // Install Legacy BIOS GUID to mark this driver as a BIOS Thunk Driver + // + return gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiLegacyBiosGuid, + NULL, + NULL + ); +} + diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/BiosVideo.h b/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/BiosVideo.h new file mode 100644 index 0000000000..45a75b9cd0 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/BiosVideo.h @@ -0,0 +1,532 @@ +/** @file + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _BIOS_GRAPHICS_OUTPUT_H_ +#define _BIOS_GRAPHICS_OUTPUT_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "VesaBiosExtensions.h" + +// +// Packed format support: The number of bits reserved for each of the colors and the actual +// position of RGB in the frame buffer is specified in the VBE Mode information +// +typedef struct { + UINT8 Position; // Position of the color + UINT8 Mask; // The number of bits expressed as a mask +} BIOS_VIDEO_COLOR_PLACEMENT; + +// +// BIOS Graphics Output Graphical Mode Data +// +typedef struct { + UINT16 VbeModeNumber; + UINT16 BytesPerScanLine; + VOID *LinearFrameBuffer; + UINTN FrameBufferSize; + UINT32 HorizontalResolution; + UINT32 VerticalResolution; + UINT32 ColorDepth; + UINT32 RefreshRate; + UINT32 BitsPerPixel; + BIOS_VIDEO_COLOR_PLACEMENT Red; + BIOS_VIDEO_COLOR_PLACEMENT Green; + BIOS_VIDEO_COLOR_PLACEMENT Blue; + BIOS_VIDEO_COLOR_PLACEMENT Reserved; + EFI_GRAPHICS_PIXEL_FORMAT PixelFormat; + EFI_PIXEL_BITMASK PixelBitMask; +} BIOS_VIDEO_MODE_DATA; + +// +// BIOS video child handle private data Structure +// +#define BIOS_VIDEO_DEV_SIGNATURE SIGNATURE_32 ('B', 'V', 'M', 'p') + +typedef struct { + UINTN Signature; + EFI_HANDLE Handle; + + // + // Consumed Protocols + // + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + // + // Original PCI attributes + // + UINT64 OriginalPciAttributes; + + // + // Produced Protocols + // + EFI_GRAPHICS_OUTPUT_PROTOCOL GraphicsOutput; + EFI_EDID_DISCOVERED_PROTOCOL EdidDiscovered; + EFI_EDID_ACTIVE_PROTOCOL EdidActive; + EFI_VGA_MINI_PORT_PROTOCOL VgaMiniPort; + + // + // General fields + // + BOOLEAN VgaCompatible; + BOOLEAN ProduceGraphicsOutput; + + // + // Graphics Output Protocol related fields + // + BOOLEAN HardwareNeedsStarting; + UINTN CurrentMode; + UINTN MaxMode; + BIOS_VIDEO_MODE_DATA *ModeData; + UINT8 *LineBuffer; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *VbeFrameBuffer; + UINT8 *VgaFrameBuffer; + + // + // VESA Bios Extensions related fields + // + UINTN NumberOfPagesBelow1MB; // Number of 4KB pages in PagesBelow1MB + EFI_PHYSICAL_ADDRESS PagesBelow1MB; // Buffer for all VBE Information Blocks + VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK *VbeInformationBlock; // 0x200 bytes. Must be allocated below 1MB + VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK *VbeModeInformationBlock; // 0x100 bytes. Must be allocated below 1MB + VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK *VbeEdidDataBlock; // 0x80 bytes. Must be allocated below 1MB + VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK *VbeCrtcInformationBlock; // 59 bytes. Must be allocated below 1MB + UINTN VbeSaveRestorePages; // Number of 4KB pages in VbeSaveRestoreBuffer + EFI_PHYSICAL_ADDRESS VbeSaveRestoreBuffer; // Must be allocated below 1MB + // + // Status code + // + EFI_DEVICE_PATH_PROTOCOL *GopDevicePath; + + EFI_EVENT ExitBootServicesEvent; +} BIOS_VIDEO_DEV; + +#define BIOS_VIDEO_DEV_FROM_PCI_IO_THIS(a) CR (a, BIOS_VIDEO_DEV, PciIo, BIOS_VIDEO_DEV_SIGNATURE) +#define BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS(a) CR (a, BIOS_VIDEO_DEV, GraphicsOutput, BIOS_VIDEO_DEV_SIGNATURE) +#define BIOS_VIDEO_DEV_FROM_VGA_MINI_PORT_THIS(a) CR (a, BIOS_VIDEO_DEV, VgaMiniPort, BIOS_VIDEO_DEV_SIGNATURE) + +#define GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER 0xffff + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gBiosVideoDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gBiosVideoComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gBiosVideoComponentName2; + +// +// Driver Binding Protocol functions +// + +/** + Supported. + + @param This Pointer to driver binding protocol + @param Controller Controller handle to connect + @param RemainingDevicePath A pointer to the remaining portion of a device + path + + @retval EFI_STATUS EFI_SUCCESS:This controller can be managed by this + driver, Otherwise, this controller cannot be + managed by this driver + +**/ +EFI_STATUS +EFIAPI +BiosVideoDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + + +/** + Install Graphics Output Protocol onto VGA device handles. + + @param This Pointer to driver binding protocol + @param Controller Controller handle to connect + @param RemainingDevicePath A pointer to the remaining portion of a device + path + + @return EFI_STATUS + +**/ +EFI_STATUS +EFIAPI +BiosVideoDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + + +/** + Stop. + + @param This Pointer to driver binding protocol + @param Controller Controller handle to connect + @param NumberOfChildren Number of children handle created by this driver + @param ChildHandleBuffer Buffer containing child handle created + + @retval EFI_SUCCESS Driver disconnected successfully from controller + @retval EFI_UNSUPPORTED Cannot find BIOS_VIDEO_DEV structure + +**/ +EFI_STATUS +EFIAPI +BiosVideoDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// Private worker functions +// + +/** + Check for VBE device. + + @param BiosVideoPrivate Pointer to BIOS_VIDEO_DEV structure + + @retval EFI_SUCCESS VBE device found + +**/ +EFI_STATUS +BiosVideoCheckForVbe ( + IN OUT BIOS_VIDEO_DEV *BiosVideoPrivate + ); + + +/** + Check for VGA device. + + @param BiosVideoPrivate Pointer to BIOS_VIDEO_DEV structure + + @retval EFI_SUCCESS Standard VGA device found + +**/ +EFI_STATUS +BiosVideoCheckForVga ( + IN OUT BIOS_VIDEO_DEV *BiosVideoPrivate + ); + + + + +/** + Release resource for biso video instance. + + @param BiosVideoPrivate Video child device private data structure + +**/ +VOID +BiosVideoDeviceReleaseResource ( + BIOS_VIDEO_DEV *BiosVideoPrivate + ); + +// +// BIOS Graphics Output Protocol functions +// + +/** + Graphics Output protocol interface to get video mode. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to return information on. + @param SizeOfInfo A pointer to the size, in bytes, of the Info + buffer. + @param Info Caller allocated buffer that returns information + about ModeNumber. + + @retval EFI_SUCCESS Mode information returned. + @retval EFI_BUFFER_TOO_SMALL The Info buffer was too small. + @retval EFI_DEVICE_ERROR A hardware error occurred trying to retrieve the + video mode. + @retval EFI_NOT_STARTED Video display is not initialized. Call SetMode () + @retval EFI_INVALID_PARAMETER One of the input args was NULL. + +**/ +EFI_STATUS +EFIAPI +BiosVideoGraphicsOutputQueryMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber, + OUT UINTN *SizeOfInfo, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info + ); + + +/** + Graphics Output protocol interface to set video mode. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to be set. + + @retval EFI_SUCCESS Graphics mode was changed. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the + request. + @retval EFI_UNSUPPORTED ModeNumber is not supported by this device. + +**/ +EFI_STATUS +EFIAPI +BiosVideoGraphicsOutputSetMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL * This, + IN UINT32 ModeNumber + ); + + +/** + Graphics Output protocol instance to block transfer for VBE device. + + @param This Pointer to Graphics Output protocol instance + @param BltBuffer The data to transfer to screen + @param BltOperation The operation to perform + @param SourceX The X coordinate of the source for BltOperation + @param SourceY The Y coordinate of the source for BltOperation + @param DestinationX The X coordinate of the destination for + BltOperation + @param DestinationY The Y coordinate of the destination for + BltOperation + @param Width The width of a rectangle in the blt rectangle in + pixels + @param Height The height of a rectangle in the blt rectangle in + pixels + @param Delta Not used for EfiBltVideoFill and + EfiBltVideoToVideo operation. If a Delta of 0 is + used, the entire BltBuffer will be operated on. If + a subrectangle of the BltBuffer is used, then + Delta represents the number of bytes in a row of + the BltBuffer. + + @retval EFI_INVALID_PARAMETER Invalid parameter passed in + @retval EFI_SUCCESS Blt operation success + +**/ +EFI_STATUS +EFIAPI +BiosVideoGraphicsOutputVbeBlt ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ); + + +/** + Grahpics Output protocol instance to block transfer for VGA device. + + @param This Pointer to Grahpics Output protocol instance + @param BltBuffer The data to transfer to screen + @param BltOperation The operation to perform + @param SourceX The X coordinate of the source for BltOperation + @param SourceY The Y coordinate of the source for BltOperation + @param DestinationX The X coordinate of the destination for + BltOperation + @param DestinationY The Y coordinate of the destination for + BltOperation + @param Width The width of a rectangle in the blt rectangle in + pixels + @param Height The height of a rectangle in the blt rectangle in + pixels + @param Delta Not used for EfiBltVideoFill and + EfiBltVideoToVideo operation. If a Delta of 0 is + used, the entire BltBuffer will be operated on. If + a subrectangle of the BltBuffer is used, then + Delta represents the number of bytes in a row of + the BltBuffer. + + @retval EFI_INVALID_PARAMETER Invalid parameter passed in + @retval EFI_SUCCESS Blt operation success + +**/ +EFI_STATUS +EFIAPI +BiosVideoGraphicsOutputVgaBlt ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ); + +// +// BIOS VGA Mini Port Protocol functions +// + +/** + VgaMiniPort protocol interface to set mode. + + @param This Pointer to VgaMiniPort protocol instance + @param ModeNumber The index of the mode + + @retval EFI_UNSUPPORTED The requested mode is not supported + @retval EFI_SUCCESS The requested mode is set successfully + +**/ +EFI_STATUS +EFIAPI +BiosVideoVgaMiniPortSetMode ( + IN EFI_VGA_MINI_PORT_PROTOCOL *This, + IN UINTN ModeNumber + ); + +/** + Event handler for Exit Boot Service. + + @param Event The event that be siganlled when exiting boot service. + @param Context Pointer to instance of BIOS_VIDEO_DEV. + +**/ +VOID +EFIAPI +BiosVideoNotifyExitBootServices ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +// +// Standard VGA Definitions +// +#define VGA_HORIZONTAL_RESOLUTION 640 +#define VGA_VERTICAL_RESOLUTION 480 +#define VGA_NUMBER_OF_BIT_PLANES 4 +#define VGA_PIXELS_PER_BYTE 8 +#define VGA_BYTES_PER_SCAN_LINE (VGA_HORIZONTAL_RESOLUTION / VGA_PIXELS_PER_BYTE) +#define VGA_BYTES_PER_BIT_PLANE (VGA_VERTICAL_RESOLUTION * VGA_BYTES_PER_SCAN_LINE) + +#define VGA_GRAPHICS_CONTROLLER_ADDRESS_REGISTER 0x3ce +#define VGA_GRAPHICS_CONTROLLER_DATA_REGISTER 0x3cf + +#define VGA_GRAPHICS_CONTROLLER_SET_RESET_REGISTER 0x00 + +#define VGA_GRAPHICS_CONTROLLER_ENABLE_SET_RESET_REGISTER 0x01 + +#define VGA_GRAPHICS_CONTROLLER_COLOR_COMPARE_REGISTER 0x02 + +#define VGA_GRAPHICS_CONTROLLER_DATA_ROTATE_REGISTER 0x03 +#define VGA_GRAPHICS_CONTROLLER_FUNCTION_REPLACE 0x00 +#define VGA_GRAPHICS_CONTROLLER_FUNCTION_AND 0x08 +#define VGA_GRAPHICS_CONTROLLER_FUNCTION_OR 0x10 +#define VGA_GRAPHICS_CONTROLLER_FUNCTION_XOR 0x18 + +#define VGA_GRAPHICS_CONTROLLER_READ_MAP_SELECT_REGISTER 0x04 + +#define VGA_GRAPHICS_CONTROLLER_MODE_REGISTER 0x05 +#define VGA_GRAPHICS_CONTROLLER_READ_MODE_0 0x00 +#define VGA_GRAPHICS_CONTROLLER_READ_MODE_1 0x08 +#define VGA_GRAPHICS_CONTROLLER_WRITE_MODE_0 0x00 +#define VGA_GRAPHICS_CONTROLLER_WRITE_MODE_1 0x01 +#define VGA_GRAPHICS_CONTROLLER_WRITE_MODE_2 0x02 +#define VGA_GRAPHICS_CONTROLLER_WRITE_MODE_3 0x03 + +#define VGA_GRAPHICS_CONTROLLER_MISCELLANEOUS_REGISTER 0x06 + +#define VGA_GRAPHICS_CONTROLLER_COLOR_DONT_CARE_REGISTER 0x07 + +#define VGA_GRAPHICS_CONTROLLER_BIT_MASK_REGISTER 0x08 + +/** + Install child handles if the Handle supports MBR format. + + @param This Calling context. + @param ParentHandle Parent Handle + @param ParentPciIo Parent PciIo interface + @param ParentLegacyBios Parent LegacyBios interface + @param ParentDevicePath Parent Device Path + @param RemainingDevicePath Remaining Device Path + @param OriginalPciAttributes Original PCI Attributes + + @retval EFI_SUCCESS If a child handle was added + @retval other A child handle was not added + +**/ +EFI_STATUS +BiosVideoChildHandleInstall ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ParentHandle, + IN EFI_PCI_IO_PROTOCOL *ParentPciIo, + IN EFI_LEGACY_BIOS_PROTOCOL *ParentLegacyBios, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath, + IN UINT64 OriginalPciAttributes + ); + +/** + Deregister an video child handle and free resources. + + @param This Protocol instance pointer. + @param Controller Video controller handle + @param Handle Video child handle + + @return EFI_STATUS + +**/ +EFI_STATUS +BiosVideoChildHandleUninstall ( + EFI_DRIVER_BINDING_PROTOCOL *This, + EFI_HANDLE Controller, + EFI_HANDLE Handle + ); + +/** + Release resource for biso video instance. + + @param BiosVideoPrivate Video child device private data structure + +**/ +VOID +BiosVideoDeviceReleaseResource ( + BIOS_VIDEO_DEV *BiosVideoPrivate + ); + +#endif diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/ComponentName.c b/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/ComponentName.c new file mode 100644 index 0000000000..1d70e5c4a3 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/ComponentName.c @@ -0,0 +1,313 @@ +/** @file + +Copyright (c) 2006, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "BiosVideo.h" + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosVideoComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosVideoComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gBiosVideoComponentName = { + BiosVideoComponentNameGetDriverName, + BiosVideoComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gBiosVideoComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) BiosVideoComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) BiosVideoComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mBiosVideoDriverNameTable[] = { + { + "eng;en", + L"BIOS[INT10] Video Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosVideoComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mBiosVideoDriverNameTable, + DriverName, + (BOOLEAN)(This == &gBiosVideoComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +BiosVideoComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/VesaBiosExtensions.h b/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/VesaBiosExtensions.h new file mode 100644 index 0000000000..dcda3fcd5a --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/VesaBiosExtensions.h @@ -0,0 +1,451 @@ +/** @file + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _VESA_BIOS_EXTENSIONS_H_ +#define _VESA_BIOS_EXTENSIONS_H_ + +// +// Turn on byte packing of data structures +// +#pragma pack(1) +// +// VESA BIOS Extensions status codes +// +#define VESA_BIOS_EXTENSIONS_STATUS_SUCCESS 0x004f + +// +// VESA BIOS Extensions Services +// +#define VESA_BIOS_EXTENSIONS_RETURN_CONTROLLER_INFORMATION 0x4f00 + +/*++ + + Routine Description: + Function 00 : Return Controller Information + + Arguments: + Inputs: + AX = 0x4f00 + ES:DI = Pointer to buffer to place VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK structure + Outputs: + AX = Return Status + +--*/ +#define VESA_BIOS_EXTENSIONS_RETURN_MODE_INFORMATION 0x4f01 + +/*++ + + Routine Description: + Function 01 : Return Mode Information + + Arguments: + Inputs: + AX = 0x4f01 + CX = Mode Number + ES:DI = Pointer to buffer to place VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK structure + Outputs: + AX = Return Status + +--*/ +#define VESA_BIOS_EXTENSIONS_SET_MODE 0x4f02 + +/*++ + + Routine Description: + Function 02 : Set Mode + + Arguments: + Inputs: + AX = 0x4f02 + BX = Desired mode to set + D0-D8 = Mode Number + D9-D10 = Reserved (must be 0) + D11 = 0 - Use current default refresh rate + = 1 - Use user specfieid CRTC values for refresh rate + D12-D13 = Reserved (must be 0) + D14 = 0 - Use windowed frame buffer model + = 1 - Use linear/flat frame buffer model + D15 = 0 - Clear display memory + = 1 - Don't clear display memory + ES:DI = Pointer to buffer to the VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK structure + Outputs: + AX = Return Status + +--*/ +#define VESA_BIOS_EXTENSIONS_RETURN_CURRENT_MODE 0x4f03 + +/*++ + + Routine Description: + Function 03 : Return Current Mode + + Arguments: + Inputs: + AX = 0x4f03 + Outputs: + AX = Return Status + BX = Current mode + D0-D13 = Mode Number + D14 = 0 - Windowed frame buffer model + = 1 - Linear/flat frame buffer model + D15 = 0 - Memory cleared at last mode set + = 1 - Memory not cleared at last mode set + +--*/ +#define VESA_BIOS_EXTENSIONS_SAVE_RESTORE_STATE 0x4f04 + +/*++ + + Routine Description: + Function 04 : Save/Restore State + + Arguments: + Inputs: + AX = 0x4f03 + DL = 0x00 - Return Save/Restore State buffer size + = 0x01 - Save State + = 0x02 - Restore State + CX = Requested Status + D0 = Save/Restore controller hardware state + D1 = Save/Restore BIOS data state + D2 = Save/Restore DAC state + D3 = Save/Restore Regsiter state + ES:BX = Pointer to buffer if DL=1 or DL=2 + Outputs: + AX = Return Status + BX = Number of 64 byte blocks to hold the state buffer if DL=0 + +--*/ +#define VESA_BIOS_EXTENSIONS_EDID 0x4f15 + +/*++ + + Routine Description: + Function 15 : implement VBE/DDC service + + Arguments: + Inputs: + AX = 0x4f15 + BL = 0x00 - Report VBE/DDC Capabilities + CX = 0x00 - Controller unit number (00 = primary controller) + ES:DI = Null pointer, must be 0:0 in version 1.0 + Outputs: + AX = Return Status + BH = Approx. time in seconds, rounded up, to transfer one EDID block(128 bytes) + BL = DDC level supported + D0 = 0 DDC1 not supported + = 1 DDC1 supported + D1 = 0 DDC2 not supported + = 1 DDC2 supported + D2 = 0 Screen not blanked during data transfer + = 1 Screen blanked during data transfer + + Inputs: + AX = 0x4f15 + BL = 0x01 - Read EDID + CX = 0x00 - Controller unit number (00 = primary controller) + DX = 0x00 - EDID block number + ES:DI = Pointer to buffer in which the EDID block is returned + Outputs: + AX = Return Status +--*/ + +// +// Timing data from EDID data block +// +#define VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE 128 +#define VESA_BIOS_EXTENSIONS_EDID_ESTABLISHED_TIMING_MAX_NUMBER 17 + +typedef struct { + UINT16 HorizontalResolution; + UINT16 VerticalResolution; + UINT16 RefreshRate; +} VESA_BIOS_EXTENSIONS_EDID_TIMING; + +typedef struct { + UINT32 ValidNumber; + UINT32 Key[VESA_BIOS_EXTENSIONS_EDID_ESTABLISHED_TIMING_MAX_NUMBER]; +} VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING; + +typedef struct { + UINT8 Header[8]; //EDID header "00 FF FF FF FF FF FF 00" + UINT16 ManufactureName; //EISA 3-character ID + UINT16 ProductCode; //Vendor assigned code + UINT32 SerialNumber; //32-bit serial number + UINT8 WeekOfManufacture; //Week number + UINT8 YearOfManufacture; //Year + UINT8 EdidVersion; //EDID Structure Version + UINT8 EdidRevision; //EDID Structure Revision + UINT8 VideoInputDefinition; + UINT8 MaxHorizontalImageSize; //cm + UINT8 MaxVerticalImageSize; //cm + UINT8 DisplayTransferCharacteristic; + UINT8 FeatureSupport; + UINT8 RedGreenLowBits; //Rx1 Rx0 Ry1 Ry0 Gx1 Gx0 Gy1Gy0 + UINT8 BlueWhiteLowBits; //Bx1 Bx0 By1 By0 Wx1 Wx0 Wy1 Wy0 + UINT8 RedX; //Red-x Bits 9 - 2 + UINT8 RedY; //Red-y Bits 9 - 2 + UINT8 GreenX; //Green-x Bits 9 - 2 + UINT8 GreenY; //Green-y Bits 9 - 2 + UINT8 BlueX; //Blue-x Bits 9 - 2 + UINT8 BlueY; //Blue-y Bits 9 - 2 + UINT8 WhiteX; //White-x Bits 9 - 2 + UINT8 WhiteY; //White-x Bits 9 - 2 + UINT8 EstablishedTimings[3]; + UINT8 StandardTimingIdentification[16]; + UINT8 DetailedTimingDescriptions[72]; + UINT8 ExtensionFlag; //Number of (optional) 128-byte EDID extension blocks to follow + UINT8 Checksum; +} VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK; + +// +// Super VGA Information Block +// +typedef struct { + UINT32 VESASignature; // 'VESA' 4 byte signature + UINT16 VESAVersion; // VBE version number + UINT32 OEMStringPtr; // Pointer to OEM string + UINT32 Capabilities; // Capabilities of video card + UINT32 VideoModePtr; // Pointer to an array of 16-bit supported modes values terminated by 0xFFFF + UINT16 TotalMemory; // Number of 64kb memory blocks + UINT16 OemSoftwareRev; // VBE implementation Software revision + UINT32 OemVendorNamePtr; // VbeFarPtr to Vendor Name String + UINT32 OemProductNamePtr; // VbeFarPtr to Product Name String + UINT32 OemProductRevPtr; // VbeFarPtr to Product Revision String + UINT8 Reserved[222]; // Reserved for VBE implementation scratch area + UINT8 OemData[256]; // Data area for OEM strings. Pad to 512 byte block size +} VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK; + +// +// Super VGA Information Block VESASignature values +// +#define VESA_BIOS_EXTENSIONS_VESA_SIGNATURE SIGNATURE_32 ('V', 'E', 'S', 'A') +#define VESA_BIOS_EXTENSIONS_VBE2_SIGNATURE SIGNATURE_32 ('V', 'B', 'E', '2') + +// +// Super VGA Information Block VESAVersion values +// +#define VESA_BIOS_EXTENSIONS_VERSION_1_2 0x0102 +#define VESA_BIOS_EXTENSIONS_VERSION_2_0 0x0200 +#define VESA_BIOS_EXTENSIONS_VERSION_3_0 0x0300 + +// +// Super VGA Information Block Capabilities field bit defintions +// +#define VESA_BIOS_EXTENSIONS_CAPABILITY_8_BIT_DAC 0x01 // 0: DAC width is fixed at 6 bits/color +// 1: DAC width switchable to 8 bits/color +// +#define VESA_BIOS_EXTENSIONS_CAPABILITY_NOT_VGA 0x02 // 0: Controller is VGA compatible +// 1: Controller is not VGA compatible +// +#define VESA_BIOS_EXTENSIONS_CAPABILITY_NOT_NORMAL_RAMDAC 0x04 // 0: Normal RAMDAC operation +// 1: Use blank bit in function 9 to program RAMDAC +// +#define VESA_BIOS_EXTENSIONS_CAPABILITY_STEREOSCOPIC 0x08 // 0: No hardware stereoscopic signal support +// 1: Hardware stereoscopic signal support +// +#define VESA_BIOS_EXTENSIONS_CAPABILITY_VESA_EVC 0x10 // 0: Stero signaling supported via external VESA stereo connector +// 1: Stero signaling supported via VESA EVC connector +// +// Super VGA mode number bite field definitions +// +#define VESA_BIOS_EXTENSIONS_MODE_NUMBER_VESA 0x0100 // 0: Not a VESA defined VBE mode +// 1: A VESA defined VBE mode +// +#define VESA_BIOS_EXTENSIONS_MODE_NUMBER_REFRESH_CONTROL_USER 0x0800 // 0: Use current BIOS default referesh rate +// 1: Use the user specified CRTC values for refresh rate +// +#define VESA_BIOS_EXTENSIONS_MODE_NUMBER_LINEAR_FRAME_BUFFER 0x4000 // 0: Use a banked/windowed frame buffer +// 1: Use a linear/flat frame buffer +// +#define VESA_BIOS_EXTENSIONS_MODE_NUMBER_PRESERVE_MEMORY 0x8000 // 0: Clear display memory +// 1: Preseve display memory +// +// Super VGA Information Block mode list terminator value +// +#define VESA_BIOS_EXTENSIONS_END_OF_MODE_LIST 0xffff + +// +// Window Function +// +typedef +VOID +(*VESA_BIOS_EXTENSIONS_WINDOW_FUNCTION) ( + VOID + ); + +// +// Super VGA Mode Information Block +// +typedef struct { + // + // Manadory fields for all VESA Bios Extensions revisions + // + UINT16 ModeAttributes; // Mode attributes + UINT8 WinAAttributes; // Window A attributes + UINT8 WinBAttributes; // Window B attributes + UINT16 WinGranularity; // Window granularity in k + UINT16 WinSize; // Window size in k + UINT16 WinASegment; // Window A segment + UINT16 WinBSegment; // Window B segment + UINT32 WindowFunction; // Pointer to window function + UINT16 BytesPerScanLine; // Bytes per scanline + // + // Manadory fields for VESA Bios Extensions 1.2 and above + // + UINT16 XResolution; // Horizontal resolution + UINT16 YResolution; // Vertical resolution + UINT8 XCharSize; // Character cell width + UINT8 YCharSize; // Character cell height + UINT8 NumberOfPlanes; // Number of memory planes + UINT8 BitsPerPixel; // Bits per pixel + UINT8 NumberOfBanks; // Number of CGA style banks + UINT8 MemoryModel; // Memory model type + UINT8 BankSize; // Size of CGA style banks + UINT8 NumberOfImagePages; // Number of images pages + UINT8 Reserved1; // Reserved + UINT8 RedMaskSize; // Size of direct color red mask + UINT8 RedFieldPosition; // Bit posn of lsb of red mask + UINT8 GreenMaskSize; // Size of direct color green mask + UINT8 GreenFieldPosition; // Bit posn of lsb of green mask + UINT8 BlueMaskSize; // Size of direct color blue mask + UINT8 BlueFieldPosition; // Bit posn of lsb of blue mask + UINT8 RsvdMaskSize; // Size of direct color res mask + UINT8 RsvdFieldPosition; // Bit posn of lsb of res mask + UINT8 DirectColorModeInfo; // Direct color mode attributes + // + // Manadory fields for VESA Bios Extensions 2.0 and above + // + UINT32 PhysBasePtr; // Physical Address for flat memory frame buffer + UINT32 Reserved2; // Reserved + UINT16 Reserved3; // Reserved + // + // Manadory fields for VESA Bios Extensions 3.0 and above + // + UINT16 LinBytesPerScanLine; // Bytes/scan line for linear modes + UINT8 BnkNumberOfImagePages; // Number of images for banked modes + UINT8 LinNumberOfImagePages; // Number of images for linear modes + UINT8 LinRedMaskSize; // Size of direct color red mask (linear mode) + UINT8 LinRedFieldPosition; // Bit posiiton of lsb of red mask (linear modes) + UINT8 LinGreenMaskSize; // Size of direct color green mask (linear mode) + UINT8 LinGreenFieldPosition; // Bit posiiton of lsb of green mask (linear modes) + UINT8 LinBlueMaskSize; // Size of direct color blue mask (linear mode) + UINT8 LinBlueFieldPosition; // Bit posiiton of lsb of blue mask (linear modes) + UINT8 LinRsvdMaskSize; // Size of direct color reserved mask (linear mode) + UINT8 LinRsvdFieldPosition; // Bit posiiton of lsb of reserved mask (linear modes) + UINT32 MaxPixelClock; // Maximum pixel clock (in Hz) for graphics mode + UINT8 Pad[190]; // Pad to 256 byte block size +} VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK; + +// +// Super VGA Mode Information Block ModeAttributes field bit defintions +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_HARDWARE 0x0001 // 0: Mode not supported in handware +// 1: Mode supported in handware +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_TTY 0x0004 // 0: TTY Output functions not supported by BIOS +// 1: TTY Output functions supported by BIOS +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_COLOR 0x0008 // 0: Monochrome mode +// 1: Color mode +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_GRAPHICS 0x0010 // 0: Text mode +// 1: Graphics mode +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_NOT_VGA 0x0020 // 0: VGA compatible mode +// 1: Not a VGA compatible mode +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_NOT_WINDOWED 0x0040 // 0: VGA compatible windowed memory mode +// 1: Not a VGA compatible windowed memory mode +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER 0x0080 // 0: No linear fram buffer mode available +// 1: Linear frame buffer mode available +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_DOUBLE_SCAN 0x0100 // 0: No double scan mode available +// 1: Double scan mode available +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_INTERLACED 0x0200 // 0: No interlaced mode is available +// 1: Interlaced mode is available +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_NO_TRIPPLE_BUFFER 0x0400 // 0: No hardware triple buffer mode support available +// 1: Hardware triple buffer mode support available +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_STEREOSCOPIC 0x0800 // 0: No hardware steroscopic display support +// 1: Hardware steroscopic display support +// +#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_DUAL_DISPLAY 0x1000 // 0: No dual display start address support +// 1: Dual display start address support +// +// Super VGA Mode Information Block WinAAttribite/WinBAttributes field bit defintions +// +#define VESA_BIOS_EXTENSIONS_WINX_ATTRIBUTE_RELOCATABLE 0x01 // 0: Single non-relocatable window only +// 1: Relocatable window(s) are supported +// +#define VESA_BIOS_EXTENSIONS_WINX_ATTRIBUTE_READABLE 0x02 // 0: Window is not readable +// 1: Window is readable +// +#define VESA_BIOS_EXTENSIONS_WINX_ATTRIBUTE_WRITABLE 0x04 // 0: Window is not writable +// 1: Window is writable +// +// Super VGA Mode Information Block DirectColorMode field bit defintions +// +#define VESA_BIOS_EXTENSIONS_DIRECT_COLOR_MODE_PROG_COLOR_RAMP 0x01 // 0: Color ram is fixed +// 1: Color ramp is programmable +// +#define VESA_BIOS_EXTENSIONS_DIRECT_COLOR_MODE_RSVD_USABLE 0x02 // 0: Bits in Rsvd field are reserved +// 1: Bits in Rsdv field are usable +// +// Super VGA Memory Models +// +typedef enum { + MemPL = 3, // Planar memory model + MemPK = 4, // Packed pixel memory model + MemRGB= 6, // Direct color RGB memory model + MemYUV= 7 // Direct color YUV memory model +} VESA_BIOS_EXTENSIONS_MEMORY_MODELS; + +// +// Super VGA CRTC Information Block +// +typedef struct { + UINT16 HorizontalTotal; // Horizontal total in pixels + UINT16 HorizontalSyncStart; // Horizontal sync start in pixels + UINT16 HorizontalSyncEnd; // Horizontal sync end in pixels + UINT16 VericalTotal; // Vertical total in pixels + UINT16 VericalSyncStart; // Vertical sync start in pixels + UINT16 VericalSyncEnd; // Vertical sync end in pixels + UINT8 Flags; // Flags (Interlaced/DoubleScan/etc). + UINT32 PixelClock; // Pixel clock in units of Hz + UINT16 RefreshRate; // Refresh rate in units of 0.01 Hz + UINT8 Reserved[40]; // Pad +} VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK; + +#define VESA_BIOS_EXTENSIONS_CRTC_FLAGS_DOUBLE_SCAN 0x01 // 0: Graphics mode is not souble scanned +// 1: Graphics mode is double scanned +// +#define VESA_BIOS_EXTENSIONS_CRTC_FLAGSINTERLACED 0x02 // 0: Graphics mode is not interlaced +// 1: Graphics mode is interlaced +// +#define VESA_BIOS_EXTENSIONS_CRTC_HORIZONTAL_SYNC_NEGATIVE 0x04 // 0: Horizontal sync polarity is positive(+) +// 0: Horizontal sync polarity is negative(-) +// +#define VESA_BIOS_EXTENSIONS_CRTC_VERITICAL_SYNC_NEGATIVE 0x08 // 0: Verical sync polarity is positive(+) +// 0: Verical sync polarity is negative(-) +// +// Turn off byte packing of data structures +// +#pragma pack() + +#endif diff --git a/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/VideoDxe.inf b/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/VideoDxe.inf new file mode 100644 index 0000000000..bf5f255dcf --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/VideoDxe.inf @@ -0,0 +1,80 @@ +## @file +# Video driver based on legacy bios. +# +# This driver by using Legacy Bios protocol service to support csm Video +# and produce Graphics Output Protocol. +# +# Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions +# of the BSD License which accompanies this distribution. The +# full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BiosVideoDxe + FILE_GUID = 0B04B2ED-861C-42cd-A22F-C3AAFACCB896 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = BiosVideoEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gBiosVideoDriverBinding +# COMPONENT_NAME = gBiosVideoComponentName +# + +[Sources] + BiosVideo.c + BiosVideo.h + ComponentName.c + VesaBiosExtensions.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + IntelFrameworkPkg/IntelFrameworkPkg.dec + IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec + + +[LibraryClasses] + MemoryAllocationLib + DevicePathLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + ReportStatusCodeLib + DebugLib + PcdLib + + +[Guids] + gEfiLegacyBiosGuid # ALWAYS_PRODUCED + gEfiEventExitBootServicesGuid + +[Protocols] + gEfiVgaMiniPortProtocolGuid # PROTOCOL BY_START + gEfiEdidDiscoveredProtocolGuid # PROTOCOL BY_START + gEfiGraphicsOutputProtocolGuid # PROTOCOL BY_START + gEfiEdidActiveProtocolGuid # PROTOCOL BY_START + gEfiLegacyBiosProtocolGuid # PROTOCOL TO_START + gEfiPciIoProtocolGuid # PROTOCOL TO_START + gEfiDevicePathProtocolGuid # PROTOCOL TO_START + gEfiEdidOverrideProtocolGuid # PROTOCOL TO_START + +[Pcd] + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdBiosVideoSetTextVgaModeEnable + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdBiosVideoCheckVbeEnable + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdBiosVideoCheckVgaEnable diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/IA32/InterruptTable.S b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/IA32/InterruptTable.S new file mode 100644 index 0000000000..a785256052 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/IA32/InterruptTable.S @@ -0,0 +1,67 @@ +## @file +# Interrupt Redirection Template +# +# Copyright (c) 2006, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions +# of the BSD License which accompanies this distribution. The +# full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +ASM_GLOBAL ASM_PFX(InterruptRedirectionTemplate) + +#---------------------------------------------------------------------------- +# Procedure: InterruptRedirectionTemplate: Redirects interrupts 0x68-0x6F +# +# Input: None +# +# Output: None +# +# Prototype: VOID +# InterruptRedirectionTemplate ( +# VOID +# ); +# +# Saves: None +# +# Modified: None +# +# Description: Contains the code that is copied into low memory (below 640K). +# This code reflects interrupts 0x68-0x6f to interrupts 0x08-0x0f. +# This template must be copied into low memory, and the IDT entries +# 0x68-0x6F must be point to the low memory copy of this code. Each +# entry is 4 bytes long, so IDT entries 0x68-0x6F can be easily +# computed. +# +#---------------------------------------------------------------------------- +ASM_PFX(InterruptRedirectionTemplate): + int $0x8 + .byte 0xcf + nop + int $0x9 + .byte 0xcf + nop + int $0xa + .byte 0xcf + nop + int $0xb + .byte 0xcf + nop + int $0xc + .byte 0xcf + nop + int $0xd + .byte 0xcf + nop + int $0xe + .byte 0xcf + nop + int $0xf + .byte 0xcf + nop diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/IA32/InterruptTable.asm b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/IA32/InterruptTable.asm new file mode 100644 index 0000000000..410ce5be6e --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/IA32/InterruptTable.asm @@ -0,0 +1,73 @@ +;; @file +; Interrupt Redirection Template +; +; Copyright (c) 2006, Intel Corporation. All rights reserved.
+; +; This program and the accompanying materials +; are licensed and made available under the terms and conditions +; of the BSD License which accompanies this distribution. The +; full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +;; + +.686P +.MODEL FLAT, C +.CODE + +;---------------------------------------------------------------------------- +; Procedure: InterruptRedirectionTemplate: Redirects interrupts 0x68-0x6F +; +; Input: None +; +; Output: None +; +; Prototype: VOID +; InterruptRedirectionTemplate ( +; VOID +; ); +; +; Saves: None +; +; Modified: None +; +; Description: Contains the code that is copied into low memory (below 640K). +; This code reflects interrupts 0x68-0x6f to interrupts 0x08-0x0f. +; This template must be copied into low memory, and the IDT entries +; 0x68-0x6F must be point to the low memory copy of this code. Each +; entry is 4 bytes long, so IDT entries 0x68-0x6F can be easily +; computed. +; +;---------------------------------------------------------------------------- + +InterruptRedirectionTemplate PROC C + int 08h + DB 0cfh ; IRET + nop + int 09h + DB 0cfh ; IRET + nop + int 0ah + DB 0cfh ; IRET + nop + int 0bh + DB 0cfh ; IRET + nop + int 0ch + DB 0cfh ; IRET + nop + int 0dh + DB 0cfh ; IRET + nop + int 0eh + DB 0cfh ; IRET + nop + int 0fh + DB 0cfh ; IRET + nop +InterruptRedirectionTemplate ENDP + +END \ No newline at end of file diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfBootSupport.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfBootSupport.c new file mode 100644 index 0000000000..b6787ae0e7 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfBootSupport.c @@ -0,0 +1,277 @@ +/** @file + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "LegacyBiosInterface.h" + +/** + Assign drive number to legacy HDD drives prior to booting an EFI + aware OS so the OS can access drives without an EFI driver. + Note: BBS compliant drives ARE NOT available until this call by + either shell or EFI. + + @param This Protocol instance pointer. + @param BbsCount Number of BBS_TABLE structures + @param BbsTable List BBS entries + + @retval EFI_SUCCESS Drive numbers assigned + +**/ +EFI_STATUS +EFIAPI +LegacyBiosPrepareToBootEfi ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + OUT UINT16 *BbsCount, + OUT BBS_TABLE **BbsTable + ) +{ + // + // Shadow All Opion ROM + // + LegacyBiosShadowAllLegacyOproms (This); + return EFI_SUCCESS; +} + + +/** + To boot from an unconventional device like parties and/or execute + HDD diagnostics. + + @param This Protocol instance pointer. + @param Attributes How to interpret the other input parameters + @param BbsEntry The 0-based index into the BbsTable for the + parent device. + @param BeerData Pointer to the 128 bytes of ram BEER data. + @param ServiceAreaData Pointer to the 64 bytes of raw Service Area data. + The caller must provide a pointer to the specific + Service Area and not the start all Service Areas. + EFI_INVALID_PARAMETER if error. Does NOT return if no error. + +**/ +EFI_STATUS +EFIAPI +LegacyBiosBootUnconventionalDevice ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UDC_ATTRIBUTES Attributes, + IN UINTN BbsEntry, + IN VOID *BeerData, + IN VOID *ServiceAreaData + ) +{ + return EFI_INVALID_PARAMETER; +} + + +/** + Attempt to legacy boot the BootOption. If the EFI contexted has been + compromised this function will not return. + + @param This Protocol instance pointer. + @param BbsDevicePath EFI Device Path from BootXXXX variable. + @param LoadOptionsSize Size of LoadOption in size. + @param LoadOptions LoadOption from BootXXXX variable + + @retval EFI_SUCCESS Removable media not present + +**/ +EFI_STATUS +EFIAPI +LegacyBiosLegacyBoot ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN BBS_BBS_DEVICE_PATH *BbsDevicePath, + IN UINT32 LoadOptionsSize, + IN VOID *LoadOptions + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Build the E820 table. + + @param Private Legacy BIOS Instance data + @param Size Size of E820 Table + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosBuildE820 ( + IN LEGACY_BIOS_INSTANCE *Private, + OUT UINTN *Size + ) +{ + *Size = 0; + return EFI_SUCCESS; +} + +/** + Get all BBS info + + @param This Protocol instance pointer. + @param HddCount Number of HDD_INFO structures + @param HddInfo Onboard IDE controller information + @param BbsCount Number of BBS_TABLE structures + @param BbsTable List BBS entries + + @retval EFI_SUCCESS Tables returned + @retval EFI_NOT_FOUND resource not found + @retval EFI_DEVICE_ERROR can not get BBS table + +**/ +EFI_STATUS +EFIAPI +LegacyBiosGetBbsInfo ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + OUT UINT16 *HddCount, + OUT HDD_INFO **HddInfo, + OUT UINT16 *BbsCount, + OUT BBS_TABLE **BbsTable + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Fill in the standard BDA for Keyboard LEDs + + @param This Protocol instance pointer. + @param Leds Current LED status + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +EFIAPI +LegacyBiosUpdateKeyboardLedStatus ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINT8 Leds + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Relocate this image under 4G memory for IPF. + + @param ImageHandle Handle of driver image. + @param SystemTable Pointer to system table. + + @retval EFI_SUCCESS Image successfully relocated. + @retval EFI_ABORTED Failed to relocate image. + +**/ +EFI_STATUS +RelocateImageUnder4GIfNeeded ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + UINTN NumberOfPages; + EFI_PHYSICAL_ADDRESS LoadedImageBase; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + EFI_PHYSICAL_ADDRESS MemoryAddress; + EFI_HANDLE NewImageHandle; + + Status = gBS->HandleProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID *) &LoadedImage + ); + + if (!EFI_ERROR (Status)) { + LoadedImageBase = (EFI_PHYSICAL_ADDRESS) (UINTN) LoadedImage->ImageBase; + if (LoadedImageBase > 0xffffffff) { + NumberOfPages = (UINTN) (DivU64x32(LoadedImage->ImageSize, EFI_PAGE_SIZE) + 1); + + // + // Allocate buffer below 4GB here + // + Status = AllocateLegacyMemory ( + AllocateMaxAddress, + 0x7FFFFFFF, + NumberOfPages, // do we have to convert this to pages?? + &MemoryAddress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ZeroMem (&ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT)); + ImageContext.Handle = (VOID *)(UINTN)LoadedImageBase; + ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; + + // + // Get information about the image being loaded + // + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + return Status; + } + ImageContext.ImageAddress = (PHYSICAL_ADDRESS)MemoryAddress; + // + // Align buffer on section boundry + // + ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; + ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1); + + // + // Load the image to our new buffer + // + Status = PeCoffLoaderLoadImage (&ImageContext); + if (EFI_ERROR (Status)) { + gBS->FreePages (MemoryAddress, NumberOfPages); + return Status; + } + + // + // Relocate the image in our new buffer + // + Status = PeCoffLoaderRelocateImage (&ImageContext); + if (EFI_ERROR (Status)) { + gBS->FreePages (MemoryAddress, NumberOfPages); + return Status; + } + + // + // Create a new handle with gEfiCallerIdGuid to be used as the ImageHandle fore the reloaded image + // + NewImageHandle = NULL; + Status = gBS->InstallProtocolInterface ( + &NewImageHandle, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + + // + // Flush the instruction cache so the image data is written before we execute it + // + InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); + + Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, SystemTable); + if (EFI_ERROR (Status)) { + gBS->FreePages (MemoryAddress, NumberOfPages); + return Status; + } + // + // return error directly the BS will unload this image + // + return EFI_ABORTED; + } + } + return EFI_SUCCESS; +} diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.h b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.h new file mode 100644 index 0000000000..26aa3a694b --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.h @@ -0,0 +1,102 @@ +/** @file + +Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _IPF_THUNK_H_ +#define _IPF_THUNK_H_ + +#include "LegacyBiosInterface.h" +#include + +/** + Template of real mode code. + + @param CodeStart Start address of code. + @param CodeEnd End address of code + @param ReverseThunkStart Start of reverse thunk. + @param IntThunk Low memory thunk. + +**/ +VOID +RealModeTemplate ( + OUT UINTN *CodeStart, + OUT UINTN *CodeEnd, + OUT UINTN *ReverseThunkStart, + LOW_MEMORY_THUNK *IntThunk + ); + +/** + Register physical address of Esal Data Area + + @param ReverseThunkCodeAddress Reverse Thunk Address + @param IntThunkAddress IntThunk Address + + @retval EFI_SUCCESS ESAL data area set successfully. + +**/ +EFI_STATUS +EsalSetSalDataArea ( + IN UINTN ReverseThunkCodeAddress, + IN UINTN IntThunkAddress + ); + +/** + Get address of reverse thunk. + + @retval EFI_SAL_SUCCESS Address of reverse thunk returned successfully. + +**/ +SAL_RETURN_REGS +EsalGetReverseThunkAddress ( + VOID + ); + +typedef struct { + UINT32 Eax; // 0 + UINT32 Ecx; // 4 + UINT32 Edx; // 8 + UINT32 Ebx; // 12 + UINT32 Esp; // 16 + UINT32 Ebp; // 20 + UINT32 Esi; // 24 + UINT32 Edi; // 28 + UINT32 Eflag; // 32 + UINT32 Eip; // 36 + UINT16 Cs; // 40 + UINT16 Ds; // 42 + UINT16 Es; // 44 + UINT16 Fs; // 46 + UINT16 Gs; // 48 + UINT16 Ss; // 50 +} IPF_DWORD_REGS; + +/** + Entrypoint of IA32 code. + + @param CallTypeData Data of call type + @param DwordRegister Register set of IA32 general registers + and segment registers + @param StackPointer Stack pointer. + @param StackSize Size of stack. + +**/ +VOID +EfiIaEntryPoint ( + UINT64 CallTypeData, + IPF_DWORD_REGS *DwordRegister, + UINT64 StackPointer, + UINT64 StackSize + ); + +#endif diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.i b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.i new file mode 100644 index 0000000000..441bb25e3d --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.i @@ -0,0 +1,89 @@ +//// @file +// +// Copyright (c) 2006, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions +// of the BSD License which accompanies this distribution. The +// full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +//// + +#define NUM_REAL_GDT_ENTRIES 3 +#define LOW_STACK_SIZE (8*1024) // 8k? + +// +// Low memory Thunk Structure +// +#define Code 0 +#define LowReverseThunkStart Code + 4096 +#define GdtDesc LowReverseThunkStart + 4 +#define IdtDesc GdtDesc + 6 +#define FlatSs IdtDesc + 6 +#define FlatEsp FlatSs + 4 +#define LowCodeSelector FlatEsp + 4 +#define LowDataSelector LowCodeSelector + 4 +#define LowStack LowDataSelector + 4 +#define RealModeIdtDesc LowStack + 4 +#define RealModeGdt RealModeIdtDesc + 6 +#define RealModeGdtDesc RealModeGdt + (8 * NUM_REAL_GDT_ENTRIES) +#define RevRealDs RealModeGdtDesc + 6 +#define RevRealSs RevRealDs + 2 +#define RevRealEsp RevRealSs + 2 +#define RevRealIdtDesc RevRealEsp + 4 +#define RevFlatDataSelector RevRealIdtDesc + 6 +#define RevFlatStack RevFlatDataSelector + 2 +#define Stack RevFlatStack + 4 +#define RevThunkStack Stack + LOW_STACK_SIZE + +#define EfiToLegacy16InitTable RevThunkStack + LOW_STACK_SIZE +#define InitTableBiosLessThan1MB EfiToLegacy16InitTable +#define InitTableHiPmmMemory InitTableBiosLessThan1MB + 4 +#define InitTablePmmMemorySizeInBytes InitTableHiPmmMemory + 4 +#define InitTableReverseThunkCallSegment InitTablePmmMemorySizeInBytes + 4 +#define InitTableReverseThunkCallOffset InitTableReverseThunkCallSegment + 2 +#define InitTableNumberE820Entries InitTableReverseThunkCallOffset + 2 +#define InitTableOsMemoryAbove1Mb InitTableNumberE820Entries + 4 +#define InitTableThunkStart InitTableOsMemoryAbove1Mb + 4 +#define InitTableThunkSizeInBytes InitTableThunkStart + 4 +#define InitTable16InitTableEnd InitTableThunkSizeInBytes + 4 + +#define EfiToLegacy16BootTable InitTable16InitTableEnd +#define BootTableBiosLessThan1MB EfiToLegacy16BootTable +#define BootTableHiPmmMemory BootTableBiosLessThan1MB + 4 +#define BootTablePmmMemorySizeInBytes BootTableHiPmmMemory + 4 +#define BootTableReverseThunkCallSegment BootTablePmmMemorySizeInBytes + 4 +#define BootTableReverseThunkCallOffset BootTableReverseThunkCallSegment + 2 +#define BootTableNumberE820Entries BootTableReverseThunkCallOffset + 2 +#define BootTableOsMemoryAbove1Mb BootTableNumberE820Entries + 4 +#define BootTableThunkStart BootTableOsMemoryAbove1Mb + 4 +#define BootTableThunkSizeInBytes BootTableThunkStart + 4 +#define EfiToLegacy16BootTableEnd BootTableThunkSizeInBytes + 4 + +#define InterruptRedirectionCode EfiToLegacy16BootTableEnd +#define PciHandler InterruptRedirectionCode + 32 + + +// +// Register Sets (16 Bit) +// + +#define AX 0 +#define BX 2 +#define CX 4 +#define DX 6 +#define SI 8 +#define DI 10 +#define Flags 12 +#define ES 14 +#define CS 16 +#define SS 18 +#define DS 20 +#define BP 22 + + + diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.s b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.s new file mode 100644 index 0000000000..d08f781319 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/IpfThunk.s @@ -0,0 +1,524 @@ +//// @file +// +// Copyright (c) 1999 - 2008, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions +// of the BSD License which accompanies this distribution. The +// full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +//// + +.file "IpfThunk.s" + +#include "IpfMacro.i" +#include "Ipf/IpfThunk.i" + +.align 0x10 +//----------------------------------------------------------------------------- +//++ +// EfiIaEntryPoint +// +// Register physical address of Esal Data Area +// +// On Entry : +// in1 = ptr to legacy bios reg +// in2 = ptr to Call Stack +// in3 = Call Stack Size +// +// Return Value: +// r8 = SAL_SUCCESS +// +// As per static calling conventions. +// +//-- +//--------------------------------------------------------------------------- +PROCEDURE_ENTRY(EfiIaEntryPoint) + + alloc loc0 = 8,10,8,0;; + + mov out0 = r0;; + mov out1 = r0;; + mov out2 = r0;; + mov out3 = r0;; + mov out4 = r0;; + mov out5 = r0;; + mov out6 = r0;; + mov out7 = r0;; + + mov loc1 = b0;; // save efi (b0) + mov loc2 = psr;; // save efi (PSR) + mov loc3 = gp;; // save efi (GP) + mov loc4 = pr;; // save efi (PR) + mov loc5 = sp;; // save efi (SP) + mov loc6 = r13;; // save efi (TP) + mov loc7 = ar.lc;; // save efi (LC) + mov loc8 = ar.fpsr;; // save efi (FPSR) + + mov r8 = r0;; // return status + mov r9 = r0;; // return value + mov r10 = r0;; // return value + mov r11 = r0;; // return value + +bios_int_func:: + rsm 0x4000;; // i(14)=0, disable interrupt + srlz.d;; + srlz.i;; + +//---------------------// +// save fp registers // +//---------------------// + + dep sp = 0,sp,0,4;; // align 16 + add sp = -16,sp;; // post decrement + +int_ip_1x:: + mov r2 = ip;; + add r2 = (int_ip_1y - int_ip_1x),r2;; + mov b7 = r2;; + br save_fp_registers;; + +int_ip_1y:: + add sp = 16,sp;; // adjust (SP) + mov loc9 = sp;; // save (SP) + adds sp = 0x10,in1;; // in1 + 0x10 = SP + ld4 sp = [sp];; // SP + adds r17 = 0x32,in1;; // in1 + 0x32 = SS + ld2 r17 = [r17];; // SS + movl r2 = 0xffffffff;; // if no SS:SP, then define new SS:SP + cmp.ne p6,p0 = sp,r2;; + movl r2 = 0xffff;; + cmp.ne.or p6,p0 = r17,r2;; + (p6) br.sptk bif_1;; + + mov sp = in3;; // 16-bit stack pointer + mov r2 = psr;; + tbit.z p6,p7 = r2,17;; // psr.dt (Physical OR Virtual) + +bif_ip1x:: + mov r2 = in2;; // ia32 callback stack top + mov r3 = in3;; // 16-bit stack pointer + sub r2 = r2,r3;; + shr.u r17 = r2,4;; // 16-bit stack segment + +bif_1:: + extr.u sp = sp,0,16;; // SP (16-bit sp for legacy code) + dep sp = 0,sp,0,3;; // align 8 + cmp.eq p6,p0 = 0,sp;; // if SP=0000 then wrap to 0x10000 + (p6) dep sp = -1,sp,16,1;; + shladd r2 = r17,4,sp;; // ESP = SS<<4+SP + add r2 = -8,r2;; // post decrement 64 bit pointer + add sp = -8,sp;; // post decrement SP + +sale_ip1x:: + mov r18 = ip;; + adds r18 = (sale_ip1y - sale_ip1x),r18;; + sub r18 = r18,r2;; // return address - CS base + add r18 = r18,sp;; // adjustment for stack + shl r18 = r18,32;; + movl r19 = 0xb80f66fa;; // CLI, JMPE xxxxxxxx + or r18 = r18,r19;; + st8 [r2] = r18;; // (FA,66,0F,B8,xx,xx,xx,xx) + + cmp.eq p6,p0 = 0,sp;; // if SP=0000 then wrap to 0x10000 + (p6) dep sp = -1,sp,16,1;; + shladd r2 = r17,4,sp;; // ESP=SS<<4+SP + add r2 = -2,r2;; // post decrement 64 bit pointer + add sp = -2,sp;; // post decrement SP + + movl r18 = 0x8000000000000100;; // CALL FAR function + cmp.eq p6,p7 = in0,r18;; + (p6) add r19 = 0x28,in1;; // in1 + 0x28 = CS + (p6) ld2 r18 = [r19],-4;; // CS + (p6) st2 [r2] = r18,-2;; // in1 + 0x24 = EIP + (p6) ld2 r18 = [r19];; // EIP + (p6) st2 [r2] = r18,-2;; // + (p6) movl r18 = 0x9a90;; // nop, CALLFAR xxxx:yyyy + + (p7) movl r18 = 0xcd;; // INT xx + (p7) dep r18 = in0,r18,8,8;; + st2 [r2] = r18;; // (CD,xx) + + mov r18 = r2;; // EIP for legacy execution + +//------------------------------// +// flush 32 bytes legacy code // +//------------------------------// + + dep r2 = 0,r2,0,5;; // align to 32 + fc r2;; + sync.i;; + srlz.i;; + srlz.d;; + +//------------------------------// +// load legacy registers // +//------------------------------// + mov r2 = in1;; // IA32 BIOS register state + ld4 r8 = [r2],4;; // in1 + 0 = EAX + ld4 r9 = [r2],4;; // in1 + 4 = ECX + ld4 r10 = [r2],4;; // in1 + 8 = EDX + ld4 r11 = [r2],4;; // in1 + 12 = EBX + + add r2 = 4,r2;; // in1 + 16 = ESP (skip) + + ld4 r13 = [r2],4;; // in1 + 20 = EBP + ld4 r14 = [r2],4;; // in1 + 24 = ESI + ld4 r15 = [r2],4;; // in1 + 28 = EDI + ld4 r3 = [r2],4;; // in1 + 32 = EFLAGS + mov ar.eflag = r3;; + + add r2 = 4,r2;; // in1 + 36 = EIP (skip) + add r2 = 2,r2;; // in1 + 40 = CS (skip) + + ld2 r16 = [r2],2;; // in1 + 42 = DS, (r16 = GS,FS,ES,DS) + movl r27 = 0xc93fffff00000000;; + dep r27 = r16,r27,4,16;; // r27 = DSD + + ld2 r19 = [r2],2;; // in1 + 44 = ES + dep r16 = r19,r16,16,16;; + movl r24 = 0xc93fffff00000000;; + dep r24 = r19,r24,4,16;; // r24 = ESD + + ld2 r19 = [r2],2;; // in1 + 46 = FS + dep r16 = r19,r16,32,16;; + movl r28 = 0xc93fffff00000000;; + dep r28 = r19,r28,4,16;; // r28 = FSD + + ld2 r19 = [r2],2;; // in1 + 48 = GS + dep r16 = r19,r16,48,16;; + movl r29 = 0xc93fffff00000000;; + dep r29 = r19,r29,4,16;; // r29 = GSD + + mov r30 = r0;; // r30 = LDTD, clear NaT + mov r31 = r0;; // r31 = GDTD, clear NaT + + dep r17 = r17,r17,16,16;; // CS = SS, (r17 = TSS,LDT,SS,CS) + + movl r3 = 0x0930ffff00000000;; + dep r3 = r17,r3,4,16;; + mov ar.csd = r3;; // ar25 = CSD + mov ar.ssd = r3;; // ar26 = SSD + +//------------------------------// +// give control to INT function // +//------------------------------// + + br.call.sptk b0 = execute_int_function;; + +//------------------------------// +// store legacy registers // +//------------------------------// + + mov r2 = in1;; + st4 [r2] = r8,4;; // EAX + st4 [r2] = r9,4;; // ECX + st4 [r2] = r10,4;; // EDX + st4 [r2] = r11,4;; // EBX + + add r2 = 4,r2;; // ESP (skip) + + st4 [r2] = r13,4;; // EBP + st4 [r2] = r14,4;; // ESI + st4 [r2] = r15,4;; // EDI + + mov r3 = ar.eflag;; + st4 [r2] = r3,4;; // EFLAGS + + add r2 = 4,r2;; // EIP (skip) + add r2 = 2,r2;; // CS (skip) + + st2 [r2] = r16,2;; // DS, (r16 = GS,FS,ES,DS) + + extr.u r3 = r16,16,16;; + st2 [r2] = r3,2;; // ES + + extr.u r3 = r16,32,16;; + st2 [r2] = r3,2;; // FS + + extr.u r3 = r16,48,16;; + st2 [r2] = r3,2;; // GS + +//------------------------------// +// restore fp registers // +//------------------------------// + mov sp = loc9;; // restore (SP) +int_ip_2x:: + mov r2 = ip;; + add r2 = (int_ip_2y - int_ip_2x),r2;; + mov b7 = r2;; + br restore_fp_registers;; + +int_ip_2y:: + mov r8 = r0;; // return status + mov r9 = r0;; // return value + mov r10 = r0;; // return value + mov r11 = r0;; // return value + + mov ar.fpsr = loc8;; // restore efi (FPSR) + mov ar.lc = loc7;; // restore efi (LC) + mov r13 = loc6;; // restore efi (TP) + mov sp = loc5;; // restore efi (SP) + mov pr = loc4;; // restore efi (PR) + mov gp = loc3;; // restore efi (GP) + mov psr.l = loc2;; // restore efi (PSR) + srlz.d;; + srlz.i;; + mov b0 = loc1;; // restore efi (b0) + mov ar.pfs = loc0;; + br.ret.sptk b0;; // return to efi + +PROCEDURE_EXIT (EfiIaEntryPoint) + +//==============================// +// EXECUTE_INT_FUNCTION // +//==============================// +// switch to virtual address // +//------------------------------// + +execute_int_function:: + + alloc r2 = 0,0,0,0;; // cfm.sof=0 + flushrs;; + + rsm 0x2000;; // ic(13)=0 for control register programming + srlz.d;; + srlz.i;; + + mov r2 = psr;; + dep r2 = -1,r2,34,1;; // set is(34) + dep r2 = -1,r2,44,1;; // set bn(44) + dep r2 = -1,r2,36,1;; // set it(36) + dep r2 = -1,r2,27,1;; // set rt(27) + dep r2 = -1,r2,17,1;; // set dt(17) + dep r2 = 0,r2,3,1;; // reset ac(3) + dep r2 = -1,r2,13,1;; // set ic(13) + + mov cr.ipsr = r2;; + mov cr.ifs = r0;; // clear interruption function state register + mov cr.iip = r18;; + + rfi;; // go to legacy code execution + +//------------------------------// +// back from legacy code // +//------------------------------// +// switch to physical address // +//------------------------------// + +sale_ip1y:: + rsm 0x6000;; // i(14)=0,ic(13)=0 for control reg programming + srlz.d;; + srlz.i;; + + mov r2 = psr;; + dep r2 = -1,r2,44,1;; // set bn(44) + dep r2 = 0,r2,36,1;; // reset it(36) + dep r2 = 0,r2,27,1;; // reset rt(27) + dep r2 = 0,r2,17,1;; // reset dt(17) + dep r2 = -1,r2,13,1;; // set ic(13) + mov cr.ipsr = r2;; + +sale_ip2x:: + mov r2 = ip;; + add r2 = (sale_ip2y - sale_ip2x),r2;; + mov cr.ifs = r0;; // clear interruption function state register + mov cr.iip = r2;; + rfi;; + +sale_ip2y:: + br.ret.sptk b0;; // return to SAL + +//------------------------------// +// store fp registers // +//------------------------------// +save_fp_registers:: + stf.spill [sp]=f2,-16;; stf.spill [sp]=f3,-16;; + stf.spill [sp]=f4,-16;; stf.spill [sp]=f5,-16;; stf.spill [sp]=f6,-16;; stf.spill [sp]=f7,-16;; + stf.spill [sp]=f8,-16;; stf.spill [sp]=f9,-16;; stf.spill [sp]=f10,-16;; stf.spill [sp]=f11,-16;; + stf.spill [sp]=f12,-16;; stf.spill [sp]=f13,-16;; stf.spill [sp]=f14,-16;; stf.spill [sp]=f15,-16;; + stf.spill [sp]=f16,-16;; stf.spill [sp]=f17,-16;; stf.spill [sp]=f18,-16;; stf.spill [sp]=f19,-16;; + stf.spill [sp]=f20,-16;; stf.spill [sp]=f21,-16;; stf.spill [sp]=f22,-16;; stf.spill [sp]=f23,-16;; + stf.spill [sp]=f24,-16;; stf.spill [sp]=f25,-16;; stf.spill [sp]=f26,-16;; stf.spill [sp]=f27,-16;; + stf.spill [sp]=f28,-16;; stf.spill [sp]=f29,-16;; stf.spill [sp]=f30,-16;; stf.spill [sp]=f31,-16;; + stf.spill [sp]=f32,-16;; stf.spill [sp]=f33,-16;; stf.spill [sp]=f34,-16;; stf.spill [sp]=f35,-16;; + stf.spill [sp]=f36,-16;; stf.spill [sp]=f37,-16;; stf.spill [sp]=f38,-16;; stf.spill [sp]=f39,-16;; + stf.spill [sp]=f40,-16;; stf.spill [sp]=f41,-16;; stf.spill [sp]=f42,-16;; stf.spill [sp]=f43,-16;; + stf.spill [sp]=f44,-16;; stf.spill [sp]=f45,-16;; stf.spill [sp]=f46,-16;; stf.spill [sp]=f47,-16;; + stf.spill [sp]=f48,-16;; stf.spill [sp]=f49,-16;; stf.spill [sp]=f50,-16;; stf.spill [sp]=f51,-16;; + stf.spill [sp]=f52,-16;; stf.spill [sp]=f53,-16;; stf.spill [sp]=f54,-16;; stf.spill [sp]=f55,-16;; + stf.spill [sp]=f56,-16;; stf.spill [sp]=f57,-16;; stf.spill [sp]=f58,-16;; stf.spill [sp]=f59,-16;; + stf.spill [sp]=f60,-16;; stf.spill [sp]=f61,-16;; stf.spill [sp]=f62,-16;; stf.spill [sp]=f63,-16;; + stf.spill [sp]=f64,-16;; stf.spill [sp]=f65,-16;; stf.spill [sp]=f66,-16;; stf.spill [sp]=f67,-16;; + stf.spill [sp]=f68,-16;; stf.spill [sp]=f69,-16;; stf.spill [sp]=f70,-16;; stf.spill [sp]=f71,-16;; + stf.spill [sp]=f72,-16;; stf.spill [sp]=f73,-16;; stf.spill [sp]=f74,-16;; stf.spill [sp]=f75,-16;; + stf.spill [sp]=f76,-16;; stf.spill [sp]=f77,-16;; stf.spill [sp]=f78,-16;; stf.spill [sp]=f79,-16;; + stf.spill [sp]=f80,-16;; stf.spill [sp]=f81,-16;; stf.spill [sp]=f82,-16;; stf.spill [sp]=f83,-16;; + stf.spill [sp]=f84,-16;; stf.spill [sp]=f85,-16;; stf.spill [sp]=f86,-16;; stf.spill [sp]=f87,-16;; + stf.spill [sp]=f88,-16;; stf.spill [sp]=f89,-16;; stf.spill [sp]=f90,-16;; stf.spill [sp]=f91,-16;; + stf.spill [sp]=f92,-16;; stf.spill [sp]=f93,-16;; stf.spill [sp]=f94,-16;; stf.spill [sp]=f95,-16;; + stf.spill [sp]=f96,-16;; stf.spill [sp]=f97,-16;; stf.spill [sp]=f98,-16;; stf.spill [sp]=f99,-16;; + stf.spill [sp]=f100,-16;;stf.spill [sp]=f101,-16;;stf.spill [sp]=f102,-16;;stf.spill [sp]=f103,-16;; + stf.spill [sp]=f104,-16;;stf.spill [sp]=f105,-16;;stf.spill [sp]=f106,-16;;stf.spill [sp]=f107,-16;; + stf.spill [sp]=f108,-16;;stf.spill [sp]=f109,-16;;stf.spill [sp]=f110,-16;;stf.spill [sp]=f111,-16;; + stf.spill [sp]=f112,-16;;stf.spill [sp]=f113,-16;;stf.spill [sp]=f114,-16;;stf.spill [sp]=f115,-16;; + stf.spill [sp]=f116,-16;;stf.spill [sp]=f117,-16;;stf.spill [sp]=f118,-16;;stf.spill [sp]=f119,-16;; + stf.spill [sp]=f120,-16;;stf.spill [sp]=f121,-16;;stf.spill [sp]=f122,-16;;stf.spill [sp]=f123,-16;; + stf.spill [sp]=f124,-16;;stf.spill [sp]=f125,-16;;stf.spill [sp]=f126,-16;;stf.spill [sp]=f127,-16;; + invala;; + br b7;; + +//------------------------------// +// restore fp registers // +//------------------------------// +restore_fp_registers:: + ldf.fill f127=[sp],16;;ldf.fill f126=[sp],16;;ldf.fill f125=[sp],16;;ldf.fill f124=[sp],16;; + ldf.fill f123=[sp],16;;ldf.fill f122=[sp],16;;ldf.fill f121=[sp],16;;ldf.fill f120=[sp],16;; + ldf.fill f119=[sp],16;;ldf.fill f118=[sp],16;;ldf.fill f117=[sp],16;;ldf.fill f116=[sp],16;; + ldf.fill f115=[sp],16;;ldf.fill f114=[sp],16;;ldf.fill f113=[sp],16;;ldf.fill f112=[sp],16;; + ldf.fill f111=[sp],16;;ldf.fill f110=[sp],16;;ldf.fill f109=[sp],16;;ldf.fill f108=[sp],16;; + ldf.fill f107=[sp],16;;ldf.fill f106=[sp],16;;ldf.fill f105=[sp],16;;ldf.fill f104=[sp],16;; + ldf.fill f103=[sp],16;;ldf.fill f102=[sp],16;;ldf.fill f101=[sp],16;;ldf.fill f100=[sp],16;; + ldf.fill f99=[sp],16;; ldf.fill f98=[sp],16;; ldf.fill f97=[sp],16;; ldf.fill f96=[sp],16;; + ldf.fill f95=[sp],16;; ldf.fill f94=[sp],16;; ldf.fill f93=[sp],16;; ldf.fill f92=[sp],16;; + ldf.fill f91=[sp],16;; ldf.fill f90=[sp],16;; ldf.fill f89=[sp],16;; ldf.fill f88=[sp],16;; + ldf.fill f87=[sp],16;; ldf.fill f86=[sp],16;; ldf.fill f85=[sp],16;; ldf.fill f84=[sp],16;; + ldf.fill f83=[sp],16;; ldf.fill f82=[sp],16;; ldf.fill f81=[sp],16;; ldf.fill f80=[sp],16;; + ldf.fill f79=[sp],16;; ldf.fill f78=[sp],16;; ldf.fill f77=[sp],16;; ldf.fill f76=[sp],16;; + ldf.fill f75=[sp],16;; ldf.fill f74=[sp],16;; ldf.fill f73=[sp],16;; ldf.fill f72=[sp],16;; + ldf.fill f71=[sp],16;; ldf.fill f70=[sp],16;; ldf.fill f69=[sp],16;; ldf.fill f68=[sp],16;; + ldf.fill f67=[sp],16;; ldf.fill f66=[sp],16;; ldf.fill f65=[sp],16;; ldf.fill f64=[sp],16;; + ldf.fill f63=[sp],16;; ldf.fill f62=[sp],16;; ldf.fill f61=[sp],16;; ldf.fill f60=[sp],16;; + ldf.fill f59=[sp],16;; ldf.fill f58=[sp],16;; ldf.fill f57=[sp],16;; ldf.fill f56=[sp],16;; + ldf.fill f55=[sp],16;; ldf.fill f54=[sp],16;; ldf.fill f53=[sp],16;; ldf.fill f52=[sp],16;; + ldf.fill f51=[sp],16;; ldf.fill f50=[sp],16;; ldf.fill f49=[sp],16;; ldf.fill f48=[sp],16;; + ldf.fill f47=[sp],16;; ldf.fill f46=[sp],16;; ldf.fill f45=[sp],16;; ldf.fill f44=[sp],16;; + ldf.fill f43=[sp],16;; ldf.fill f42=[sp],16;; ldf.fill f41=[sp],16;; ldf.fill f40=[sp],16;; + ldf.fill f39=[sp],16;; ldf.fill f38=[sp],16;; ldf.fill f37=[sp],16;; ldf.fill f36=[sp],16;; + ldf.fill f35=[sp],16;; ldf.fill f34=[sp],16;; ldf.fill f33=[sp],16;; ldf.fill f32=[sp],16;; + ldf.fill f31=[sp],16;; ldf.fill f30=[sp],16;; ldf.fill f29=[sp],16;; ldf.fill f28=[sp],16;; + ldf.fill f27=[sp],16;; ldf.fill f26=[sp],16;; ldf.fill f25=[sp],16;; ldf.fill f24=[sp],16;; + ldf.fill f23=[sp],16;; ldf.fill f22=[sp],16;; ldf.fill f21=[sp],16;; ldf.fill f20=[sp],16;; + ldf.fill f19=[sp],16;; ldf.fill f18=[sp],16;; ldf.fill f17=[sp],16;; ldf.fill f16=[sp],16;; + ldf.fill f15=[sp],16;; ldf.fill f14=[sp],16;; ldf.fill f13=[sp],16;; ldf.fill f12=[sp],16;; + ldf.fill f11=[sp],16;; ldf.fill f10=[sp],16;; ldf.fill f9=[sp],16;; ldf.fill f8=[sp],16;; + ldf.fill f7=[sp],16;; ldf.fill f6=[sp],16;; ldf.fill f5=[sp],16;; ldf.fill f4=[sp],16;; + ldf.fill f3=[sp],16;; ldf.fill f2=[sp],16;; + invala;; + br b7;; + +//----------------------------------------------------------------------------- +//++ +// EsalSetSalDataArea +// +// Register physical address of Esal Data Area +// +// On Entry : +// in0 = Reverse Thunk Address +// in1 = IntThunk Address +// +// Return Value: +// r8 = SAL_SUCCESS +// +// As per static calling conventions. +// +//-- +//--------------------------------------------------------------------------- + +PROCEDURE_ENTRY (EsalSetSalDataArea) + + NESTED_SETUP (4,8,0,0) + +EsalCalcStart1_3:: + mov r8 = ip;; + add r8 = (ReverseThunkAddress - EsalCalcStart1_3), r8;; + st8 [r8] = in0;; + +EsalCalcStart1_4:: + mov r8 = ip;; + add r8 = (IntThunkAddress - EsalCalcStart1_4), r8;; + st8 [r8] = in1;; + + mov r8 = r0;; + + NESTED_RETURN + +PROCEDURE_EXIT (EsalSetSalDataArea) + +//----------------------------------------------------------------------------- +//++ +// EsagGetReverseThunkAddress +// +// Register physical address of Esal Data Area +// +// On Entry : +// out0 = CodeStart +// out1 = CodeEnd +// out1 = ReverseThunkCode +// +// Return Value: +// r8 = SAL_SUCCESS +// +// As per static calling conventions. +// +//-- +//--------------------------------------------------------------------------- + +PROCEDURE_ENTRY (EsalGetReverseThunkAddress) + + NESTED_SETUP (4,8,0,0) + +EsalCalcStart1_31:: + mov r8 = ip;; + add r8 = (Ia32CodeStart - EsalCalcStart1_31), r8;; + mov r9 = r8;; + +EsalCalcStart1_41:: + mov r8 = ip;; + add r8 = (Ia32CodeEnd - EsalCalcStart1_41), r8;; + mov r10 = r8;; + +EsalCalcStart1_51:: + mov r8 = ip;; + add r8 = (ReverseThunkAddress - EsalCalcStart1_51), r8;; + mov r11 = r8;; + mov r8 = r0;; + + NESTED_RETURN + +PROCEDURE_EXIT (EsalGetReverseThunkAddress) + + +.align 16 +PROCEDURE_ENTRY (InterruptRedirectionTemplate) + data8 0x90CFCD08 + data8 0x90CFCD09 + data8 0x90CFCD0A + data8 0x90CFCD0B + data8 0x90CFCD0C + data8 0x90CFCD0D + data8 0x90CFCD0E + data8 0x90CFCD0F +PROCEDURE_EXIT (InterruptRedirectionTemplate) + +//------------------------------// +// Reverse Thunk Code // +//------------------------------// + +Ia32CodeStart:: + br.sptk.few Ia32CodeStart;; // IPF CSM integration -Bug (Write This Code) +ReverseThunkCode:: + data8 0xb80f66fa // CLI, JMPE xxxx +ReverseThunkAddress:: + data8 0 // Return Address +IntThunkAddress:: + data8 0 // IntThunk Address +Ia32CodeEnd:: + + + + diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/Thunk.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/Thunk.c new file mode 100644 index 0000000000..ca59b97ec4 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Ipf/Thunk.c @@ -0,0 +1,550 @@ +/** @file + Call into 16-bit BIOS code + + BugBug: Thunker does A20 gate. Can we get rid of this code or + put it into Legacy16 code. + +Copyright (c) 1999 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "LegacyBiosInterface.h" +#include "IpfThunk.h" + +/** + Gets the current flat GDT and IDT descriptors and store them in + Private->IntThunk. These values are used by the Thunk code. + This method must be called before every thunk in order to assure + that the correct GDT and IDT are restored after the thunk. + + @param Private Private context for Legacy BIOS + + @retval EFI_SUCCESS Should only pass. + +**/ +EFI_STATUS +LegacyBiosGetFlatDescs ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + return EFI_SUCCESS; +} + + +/** + BIOS interrupt call function. + + @param BiosInt Int number of BIOS call + @param Segment Segment number + @param Offset Offset in segment + @param Regs IA32 Register set. + @param Stack Base address of stack + @param StackSize Size of stack + + @retval EFI_SUCCESS BIOS interrupt call succeeds. + +**/ +EFI_STATUS +BiosIntCall ( + IN UINT16 BiosInt, + IN UINT16 Segment, + IN UINT16 Offset, + IN EFI_IA32_REGISTER_SET *Regs, + IN VOID *Stack, + IN UINTN StackSize + ) +{ + IPF_DWORD_REGS DwordRegs; + UINT64 IntTypeVariable; + + IntTypeVariable = 0x8000000000000000; + IntTypeVariable |= BiosInt; + + DwordRegs.Cs = Segment; + DwordRegs.Eip = Offset; + + DwordRegs.Ds = Regs->X.DS; + DwordRegs.Es = Regs->X.ES; + DwordRegs.Fs = Regs->X.ES; + DwordRegs.Gs = Regs->X.ES; + DwordRegs.Ss = 0xFFFF; + + DwordRegs.Eax = Regs->X.AX; + DwordRegs.Ebx = Regs->X.BX; + // + // Sometimes, ECX is used to pass in 32 bit data. For example, INT 1Ah, AX = B10Dh is + // "PCI BIOS v2.0c + Write Configuration DWORD" and ECX has the dword to write. + // + DwordRegs.Ecx = Regs->E.ECX; + DwordRegs.Edx = Regs->X.DX; + + DwordRegs.Ebp = Regs->X.BP; + DwordRegs.Eflag = *((UINT16 *) &Regs->X.Flags); + + DwordRegs.Edi = Regs->X.DI; + DwordRegs.Esi = Regs->X.SI; + DwordRegs.Esp = 0xFFFFFFFF; + + EfiIaEntryPoint (IntTypeVariable, &DwordRegs, ((UINTN) Stack + StackSize), StackSize); + + Regs->X.CS = DwordRegs.Cs; + + Regs->X.DS = (UINT16) DwordRegs.Ds; + Regs->X.SS = (UINT16) DwordRegs.Ss; + + Regs->E.EAX = DwordRegs.Eax; + Regs->E.EBX = DwordRegs.Ebx; + Regs->E.ECX = DwordRegs.Ecx; + Regs->E.EDX = DwordRegs.Edx; + + Regs->E.EBP = DwordRegs.Ebp; + CopyMem (&Regs->X.Flags, &DwordRegs.Eflag, sizeof (EFI_FLAGS_REG)); + + Regs->E.EDI = DwordRegs.Edi; + Regs->E.ESI = DwordRegs.Esi; + + return EFI_SUCCESS; +} + + +/** + Template of real mode code. + + @param CodeStart Start address of code. + @param CodeEnd End address of code + @param ReverseThunkStart Start of reverse thunk. + @param IntThunk Low memory thunk. + +**/ +VOID +RealModeTemplate ( + OUT UINTN *CodeStart, + OUT UINTN *CodeEnd, + OUT UINTN *ReverseThunkStart, + LOW_MEMORY_THUNK *IntThunk + ) +{ + SAL_RETURN_REGS SalStatus; + + SalStatus = EsalGetReverseThunkAddress (); + + *CodeStart = SalStatus.r9; + *CodeEnd = SalStatus.r10; + *ReverseThunkStart = SalStatus.r11; + +} + + +/** + Allocate memory < 1 MB and copy the thunker code into low memory. Se up + all the descriptors. + + @param Private Private context for Legacy BIOS + + @retval EFI_SUCCESS Should only pass. + +**/ +EFI_STATUS +LegacyBiosInitializeThunk ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + GDT32 *CodeGdt; + GDT32 *DataGdt; + UINTN CodeStart; + UINTN CodeEnd; + UINTN ReverseThunkStart; + UINT32 Base; + LOW_MEMORY_THUNK *IntThunk; + UINTN TempData; + + ASSERT (Private); + + IntThunk = Private->IntThunk; + + // + // Clear the reserved descriptor + // + ZeroMem (&(IntThunk->RealModeGdt[0]), sizeof (GDT32)); + + // + // Setup a descriptor for real-mode code + // + CodeGdt = &(IntThunk->RealModeGdt[1]); + + // + // Fill in the descriptor with our real-mode segment value + // + CodeGdt->Type = 0xA; + // + // code/read + // + CodeGdt->System = 1; + CodeGdt->Dpl = 0; + CodeGdt->Present = 1; + CodeGdt->Software = 0; + CodeGdt->Reserved = 0; + CodeGdt->DefaultSize = 0; + // + // 16 bit operands + // + CodeGdt->Granularity = 0; + + CodeGdt->LimitHi = 0; + CodeGdt->LimitLo = 0xffff; + + Base = (*((UINT32 *) &IntThunk->Code)); + CodeGdt->BaseHi = (Base >> 24) & 0xFF; + CodeGdt->BaseMid = (Base >> 16) & 0xFF; + CodeGdt->BaseLo = Base & 0xFFFF; + + // + // Setup a descriptor for read-mode data + // + DataGdt = &(IntThunk->RealModeGdt[2]); + CopyMem (DataGdt, CodeGdt, sizeof (GDT32)); + + DataGdt->Type = 0x2; + // + // read/write data + // + DataGdt->BaseHi = 0x0; + // + // Base = 0 + // + DataGdt->BaseMid = 0x0; + // + DataGdt->BaseLo = 0x0; + // + DataGdt->LimitHi = 0x0F; + // + // Limit = 4Gb + // + DataGdt->LimitLo = 0xFFFF; + // + DataGdt->Granularity = 0x1; + // + // + // Compute selector value + // + IntThunk->RealModeGdtDesc.Limit = (UINT16) (sizeof (IntThunk->RealModeGdt) - 1); + CopyMem (&IntThunk->RealModeGdtDesc.Base, (UINT32 *) &IntThunk->RealModeGdt, sizeof (UINT32)); + // + // IntThunk->RealModeGdtDesc.Base = *((UINT32*) &IntThunk->RealModeGdt); + // + IntThunk->RealModeIdtDesc.Limit = 0xFFFF; + IntThunk->RealModeIdtDesc.Base = 0; + IntThunk->LowCodeSelector = (UINT32) ((UINTN) CodeGdt - IntThunk->RealModeGdtDesc.Base); + IntThunk->LowDataSelector = (UINT32) ((UINTN) DataGdt - IntThunk->RealModeGdtDesc.Base); + + // + // Initialize low real-mode code thunk + // + RealModeTemplate (&CodeStart, &CodeEnd, &ReverseThunkStart, IntThunk); + + TempData = (UINTN) &(IntThunk->Code); + IntThunk->LowReverseThunkStart = ((UINT32) TempData + (UINT32) (ReverseThunkStart - CodeStart)); + + EsalSetSalDataArea (TempData, (UINTN) IntThunk); + CopyMem (IntThunk->Code, (VOID *) CodeStart, CodeEnd - CodeStart); + + IntThunk->EfiToLegacy16InitTable.ReverseThunkCallSegment = EFI_SEGMENT (*((UINT32 *) &IntThunk->LowReverseThunkStart)); + IntThunk->EfiToLegacy16InitTable.ReverseThunkCallOffset = EFI_OFFSET (*((UINT32 *) &IntThunk->LowReverseThunkStart)); + + return EFI_SUCCESS; +} + + +/** + Thunk to 16-bit real mode and execute a software interrupt with a vector + of BiosInt. Regs will contain the 16-bit register context on entry and + exit. + + @param This Protocol instance pointer. + @param BiosInt Processor interrupt vector to invoke + @param Regs Register contexted passed into (and returned) from + thunk to 16-bit mode + + @retval FALSE Thunk completed, and there were no BIOS errors in the + target code. See Regs for status. + @retval TRUE There was a BIOS erro in the target code. + +**/ +BOOLEAN +EFIAPI +LegacyBiosInt86 ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINT8 BiosInt, + IN EFI_IA32_REGISTER_SET *Regs + ) +{ + EFI_STATUS Status; + LEGACY_BIOS_INSTANCE *Private; + LOW_MEMORY_THUNK *IntThunk; + UINT16 *Stack16; + EFI_TPL OriginalTpl; + UINTN IaSegment; + UINTN IaOffset; + UINTN *Address; + UINTN TempData; + + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + IntThunk = Private->IntThunk; + + // + // Get the current flat GDT, IDT, and SS and store them in Private->IntThunk. + // + Status = LegacyBiosGetFlatDescs (Private); + ASSERT_EFI_ERROR (Status); + + Regs->X.Flags.Reserved1 = 1; + Regs->X.Flags.Reserved2 = 0; + Regs->X.Flags.Reserved3 = 0; + Regs->X.Flags.Reserved4 = 0; + Regs->X.Flags.IOPL = 3; + Regs->X.Flags.NT = 0; + Regs->X.Flags.IF = 1; + Regs->X.Flags.TF = 0; + Regs->X.Flags.CF = 0; + // + // Clear the error flag; thunk code may set it. + // + Stack16 = (UINT16 *) (IntThunk->Stack + LOW_STACK_SIZE); + + // + // Copy regs to low memory stack + // + Stack16 -= sizeof (EFI_IA32_REGISTER_SET) / sizeof (UINT16); + CopyMem (Stack16, Regs, sizeof (EFI_IA32_REGISTER_SET)); + + // + // Provide low stack esp + // + TempData = ((UINTN) Stack16) - ((UINTN) IntThunk); + IntThunk->LowStack = *((UINT32 *) &TempData); + + // + // Stack for reverse thunk flat mode. + // It must point to top of stack (end of stack space). + // + TempData = ((UINTN) IntThunk->RevThunkStack) + LOW_STACK_SIZE; + IntThunk->RevFlatStack = *((UINT32 *) &TempData); + + // + // The call to Legacy16 is a critical section to EFI + // + OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + // + // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases. + // + Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259LegacyMode, NULL, NULL); + ASSERT_EFI_ERROR (Status); + + // + // Call the real mode thunk code + // + TempData = BiosInt * 4; + Address = (UINTN *) TempData; + IaOffset = 0xFFFF & (*Address); + IaSegment = 0xFFFF & ((*Address) >> 16); + + Status = BiosIntCall ( + BiosInt, + (UINT16) IaSegment, + (UINT16) IaOffset, + (EFI_IA32_REGISTER_SET *) Stack16, + IntThunk, + IntThunk->LowStack + ); + + // + // Check for errors with the thunk + // + switch (Status) { + case THUNK_OK: + break; + + case THUNK_ERR_A20_UNSUP: + case THUNK_ERR_A20_FAILED: + default: + // + // For all errors, set EFLAGS.CF (used by legacy BIOS to indicate error). + // + Regs->X.Flags.CF = 1; + break; + } + + Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259ProtectedMode, NULL, NULL); + ASSERT_EFI_ERROR (Status); + + // + // End critical section + // + gBS->RestoreTPL (OriginalTpl); + + // + // Return the resulting registers + // + CopyMem (Regs, Stack16, sizeof (EFI_IA32_REGISTER_SET)); + + return (BOOLEAN) (Regs->X.Flags.CF != 0); +} + + +/** + Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the + 16-bit register context on entry and exit. Arguments can be passed on + the Stack argument + + @param This Protocol instance pointer. + @param Segment Segemnt of 16-bit mode call + @param Offset Offset of 16-bit mdoe call + @param Regs Register contexted passed into (and returned) from + thunk to 16-bit mode + @param Stack Caller allocated stack used to pass arguments + @param StackSize Size of Stack in bytes + + @retval FALSE Thunk completed, and there were no BIOS errors in the + target code. See Regs for status. + @retval TRUE There was a BIOS erro in the target code. + +**/ +BOOLEAN +EFIAPI +LegacyBiosFarCall86 ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINT16 Segment, + IN UINT16 Offset, + IN EFI_IA32_REGISTER_SET *Regs, + IN VOID *Stack, + IN UINTN StackSize + ) +{ + EFI_STATUS Status; + LEGACY_BIOS_INSTANCE *Private; + LOW_MEMORY_THUNK *IntThunk; + UINT16 *Stack16; + EFI_TPL OriginalTpl; + UINTN IaSegment; + UINTN IaOffset; + UINTN TempData; + + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + IntThunk = Private->IntThunk; + IaSegment = Segment; + IaOffset = Offset; + + // + // Get the current flat GDT and IDT and store them in Private->IntThunk. + // + Status = LegacyBiosGetFlatDescs (Private); + ASSERT_EFI_ERROR (Status); + + Regs->X.Flags.Reserved1 = 1; + Regs->X.Flags.Reserved2 = 0; + Regs->X.Flags.Reserved3 = 0; + Regs->X.Flags.Reserved4 = 0; + Regs->X.Flags.IOPL = 3; + Regs->X.Flags.NT = 0; + Regs->X.Flags.IF = 1; + Regs->X.Flags.TF = 0; + Regs->X.Flags.CF = 0; + // + // Clear the error flag; thunk code may set it. + // + Stack16 = (UINT16 *) (IntThunk->Stack + LOW_STACK_SIZE); + if (Stack != NULL && StackSize != 0) { + // + // Copy Stack to low memory stack + // + Stack16 -= StackSize / sizeof (UINT16); + CopyMem (Stack16, Stack, StackSize); + } + // + // Copy regs to low memory stack + // + Stack16 -= sizeof (EFI_IA32_REGISTER_SET) / sizeof (UINT16); + CopyMem (Stack16, Regs, sizeof (EFI_IA32_REGISTER_SET)); + + // + // Provide low stack esp + // + TempData = ((UINTN) Stack16) - ((UINTN) IntThunk); + IntThunk->LowStack = *((UINT32 *) &TempData); + + // + // The call to Legacy16 is a critical section to EFI + // + OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + // + // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases. + // + Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259LegacyMode, NULL, NULL); + ASSERT_EFI_ERROR (Status); + + // + // Call the real mode thunk code + // + Status = BiosIntCall ( + 0x100, + (UINT16) IaSegment, + (UINT16) IaOffset, + (EFI_IA32_REGISTER_SET *) Stack16, + IntThunk, + IntThunk->LowStack + ); + + // + // Check for errors with the thunk + // + switch (Status) { + case THUNK_OK: + break; + + case THUNK_ERR_A20_UNSUP: + case THUNK_ERR_A20_FAILED: + default: + // + // For all errors, set EFLAGS.CF (used by legacy BIOS to indicate error). + // + Regs->X.Flags.CF = 1; + break; + } + // + // Restore protected mode interrupt state + // + Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259ProtectedMode, NULL, NULL); + ASSERT_EFI_ERROR (Status); + + // + // End critical section + // + gBS->RestoreTPL (OriginalTpl); + + // + // Return the resulting registers + // + CopyMem (Regs, Stack16, sizeof (EFI_IA32_REGISTER_SET)); + Stack16 += sizeof (EFI_IA32_REGISTER_SET) / sizeof (UINT16); + + if (Stack != NULL && StackSize != 0) { + // + // Copy low memory stack to Stack + // + CopyMem (Stack, Stack16, StackSize); + Stack16 += StackSize / sizeof (UINT16); + } + + return (BOOLEAN) (Regs->X.Flags.CF != 0); +} diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBbs.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBbs.c new file mode 100644 index 0000000000..c15e59972d --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBbs.c @@ -0,0 +1,384 @@ +/** @file + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "LegacyBiosInterface.h" +#include + +// Give floppy 3 states +// FLOPPY_PRESENT_WITH_MEDIA = Floppy controller present and media is inserted +// FLOPPY_NOT_PRESENT = No floppy controller present +// FLOPPY_PRESENT_NO_MEDIA = Floppy controller present but no media inserted +// +#define FLOPPY_NOT_PRESENT 0 +#define FLOPPY_PRESENT_WITH_MEDIA 1 +#define FLOPPY_PRESENT_NO_MEDIA 2 + +BBS_TABLE *mBbsTable; +BOOLEAN mBbsTableDoneFlag = FALSE; +BOOLEAN IsHaveMediaInFloppy = TRUE; + +/** + Checks the state of the floppy and if media is inserted. + + This routine checks the state of the floppy and if media is inserted. + There are 3 cases: + No floppy present - Set BBS entry to ignore + Floppy present & no media - Set BBS entry to lowest priority. We cannot + set it to ignore since 16-bit CSM will + indicate no floppy and thus drive A: is + unusable. CSM-16 will not try floppy since + lowest priority and thus not incur boot + time penality. + Floppy present & media - Set BBS entry to some priority. + + @return State of floppy media + +**/ +UINT8 +HasMediaInFloppy ( + VOID + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + EFI_ISA_IO_PROTOCOL *IsaIo; + EFI_BLOCK_IO_PROTOCOL *BlkIo; + + HandleBuffer = NULL; + HandleCount = 0; + + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiIsaIoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + + // + // If don't find any ISA/IO protocol assume no floppy. Need for floppy + // free system + // + if (HandleCount == 0) { + return FLOPPY_NOT_PRESENT; + } + + ASSERT (HandleBuffer != NULL); + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiIsaIoProtocolGuid, + (VOID **) &IsaIo + ); + if (EFI_ERROR (Status)) { + continue; + } + + if (IsaIo->ResourceList->Device.HID != EISA_PNP_ID (0x604)) { + continue; + } + // + // Update blockio in case the floppy is inserted in during BdsTimeout + // + Status = gBS->DisconnectController (HandleBuffer[Index], NULL, NULL); + + if (EFI_ERROR (Status)) { + continue; + } + + Status = gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE); + + if (EFI_ERROR (Status)) { + continue; + } + + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiBlockIoProtocolGuid, + (VOID **) &BlkIo + ); + if (EFI_ERROR (Status)) { + continue; + } + + if (BlkIo->Media->MediaPresent) { + FreePool (HandleBuffer); + return FLOPPY_PRESENT_WITH_MEDIA; + } else { + FreePool (HandleBuffer); + return FLOPPY_PRESENT_NO_MEDIA; + } + } + + FreePool (HandleBuffer); + + return FLOPPY_NOT_PRESENT; + +} + + +/** + Complete build of BBS TABLE. + + @param Private Legacy BIOS Instance data + @param BbsTable BBS Table passed to 16-bit code + + @retval EFI_SUCCESS Removable media not present + +**/ +EFI_STATUS +LegacyBiosBuildBbs ( + IN LEGACY_BIOS_INSTANCE *Private, + IN BBS_TABLE *BbsTable + ) +{ + UINTN BbsIndex; + HDD_INFO *HddInfo; + UINTN HddIndex; + UINTN Index; + + // + // First entry is floppy. + // Next 2*MAX_IDE_CONTROLLER entries are for onboard IDE. + // Next n entries are filled in after each ROM is dispatched. + // Entry filled in if follow BBS spec. See LegacyPci.c + // Next entries are for non-BBS compliant ROMS. They are filled in by + // 16-bit code during Legacy16UpdateBbs invocation. Final BootPriority + // occurs after that invocation. + // + // Floppy + // Set default state. + // + IsHaveMediaInFloppy = HasMediaInFloppy (); + if (IsHaveMediaInFloppy == FLOPPY_PRESENT_WITH_MEDIA) { + BbsTable[0].BootPriority = BBS_UNPRIORITIZED_ENTRY; + } else { + if (IsHaveMediaInFloppy == FLOPPY_PRESENT_NO_MEDIA) { + BbsTable[0].BootPriority = BBS_LOWEST_PRIORITY; + } else { + BbsTable[0].BootPriority = BBS_IGNORE_ENTRY; + } + } + + BbsTable[0].Bus = 0xff; + BbsTable[0].Device = 0xff; + BbsTable[0].Function = 0xff; + BbsTable[0].DeviceType = BBS_FLOPPY; + BbsTable[0].Class = 01; + BbsTable[0].SubClass = 02; + BbsTable[0].StatusFlags.OldPosition = 0; + BbsTable[0].StatusFlags.Reserved1 = 0; + BbsTable[0].StatusFlags.Enabled = 0; + BbsTable[0].StatusFlags.Failed = 0; + BbsTable[0].StatusFlags.MediaPresent = 0; + BbsTable[0].StatusFlags.Reserved2 = 0; + + // + // Onboard HDD - Note Each HDD controller controls 2 drives + // Master & Slave + // + HddInfo = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo[0]; + // + // Get IDE Drive Info + // + LegacyBiosBuildIdeData (Private, &HddInfo, 0); + + for (HddIndex = 0; HddIndex < MAX_IDE_CONTROLLER; HddIndex++) { + + BbsIndex = HddIndex * 2 + 1; + for (Index = 0; Index < 2; ++Index) { + + BbsTable[BbsIndex + Index].Bus = HddInfo[HddIndex].Bus; + BbsTable[BbsIndex + Index].Device = HddInfo[HddIndex].Device; + BbsTable[BbsIndex + Index].Function = HddInfo[HddIndex].Function; + BbsTable[BbsIndex + Index].Class = 01; + BbsTable[BbsIndex + Index].SubClass = 01; + BbsTable[BbsIndex + Index].StatusFlags.OldPosition = 0; + BbsTable[BbsIndex + Index].StatusFlags.Reserved1 = 0; + BbsTable[BbsIndex + Index].StatusFlags.Enabled = 0; + BbsTable[BbsIndex + Index].StatusFlags.Failed = 0; + BbsTable[BbsIndex + Index].StatusFlags.MediaPresent = 0; + BbsTable[BbsIndex + Index].StatusFlags.Reserved2 = 0; + + // + // If no controller found or no device found set to ignore + // else set to unprioritized and set device type + // + if (HddInfo[HddIndex].CommandBaseAddress == 0) { + BbsTable[BbsIndex + Index].BootPriority = BBS_IGNORE_ENTRY; + } else { + if (Index == 0) { + if ((HddInfo[HddIndex].Status & (HDD_MASTER_IDE | HDD_MASTER_ATAPI_CDROM | HDD_MASTER_ATAPI_ZIPDISK)) != 0) { + BbsTable[BbsIndex + Index].BootPriority = BBS_UNPRIORITIZED_ENTRY; + if ((HddInfo[HddIndex].Status & HDD_MASTER_IDE) != 0) { + BbsTable[BbsIndex + Index].DeviceType = BBS_HARDDISK; + } else if ((HddInfo[HddIndex].Status & HDD_MASTER_ATAPI_CDROM) != 0) { + BbsTable[BbsIndex + Index].DeviceType = BBS_CDROM; + } else { + // + // for ZIPDISK + // + BbsTable[BbsIndex + Index].DeviceType = BBS_HARDDISK; + } + } else { + BbsTable[BbsIndex + Index].BootPriority = BBS_IGNORE_ENTRY; + } + } else { + if ((HddInfo[HddIndex].Status & (HDD_SLAVE_IDE | HDD_SLAVE_ATAPI_CDROM | HDD_SLAVE_ATAPI_ZIPDISK)) != 0) { + BbsTable[BbsIndex + Index].BootPriority = BBS_UNPRIORITIZED_ENTRY; + if ((HddInfo[HddIndex].Status & HDD_SLAVE_IDE) != 0) { + BbsTable[BbsIndex + Index].DeviceType = BBS_HARDDISK; + } else if ((HddInfo[HddIndex].Status & HDD_SLAVE_ATAPI_CDROM) != 0) { + BbsTable[BbsIndex + Index].DeviceType = BBS_CDROM; + } else { + // + // for ZIPDISK + // + BbsTable[BbsIndex + Index].DeviceType = BBS_HARDDISK; + } + } else { + BbsTable[BbsIndex + Index].BootPriority = BBS_IGNORE_ENTRY; + } + } + } + } + } + + return EFI_SUCCESS; + +} + + +/** + Get all BBS info + + @param This Protocol instance pointer. + @param HddCount Number of HDD_INFO structures + @param HddInfo Onboard IDE controller information + @param BbsCount Number of BBS_TABLE structures + @param BbsTable List BBS entries + + @retval EFI_SUCCESS Tables returned + @retval EFI_NOT_FOUND resource not found + @retval EFI_DEVICE_ERROR can not get BBS table + +**/ +EFI_STATUS +EFIAPI +LegacyBiosGetBbsInfo ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + OUT UINT16 *HddCount, + OUT HDD_INFO **HddInfo, + OUT UINT16 *BbsCount, + OUT BBS_TABLE **BbsTable + ) +{ + LEGACY_BIOS_INSTANCE *Private; + EFI_IA32_REGISTER_SET Regs; + EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable; +// HDD_INFO *LocalHddInfo; +// IN BBS_TABLE *LocalBbsTable; + UINTN NumHandles; + EFI_HANDLE *HandleBuffer; + UINTN Index; + UINTN TempData; + UINT32 Granularity; + + HandleBuffer = NULL; + + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable; +// LocalHddInfo = EfiToLegacy16BootTable->HddInfo; +// LocalBbsTable = (BBS_TABLE*)(UINTN)EfiToLegacy16BootTable->BbsTable; + + if (!mBbsTableDoneFlag) { + mBbsTable = Private->BbsTablePtr; + + // + // Always enable disk controllers so 16-bit CSM code has valid information for all + // drives. + // + // + // Get PciRootBridgeIO protocol + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciRootBridgeIoProtocolGuid, + NULL, + &NumHandles, + &HandleBuffer + ); + + if (NumHandles == 0) { + return EFI_NOT_FOUND; + } + + mBbsTableDoneFlag = TRUE; + for (Index = 0; Index < NumHandles; Index++) { + // + // Connect PciRootBridgeIO protocol handle with FALSE parameter to let + // PCI bus driver enumerate all subsequent handles + // + gBS->ConnectController (HandleBuffer[Index], NULL, NULL, FALSE); + + } + + LegacyBiosBuildBbs (Private, mBbsTable); + + Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xe0000, 0x20000, &Granularity); + + // + // Call into Legacy16 code to add to BBS table for non BBS compliant OPROMs. + // + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + Regs.X.AX = Legacy16UpdateBbs; + + // + // Pass in handoff data + // + TempData = (UINTN) EfiToLegacy16BootTable; + Regs.X.ES = EFI_SEGMENT ((UINT32) TempData); + Regs.X.BX = EFI_OFFSET ((UINT32) TempData); + + Private->LegacyBios.FarCall86 ( + This, + Private->Legacy16CallSegment, + Private->Legacy16CallOffset, + &Regs, + NULL, + 0 + ); + + Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate); + Private->LegacyRegion->Lock (Private->LegacyRegion, 0xe0000, 0x20000, &Granularity); + + if (Regs.X.AX != 0) { + return EFI_DEVICE_ERROR; + } + } + + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + *HddCount = MAX_IDE_CONTROLLER; + *HddInfo = EfiToLegacy16BootTable->HddInfo; + *BbsTable = (BBS_TABLE*)(UINTN)EfiToLegacy16BootTable->BbsTable; + *BbsCount = (UINT16) (sizeof (Private->IntThunk->BbsTable) / sizeof (BBS_TABLE)); + return EFI_SUCCESS; +} diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBda.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBda.c new file mode 100644 index 0000000000..c45d5d4c3e --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBda.c @@ -0,0 +1,66 @@ +/** @file + This code fills in BDA (0x400) and EBDA (pointed to by 0x4xx) + information. There is support for doing initializeation before + Legacy16 is loaded and before a legacy boot is attempted. + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "LegacyBiosInterface.h" + +/** + Fill in the standard BDA and EBDA stuff before Legacy16 load + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosInitBda ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + BDA_STRUC *Bda; + UINT8 *Ebda; + + Bda = (BDA_STRUC *) ((UINTN) 0x400); + Ebda = (UINT8 *) ((UINTN) 0x9fc00); + + ZeroMem (Bda, 0x100); + ZeroMem (Ebda, 0x400); + // + // 640k-1k for EBDA + // + Bda->MemSize = 0x27f; + Bda->KeyHead = 0x1e; + Bda->KeyTail = 0x1e; + Bda->FloppyData = 0x00; + Bda->FloppyTimeout = 0xff; + + Bda->KeyStart = 0x001E; + Bda->KeyEnd = 0x003E; + Bda->KeyboardStatus = 0x10; + Bda->Ebda = 0x9fc0; + + // + // Move LPT time out here and zero out LPT4 since some SCSI OPROMS + // use this as scratch pad (LPT4 is Reserved) + // + Bda->Lpt1_2Timeout = 0x1414; + Bda->Lpt3_4Timeout = 0x1400; + + *Ebda = 0x01; + + return EFI_SUCCESS; +} diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBios.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBios.c new file mode 100644 index 0000000000..75add5e39d --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBios.c @@ -0,0 +1,1007 @@ +/** @file + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "LegacyBiosInterface.h" + +#define PHYSICAL_ADDRESS_TO_POINTER(Address) ((VOID *) ((UINTN) Address)) + +// +// define maximum number of HDD system supports +// +#define MAX_HDD_ENTRIES 0x30 + +// +// Module Global: +// Since this driver will only ever produce one instance of the Private Data +// protocol you are not required to dynamically allocate the PrivateData. +// +LEGACY_BIOS_INSTANCE mPrivateData; + +// +// The end of OPROM shadow address +// +UINTN mEndOpromShadowAddress = 0; + +/** + Do an AllocatePages () of type AllocateMaxAddress for EfiBootServicesCode + memory. + + @param AllocateType Allocated Legacy Memory Type + @param StartPageAddress Start address of range + @param Pages Number of pages to allocate + @param Result Result of allocation + + @retval EFI_SUCCESS Legacy16 code loaded + @retval Other No protocol installed, unload driver. + +**/ +EFI_STATUS +AllocateLegacyMemory ( + IN EFI_ALLOCATE_TYPE AllocateType, + IN EFI_PHYSICAL_ADDRESS StartPageAddress, + IN UINTN Pages, + OUT EFI_PHYSICAL_ADDRESS *Result + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS MemPage; + + // + // Allocate Pages of memory less <= StartPageAddress + // + MemPage = (EFI_PHYSICAL_ADDRESS) (UINTN) StartPageAddress; + Status = gBS->AllocatePages ( + AllocateType, + EfiBootServicesCode, + Pages, + &MemPage + ); + // + // Do not ASSERT on Status error but let caller decide since some cases + // memory is already taken but that is ok. + // + if (!EFI_ERROR (Status)) { + *Result = (EFI_PHYSICAL_ADDRESS) (UINTN) MemPage; + } + // + // If reach here the status = EFI_SUCCESS + // + return Status; +} + + +/** + This function is called when EFI needs to reserve an area in the 0xE0000 or 0xF0000 + 64 KB blocks. + + Note: inconsistency with the Framework CSM spec. Per the spec, this function may be + invoked only once. This limitation is relaxed to allow multiple calls in this implemenation. + + @param This Protocol instance pointer. + @param LegacyMemorySize Size of required region + @param Region Region to use. 00 = Either 0xE0000 or 0xF0000 + block Bit0 = 1 0xF0000 block Bit1 = 1 0xE0000 + block + @param Alignment Address alignment. Bit mapped. First non-zero + bit from right is alignment. + @param LegacyMemoryAddress Region Assigned + + @retval EFI_SUCCESS Region assigned + @retval EFI_ACCESS_DENIED Procedure previously invoked + @retval Other Region not assigned + +**/ +EFI_STATUS +EFIAPI +LegacyBiosGetLegacyRegion ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINTN LegacyMemorySize, + IN UINTN Region, + IN UINTN Alignment, + OUT VOID **LegacyMemoryAddress + ) +{ + + LEGACY_BIOS_INSTANCE *Private; + EFI_IA32_REGISTER_SET Regs; + EFI_STATUS Status; + UINT32 Granularity; + + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity); + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + Regs.X.AX = Legacy16GetTableAddress; + Regs.X.BX = (UINT16) Region; + Regs.X.CX = (UINT16) LegacyMemorySize; + Regs.X.DX = (UINT16) Alignment; + Private->LegacyBios.FarCall86 ( + &Private->LegacyBios, + Private->Legacy16CallSegment, + Private->Legacy16CallOffset, + &Regs, + NULL, + 0 + ); + + if (Regs.X.AX == 0) { + *LegacyMemoryAddress = (VOID *) (UINTN) ((Regs.X.DS << 4) + Regs.X.BX); + Status = EFI_SUCCESS; + } else { + Status = EFI_OUT_OF_RESOURCES; + } + + Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate); + Private->LegacyRegion->Lock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity); + + return Status; +} + + +/** + This function is called when copying data to the region assigned by + EFI_LEGACY_BIOS_PROTOCOL.GetLegacyRegion(). + + @param This Protocol instance pointer. + @param LegacyMemorySize Size of data to copy + @param LegacyMemoryAddress Legacy Region destination address Note: must + be in region assigned by + LegacyBiosGetLegacyRegion + @param LegacyMemorySourceAddress Source of data + + @retval EFI_SUCCESS The data was copied successfully. + @retval EFI_ACCESS_DENIED Either the starting or ending address is out of bounds. +**/ +EFI_STATUS +EFIAPI +LegacyBiosCopyLegacyRegion ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINTN LegacyMemorySize, + IN VOID *LegacyMemoryAddress, + IN VOID *LegacyMemorySourceAddress + ) +{ + + LEGACY_BIOS_INSTANCE *Private; + UINT32 Granularity; + + if ((LegacyMemoryAddress < (VOID *)(UINTN)0xE0000 ) || + ((UINTN) LegacyMemoryAddress + LegacyMemorySize > (UINTN) 0x100000) + ) { + return EFI_ACCESS_DENIED; + } + // + // There is no protection from writes over lapping if this function is + // called multiple times. + // + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity); + CopyMem (LegacyMemoryAddress, LegacyMemorySourceAddress, LegacyMemorySize); + + Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate); + Private->LegacyRegion->Lock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity); + + return EFI_SUCCESS; +} + + +/** + Find Legacy16 BIOS image in the FLASH device and shadow it into memory. Find + the $EFI table in the shadow area. Thunk into the Legacy16 code after it had + been shadowed. + + @param Private Legacy BIOS context data + + @retval EFI_SUCCESS Legacy16 code loaded + @retval Other No protocol installed, unload driver. + +**/ +EFI_STATUS +ShadowAndStartLegacy16 ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + EFI_STATUS Status; + UINT8 *Ptr; + UINT8 *PtrEnd; + BOOLEAN Done; + EFI_COMPATIBILITY16_TABLE *Table; + UINT8 CheckSum; + EFI_IA32_REGISTER_SET Regs; + EFI_TO_COMPATIBILITY16_INIT_TABLE *EfiToLegacy16InitTable; + EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable; + VOID *LegacyBiosImage; + UINTN LegacyBiosImageSize; + UINTN E820Size; + UINT32 *ClearPtr; + BBS_TABLE *BbsTable; + LEGACY_EFI_HDD_TABLE *LegacyEfiHddTable; + UINTN Index; + UINT32 TpmPointer; + VOID *TpmBinaryImage; + UINTN TpmBinaryImageSize; + UINTN Location; + UINTN Alignment; + UINTN TempData; + EFI_PHYSICAL_ADDRESS Address; + UINT16 OldMask; + UINT16 NewMask; + UINT32 Granularity; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor; + + Location = 0; + Alignment = 0; + + // + // we allocate the C/D/E/F segment as RT code so no one will use it any more. + // + Address = 0xC0000; + gDS->GetMemorySpaceDescriptor (Address, &Descriptor); + if (Descriptor.GcdMemoryType == EfiGcdMemoryTypeSystemMemory) { + // + // If it is already reserved, we should be safe, or else we allocate it. + // + Status = gBS->AllocatePages ( + AllocateAddress, + EfiRuntimeServicesCode, + 0x40000/EFI_PAGE_SIZE, + &Address + ); + if (EFI_ERROR (Status)) { + // + // Bugbug: need to figure out whether C/D/E/F segment should be marked as reserved memory. + // + DEBUG ((DEBUG_ERROR, "Failed to allocate the C/D/E/F segment Status = %r", Status)); + } + } + + // + // start testtest + // GetTimerValue (&Ticker); + // + // gRT->SetVariable (L"StartLegacy", + // &gEfiGlobalVariableGuid, + // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + // sizeof (UINT64), + // (VOID *)&Ticker + // ); + // end testtest + // + EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable; + Status = Private->LegacyBiosPlatform->GetPlatformInfo ( + Private->LegacyBiosPlatform, + EfiGetPlatformBinarySystemRom, + &LegacyBiosImage, + &LegacyBiosImageSize, + &Location, + &Alignment, + 0, + 0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private->BiosStart = (UINT32) (0x100000 - LegacyBiosImageSize); + Private->OptionRom = 0xc0000; + Private->LegacyBiosImageSize = (UINT32) LegacyBiosImageSize; + + // + // Can only shadow into memory allocated for legacy useage. + // + ASSERT (Private->BiosStart > Private->OptionRom); + + // + // Shadow Legacy BIOS. Turn on memory and copy image + // + Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xc0000, 0x40000, &Granularity); + + ClearPtr = (VOID *) ((UINTN) 0xc0000); + + // + // Initialize region from 0xc0000 to start of BIOS to all ffs. This allows unused + // regions to be used by EMM386 etc. + // + SetMem ((VOID *) ClearPtr, (UINTN) (0x40000 - LegacyBiosImageSize), 0xff); + + TempData = Private->BiosStart; + + CopyMem ( + (VOID *) TempData, + LegacyBiosImage, + (UINTN) LegacyBiosImageSize + ); + + Private->Cpu->FlushDataCache (Private->Cpu, 0xc0000, 0x40000, EfiCpuFlushTypeWriteBackInvalidate); + + // + // Search for Legacy16 table in Shadowed ROM + // + Done = FALSE; + Table = NULL; + for (Ptr = (UINT8 *) TempData; Ptr < (UINT8 *) ((UINTN) 0x100000) && !Done; Ptr += 0x10) { + if (*(UINT32 *) Ptr == SIGNATURE_32 ('I', 'F', 'E', '$')) { + Table = (EFI_COMPATIBILITY16_TABLE *) Ptr; + PtrEnd = Ptr + Table->TableLength; + for (CheckSum = 0; Ptr < PtrEnd; Ptr++) { + CheckSum = (UINT8) (CheckSum +*Ptr); + } + + Done = TRUE; + } + } + + if (Table == NULL) { + DEBUG ((EFI_D_ERROR, "No Legacy16 table found\n")); + return EFI_NOT_FOUND; + } + + if (!Done) { + // + // Legacy16 table header checksum error. + // + DEBUG ((EFI_D_ERROR, "Legacy16 table found with bad talbe header checksum\n")); + } + + // + // Remember location of the Legacy16 table + // + Private->Legacy16Table = Table; + Private->Legacy16CallSegment = Table->Compatibility16CallSegment; + Private->Legacy16CallOffset = Table->Compatibility16CallOffset; + EfiToLegacy16InitTable = &Private->IntThunk->EfiToLegacy16InitTable; + Private->Legacy16InitPtr = EfiToLegacy16InitTable; + Private->Legacy16BootPtr = &Private->IntThunk->EfiToLegacy16BootTable; + Private->InternalIrqRoutingTable = NULL; + Private->NumberIrqRoutingEntries = 0; + Private->BbsTablePtr = NULL; + Private->LegacyEfiHddTable = NULL; + Private->DiskEnd = 0; + Private->Disk4075 = 0; + Private->HddTablePtr = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo; + Private->NumberHddControllers = MAX_IDE_CONTROLLER; + Private->Dump[0] = 'D'; + Private->Dump[1] = 'U'; + Private->Dump[2] = 'M'; + Private->Dump[3] = 'P'; + + ZeroMem ( + Private->Legacy16BootPtr, + sizeof (EFI_TO_COMPATIBILITY16_BOOT_TABLE) + ); + + // + // Store away a copy of the EFI System Table + // + Table->EfiSystemTable = (UINT32) (UINTN) gST; + + // + // Get the end of OPROM shadow address + // + Status = Private->LegacyBiosPlatform->GetPlatformInfo ( + Private->LegacyBiosPlatform, + EfiGetPlatformEndOpromShadowAddr, + NULL, + NULL, + &mEndOpromShadowAddress, + NULL, + 0, + 0 + ); + if (EFI_ERROR (Status)) { + mEndOpromShadowAddress = 0xDFFFF; + } + + // + // IPF CSM integration -Bug + // + // Construct the Legacy16 boot memory map. This sets up number of + // E820 entries. + // + LegacyBiosBuildE820 (Private, &E820Size); + // + // Initialize BDA and EBDA standard values needed to load Legacy16 code + // + LegacyBiosInitBda (Private); + LegacyBiosInitCmos (Private); + + // + // All legacy interrupt should be masked when do initialization work from legacy 16 code. + // + Private->Legacy8259->GetMask(Private->Legacy8259, &OldMask, NULL, NULL, NULL); + NewMask = 0xFFFF; + Private->Legacy8259->SetMask(Private->Legacy8259, &NewMask, NULL, NULL, NULL); + + // + // Call into Legacy16 code to do an INIT + // + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + Regs.X.AX = Legacy16InitializeYourself; + Regs.X.ES = EFI_SEGMENT (*((UINT32 *) &EfiToLegacy16InitTable)); + Regs.X.BX = EFI_OFFSET (*((UINT32 *) &EfiToLegacy16InitTable)); + + Private->LegacyBios.FarCall86 ( + &Private->LegacyBios, + Table->Compatibility16CallSegment, + Table->Compatibility16CallOffset, + &Regs, + NULL, + 0 + ); + + // + // Restore original legacy interrupt mask value + // + Private->Legacy8259->SetMask(Private->Legacy8259, &OldMask, NULL, NULL, NULL); + + if (Regs.X.AX != 0) { + return EFI_DEVICE_ERROR; + } + + // + // start testtest + // GetTimerValue (&Ticker); + // + // gRT->SetVariable (L"BackFromInitYourself", + // &gEfiGlobalVariableGuid, + // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + // sizeof (UINT64), + // (VOID *)&Ticker + // ); + // end testtest + // + // Copy E820 table after InitializeYourself is completed + // + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + Regs.X.AX = Legacy16GetTableAddress; + Regs.X.CX = (UINT16) E820Size; + Regs.X.DX = 1; + Private->LegacyBios.FarCall86 ( + &Private->LegacyBios, + Table->Compatibility16CallSegment, + Table->Compatibility16CallOffset, + &Regs, + NULL, + 0 + ); + + Table->E820Pointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX); + Table->E820Length = (UINT32) E820Size; + if (Regs.X.AX != 0) { + DEBUG ((EFI_D_ERROR, "Legacy16 E820 length insufficient\n")); + } else { + TempData = Table->E820Pointer; + CopyMem ((VOID *) TempData, Private->E820Table, E820Size); + } + // + // Get PnPInstallationCheck Info. + // + Private->PnPInstallationCheckSegment = Table->PnPInstallationCheckSegment; + Private->PnPInstallationCheckOffset = Table->PnPInstallationCheckOffset; + + // + // Check if PCI Express is supported. If yes, Save base address. + // + Status = Private->LegacyBiosPlatform->GetPlatformInfo ( + Private->LegacyBiosPlatform, + EfiGetPlatformPciExpressBase, + NULL, + NULL, + &Location, + &Alignment, + 0, + 0 + ); + if (!EFI_ERROR (Status)) { + Private->Legacy16Table->PciExpressBase = (UINT32)Location; + Location = 0; + } + // + // Check if TPM is supported. If yes get a region in E0000,F0000 to copy it + // into, copy it and update pointer to binary image. This needs to be + // done prior to any OPROM for security purposes. + // + Status = Private->LegacyBiosPlatform->GetPlatformInfo ( + Private->LegacyBiosPlatform, + EfiGetPlatformBinaryTpmBinary, + &TpmBinaryImage, + &TpmBinaryImageSize, + &Location, + &Alignment, + 0, + 0 + ); + if (!EFI_ERROR (Status)) { + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + Regs.X.AX = Legacy16GetTableAddress; + Regs.X.CX = (UINT16) TpmBinaryImageSize; + Regs.X.DX = 1; + Private->LegacyBios.FarCall86 ( + &Private->LegacyBios, + Table->Compatibility16CallSegment, + Table->Compatibility16CallOffset, + &Regs, + NULL, + 0 + ); + + TpmPointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX); + if (Regs.X.AX != 0) { + DEBUG ((EFI_D_ERROR, "TPM cannot be loaded\n")); + } else { + CopyMem ((VOID *) (UINTN)TpmPointer, TpmBinaryImage, TpmBinaryImageSize); + Table->TpmSegment = Regs.X.DS; + Table->TpmOffset = Regs.X.BX; + + } + } + // + // Lock the Legacy BIOS region + // + Private->Cpu->FlushDataCache (Private->Cpu, Private->BiosStart, (UINT32) LegacyBiosImageSize, EfiCpuFlushTypeWriteBackInvalidate); + Private->LegacyRegion->Lock (Private->LegacyRegion, Private->BiosStart, (UINT32) LegacyBiosImageSize, &Granularity); + + // + // Get the BbsTable from LOW_MEMORY_THUNK + // + BbsTable = (BBS_TABLE *)(UINTN)Private->IntThunk->BbsTable; + ZeroMem ((VOID *)BbsTable, sizeof (Private->IntThunk->BbsTable)); + + EfiToLegacy16BootTable->BbsTable = (UINT32)(UINTN)BbsTable; + Private->BbsTablePtr = (VOID *) BbsTable; + // + // Skip Floppy and possible onboard IDE drives + // + EfiToLegacy16BootTable->NumberBbsEntries = 1 + 2 * MAX_IDE_CONTROLLER; + + for (Index = 0; Index < (sizeof (Private->IntThunk->BbsTable) / sizeof (BBS_TABLE)); Index++) { + BbsTable[Index].BootPriority = BBS_IGNORE_ENTRY; + } + // + // Allocate space for Legacy HDD table + // + LegacyEfiHddTable = (LEGACY_EFI_HDD_TABLE *) AllocateZeroPool ((UINTN) MAX_HDD_ENTRIES * sizeof (LEGACY_EFI_HDD_TABLE)); + ASSERT (LegacyEfiHddTable); + + Private->LegacyEfiHddTable = LegacyEfiHddTable; + Private->LegacyEfiHddTableIndex = 0x00; + + // + // start testtest + // GetTimerValue (&Ticker); + // + // gRT->SetVariable (L"EndOfLoadFv", + // &gEfiGlobalVariableGuid, + // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + // sizeof (UINT64), + // (VOID *)&Ticker + // ); + // end testtest + // + return EFI_SUCCESS; +} + +/** + Shadow all legacy16 OPROMs that haven't been shadowed. + Warning: Use this with caution. This routine disconnects all EFI + drivers. If used externally then caller must re-connect EFI + drivers. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS OPROMs shadowed + +**/ +EFI_STATUS +EFIAPI +LegacyBiosShadowAllLegacyOproms ( + IN EFI_LEGACY_BIOS_PROTOCOL *This + ) +{ + LEGACY_BIOS_INSTANCE *Private; + + // + // EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform; + // EFI_LEGACY16_TABLE *Legacy16Table; + // + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + + // + // LegacyBiosPlatform = Private->LegacyBiosPlatform; + // Legacy16Table = Private->Legacy16Table; + // + // Shadow PCI ROMs. We must do this near the end since this will kick + // of Native EFI drivers that may be needed to collect info for Legacy16 + // + // WARNING: PciIo is gone after this call. + // + PciProgramAllInterruptLineRegisters (Private); + + PciShadowRoms (Private); + + // + // Shadow PXE base code, BIS etc. + // + // LegacyBiosPlatform->ShadowServiceRoms (LegacyBiosPlatform, + // &Private->OptionRom, + // Legacy16Table); + // + return EFI_SUCCESS; +} + +/** + Get the PCI BIOS interface version. + + @param Private Driver private data. + + @return The PCI interface version number in Binary Coded Decimal (BCD) format. + E.g.: 0x0210 indicates 2.10, 0x0300 indicates 3.00 + +**/ +UINT16 +GetPciInterfaceVersion ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + EFI_IA32_REGISTER_SET Reg; + BOOLEAN ThunkFailed; + UINT16 PciInterfaceVersion; + + PciInterfaceVersion = 0; + + Reg.X.AX = 0xB101; + Reg.E.EDI = 0; + + ThunkFailed = Private->LegacyBios.Int86 (&Private->LegacyBios, 0x1A, &Reg); + if (!ThunkFailed) { + // + // From PCI Firmware 3.0 Specification: + // If the CARRY FLAG [CF] is cleared and AH is set to 00h, it is still necessary to examine the + // contents of [EDX] for the presence of the string "PCI" + (trailing space) to fully validate the + // presence of the PCI function set. [BX] will further indicate the version level, with enough + // granularity to allow for incremental changes in the code that don't affect the function interface. + // Version numbers are stored as Binary Coded Decimal (BCD) values. For example, Version 2.10 + // would be returned as a 02h in the [BH] registers and 10h in the [BL] registers. + // + if ((Reg.X.Flags.CF == 0) && (Reg.H.AH == 0) && (Reg.E.EDX == SIGNATURE_32 ('P', 'C', 'I', ' '))) { + PciInterfaceVersion = Reg.X.BX; + } + } + return PciInterfaceVersion; +} + +/** + Install Driver to produce Legacy BIOS protocol. + + @param ImageHandle Handle of driver image. + @param SystemTable Pointer to system table. + + @retval EFI_SUCCESS Legacy BIOS protocol installed + @retval No protocol installed, unload driver. + +**/ +EFI_STATUS +EFIAPI +LegacyBiosInstall ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + LEGACY_BIOS_INSTANCE *Private; + EFI_TO_COMPATIBILITY16_INIT_TABLE *EfiToLegacy16InitTable; + EFI_PHYSICAL_ADDRESS MemoryAddress; + VOID *MemoryPtr; + EFI_PHYSICAL_ADDRESS MemoryAddressUnder1MB; + UINTN Index; + UINT32 *BaseVectorMaster; + EFI_PHYSICAL_ADDRESS StartAddress; + UINT32 *ClearPtr; + EFI_PHYSICAL_ADDRESS MemStart; + UINT32 IntRedirCode; + UINT32 Granularity; + BOOLEAN DecodeOn; + UINT32 MemorySize; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor; + UINT64 Length; + + // + // Load this driver's image to memory + // + Status = RelocateImageUnder4GIfNeeded (ImageHandle, SystemTable); + if (EFI_ERROR (Status)) { + return Status; + } + + Private = &mPrivateData; + ZeroMem (Private, sizeof (LEGACY_BIOS_INSTANCE)); + + // + // Grab a copy of all the protocols we depend on. Any error would + // be a dispatcher bug!. + // + Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Private->Cpu); + ASSERT_EFI_ERROR (Status); + + Status = gBS->LocateProtocol (&gEfiLegacyRegion2ProtocolGuid, NULL, (VOID **) &Private->LegacyRegion); + ASSERT_EFI_ERROR (Status); + + Status = gBS->LocateProtocol (&gEfiLegacyBiosPlatformProtocolGuid, NULL, (VOID **) &Private->LegacyBiosPlatform); + ASSERT_EFI_ERROR (Status); + + Status = gBS->LocateProtocol (&gEfiLegacy8259ProtocolGuid, NULL, (VOID **) &Private->Legacy8259); + ASSERT_EFI_ERROR (Status); + + Status = gBS->LocateProtocol (&gEfiLegacyInterruptProtocolGuid, NULL, (VOID **) &Private->LegacyInterrupt); + ASSERT_EFI_ERROR (Status); + + // + // Locate Memory Test Protocol if exists + // + Status = gBS->LocateProtocol ( + &gEfiGenericMemTestProtocolGuid, + NULL, + (VOID **) &Private->GenericMemoryTest + ); + ASSERT_EFI_ERROR (Status); + + // + // Make sure all memory from 0-640K is tested + // + for (StartAddress = 0; StartAddress < 0xa0000; ) { + gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor); + if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) { + StartAddress = Descriptor.BaseAddress + Descriptor.Length; + continue; + } + Length = MIN (Descriptor.Length, 0xa0000 - StartAddress); + Private->GenericMemoryTest->CompatibleRangeTest ( + Private->GenericMemoryTest, + StartAddress, + Length + ); + StartAddress = StartAddress + Length; + } + // + // Make sure all memory from 1MB to 16MB is tested and added to memory map + // + for (StartAddress = BASE_1MB; StartAddress < BASE_16MB; ) { + gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor); + if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) { + StartAddress = Descriptor.BaseAddress + Descriptor.Length; + continue; + } + Length = MIN (Descriptor.Length, BASE_16MB - StartAddress); + Private->GenericMemoryTest->CompatibleRangeTest ( + Private->GenericMemoryTest, + StartAddress, + Length + ); + StartAddress = StartAddress + Length; + } + + Private->Signature = LEGACY_BIOS_INSTANCE_SIGNATURE; + + Private->LegacyBios.Int86 = LegacyBiosInt86; + Private->LegacyBios.FarCall86 = LegacyBiosFarCall86; + Private->LegacyBios.CheckPciRom = LegacyBiosCheckPciRom; + Private->LegacyBios.InstallPciRom = LegacyBiosInstallPciRom; + Private->LegacyBios.LegacyBoot = LegacyBiosLegacyBoot; + Private->LegacyBios.UpdateKeyboardLedStatus = LegacyBiosUpdateKeyboardLedStatus; + Private->LegacyBios.GetBbsInfo = LegacyBiosGetBbsInfo; + Private->LegacyBios.ShadowAllLegacyOproms = LegacyBiosShadowAllLegacyOproms; + Private->LegacyBios.PrepareToBootEfi = LegacyBiosPrepareToBootEfi; + Private->LegacyBios.GetLegacyRegion = LegacyBiosGetLegacyRegion; + Private->LegacyBios.CopyLegacyRegion = LegacyBiosCopyLegacyRegion; + Private->LegacyBios.BootUnconventionalDevice = LegacyBiosBootUnconventionalDevice; + + Private->ImageHandle = ImageHandle; + + // + // Enable read attribute of legacy region. + // + DecodeOn = TRUE; + Private->LegacyRegion->Decode ( + Private->LegacyRegion, + 0xc0000, + 0x40000, + &Granularity, + &DecodeOn + ); + // + // Set Cachebility for legacy region + // BUGBUG: Comments about this legacy region cacheability setting + // This setting will make D865GCHProduction CSM Unhappy + // + if (PcdGetBool (PcdLegacyBiosCacheLegacyRegion)) { + gDS->SetMemorySpaceAttributes ( + 0x0, + 0xA0000, + EFI_MEMORY_WB + ); + gDS->SetMemorySpaceAttributes ( + 0xc0000, + 0x40000, + EFI_MEMORY_WB + ); + } + + gDS->SetMemorySpaceAttributes ( + 0xA0000, + 0x20000, + EFI_MEMORY_UC + ); + + // + // Allocate 0 - 4K for real mode interupt vectors and BDA. + // + AllocateLegacyMemory ( + AllocateAddress, + 0, + 1, + &MemoryAddress + ); + ASSERT (MemoryAddress == 0x000000000); + + ClearPtr = (VOID *) ((UINTN) 0x0000); + + // + // Initialize region from 0x0000 to 4k. This initializes interrupt vector + // range. + // + gBS->SetMem ((VOID *) ClearPtr, 0x400, INITIAL_VALUE_BELOW_1K); + ZeroMem ((VOID *) ((UINTN)ClearPtr + 0x400), 0xC00); + + // + // Allocate pages for OPROM usage + // + MemorySize = PcdGet32 (PcdEbdaReservedMemorySize); + ASSERT ((MemorySize & 0xFFF) == 0); + + Status = AllocateLegacyMemory ( + AllocateAddress, + CONVENTIONAL_MEMORY_TOP - MemorySize, + EFI_SIZE_TO_PAGES (MemorySize), + &MemoryAddress + ); + ASSERT_EFI_ERROR (Status); + + ZeroMem ((VOID *) ((UINTN) MemoryAddress), MemorySize); + + // + // Allocate all 32k chunks from 0x60000 ~ 0x88000 for Legacy OPROMs that + // don't use PMM but look for zeroed memory. Note that various non-BBS + // SCSIs expect different areas to be free + // + for (MemStart = 0x60000; MemStart < 0x88000; MemStart += 0x1000) { + Status = AllocateLegacyMemory ( + AllocateAddress, + MemStart, + 1, + &MemoryAddress + ); + if (!EFI_ERROR (Status)) { + MemoryPtr = (VOID *) ((UINTN) MemoryAddress); + ZeroMem (MemoryPtr, 0x1000); + } else { + DEBUG ((EFI_D_ERROR, "WARNING: Allocate legacy memory fail for SCSI card - %x\n", MemStart)); + } + } + + // + // Allocate a 64k area (16 4k pages) for 16-bit code for scratch pad and zero it out + // + Status = AllocateLegacyMemory ( + AllocateMaxAddress, + CONVENTIONAL_MEMORY_TOP, + 16, + &MemoryAddressUnder1MB + ); + ASSERT_EFI_ERROR (Status); + + ZeroMem ((VOID *) ((UINTN) MemoryAddressUnder1MB), 0x10000); + + // + // Allocate space for thunker and Init Thunker + // + Status = AllocateLegacyMemory ( + AllocateMaxAddress, + CONVENTIONAL_MEMORY_TOP, + (sizeof (LOW_MEMORY_THUNK) / EFI_PAGE_SIZE) + 2, + &MemoryAddress + ); + ASSERT_EFI_ERROR (Status); + Private->IntThunk = (LOW_MEMORY_THUNK *) (UINTN) MemoryAddress; + EfiToLegacy16InitTable = &Private->IntThunk->EfiToLegacy16InitTable; + EfiToLegacy16InitTable->ThunkStart = (UINT32) (EFI_PHYSICAL_ADDRESS) (UINTN) MemoryAddress; + EfiToLegacy16InitTable->ThunkSizeInBytes = (UINT32) (sizeof (LOW_MEMORY_THUNK)); + + Status = LegacyBiosInitializeThunk (Private); + ASSERT_EFI_ERROR (Status); + + // + // Init the legacy memory map in memory < 1 MB. + // + EfiToLegacy16InitTable->BiosLessThan1MB = (UINT32) MemoryAddressUnder1MB; + EfiToLegacy16InitTable->LowPmmMemory = (UINT32) MemoryAddressUnder1MB; + EfiToLegacy16InitTable->LowPmmMemorySizeInBytes = 0x10000; + + // + // Allocate 4 MB of PMM Memory under 16 MB + // + Status = AllocateLegacyMemory ( + AllocateMaxAddress, + 0x1000000, + 0x400, + &MemoryAddress + ); + if (!EFI_ERROR (Status)) { + EfiToLegacy16InitTable->HiPmmMemory = (UINT32) (EFI_PHYSICAL_ADDRESS) (UINTN) MemoryAddress; + EfiToLegacy16InitTable->HiPmmMemorySizeInBytes = PMM_MEMORY_SIZE; + } + + // + // ShutdownAPs(); + // + // Start the Legacy BIOS; + // + Status = ShadowAndStartLegacy16 (Private); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Initialize interrupt redirection code and entries; + // IDT Vectors 0x68-0x6f must be redirected to IDT Vectors 0x08-0x0f. + // + CopyMem ( + Private->IntThunk->InterruptRedirectionCode, + (VOID *) (UINTN) InterruptRedirectionTemplate, + sizeof (Private->IntThunk->InterruptRedirectionCode) + ); + + // + // Save Unexpected interrupt vector so can restore it just prior to boot + // + BaseVectorMaster = (UINT32 *) (sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER); + Private->BiosUnexpectedInt = BaseVectorMaster[0]; + IntRedirCode = (UINT32) (UINTN) Private->IntThunk->InterruptRedirectionCode; + for (Index = 0; Index < 8; Index++) { + BaseVectorMaster[Index] = (EFI_SEGMENT (IntRedirCode + Index * 4) << 16) | EFI_OFFSET (IntRedirCode + Index * 4); + } + // + // Save EFI value + // + Private->ThunkSeg = (UINT16) (EFI_SEGMENT (IntRedirCode)); + + // + // Make a new handle and install the protocol + // + Private->Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &Private->Handle, + &gEfiLegacyBiosProtocolGuid, + EFI_NATIVE_INTERFACE, + &Private->LegacyBios + ); + Private->Csm16PciInterfaceVersion = GetPciInterfaceVersion (Private); + + DEBUG ((EFI_D_INFO, "CSM16 PCI BIOS Interface Version: %02x.%02x\n", + (UINT8) (Private->Csm16PciInterfaceVersion >> 8), + (UINT8) Private->Csm16PciInterfaceVersion + )); + ASSERT (Private->Csm16PciInterfaceVersion != 0); + return Status; +} diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf new file mode 100644 index 0000000000..4e310aa298 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf @@ -0,0 +1,143 @@ +## @file +# Legacy Bios Module to support CSM. +# +# This driver installs Legacy Bios Protocol to support CSM module work in EFI system. +# +# Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions +# of the BSD License which accompanies this distribution. The +# full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = LegacyBiosDxe + FILE_GUID = F122A15C-C10B-4d54-8F48-60F4F06DD1AD + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = LegacyBiosInstall + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + LegacyCmos.c + LegacyIde.c + LegacyBios.c + LegacyBda.c + LegacyBiosInterface.h + LegacyPci.c + +[Sources.Ia32] + IA32/InterruptTable.S + IA32/InterruptTable.asm + Thunk.c + LegacyBootSupport.c + LegacyBbs.c + LegacySio.c + +[Sources.X64] + X64/InterruptTable.asm + X64/InterruptTable.S + Thunk.c + LegacyBootSupport.c + LegacyBbs.c + LegacySio.c + +[Sources.IPF] + Ipf/IpfThunk.s + Ipf/Thunk.c + Ipf/IpfThunk.i + Ipf/IpfBootSupport.c + Ipf/IpfThunk.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + IntelFrameworkPkg/IntelFrameworkPkg.dec + IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec + + +[LibraryClasses] + DevicePathLib + UefiBootServicesTableLib + MemoryAllocationLib + UefiDriverEntryPoint + BaseMemoryLib + UefiLib + DebugLib + DxeServicesTableLib + PcdLib + ReportStatusCodeLib + PeCoffLib + CacheMaintenanceLib + DebugAgentLib + +[LibraryClasses.IA32] + IoLib + HobLib + UefiRuntimeServicesTableLib + BaseLib + +[LibraryClasses.X64] + IoLib + HobLib + UefiRuntimeServicesTableLib + BaseLib + +[LibraryClasses.IPF] + IoLib + UefiRuntimeServicesTableLib + + +[Guids] + gEfiDiskInfoIdeInterfaceGuid # ALWAYS_CONSUMED + gEfiLegacyBiosGuid # ALWAYS_PRODUCED + +[Guids.IA32] + gEfiSmbiosTableGuid # ALWAYS_CONSUMED + gEfiAcpi20TableGuid # ALWAYS_CONSUMED + gEfiAcpi10TableGuid # ALWAYS_CONSUMED + +[Guids.X64] + gEfiSmbiosTableGuid # ALWAYS_CONSUMED + gEfiAcpi20TableGuid # ALWAYS_CONSUMED + gEfiAcpi10TableGuid # ALWAYS_CONSUMED + + +[Protocols] + gEfiLoadedImageProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiPciRootBridgeIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiCpuArchProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIsaIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiBlockIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiPciIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiGenericMemTestProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiDiskInfoProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiSimpleTextInProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiLegacy8259ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiLegacyBiosPlatformProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiLegacyInterruptProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiLegacyRegion2ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiLegacyBiosProtocolGuid # PROTOCOL ALWAYS_PRODUCED + gEfiTimerArchProtocolGuid # PROTOCOL ALWAYS_PRODUCED + +[Pcd] + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdLegacyBiosCacheLegacyRegion + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdEbdaReservedMemorySize + +[Depex] + gEfiLegacyRegion2ProtocolGuid AND gEfiLegacyInterruptProtocolGuid AND gEfiLegacyBiosPlatformProtocolGuid AND gEfiLegacy8259ProtocolGuid AND gEfiGenericMemTestProtocolGuid AND gEfiCpuArchProtocolGuid + diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h new file mode 100644 index 0000000000..c398d1e1ee --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h @@ -0,0 +1,1501 @@ +/** @file + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _LEGACY_BIOS_INTERFACE_ +#define _LEGACY_BIOS_INTERFACE_ + + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// System Tickers +// +#define DEFAULT_LAGACY_TIMER_TICK_DURATION 549254 +// BUGBUG: This entry maybe changed to PCD in future and wait for +// redesign of BDS library +// +#define MAX_BBS_ENTRIES 0x100 + +// +// Thunk Status Codes +// (These apply only to errors with the thunk and not to the code that was +// thunked to.) +// +#define THUNK_OK 0x00 +#define THUNK_ERR_A20_UNSUP 0x01 +#define THUNK_ERR_A20_FAILED 0x02 + +// +// Vector base definitions +// +// +// 8259 Hardware definitions +// +#define LEGACY_MODE_BASE_VECTOR_MASTER 0x08 +#define LEGACY_MODE_BASE_VECTOR_SLAVE 0x70 + +// +// The original PC used INT8-F for master PIC. Since these mapped over +// processor exceptions TIANO moved the master PIC to INT68-6F. +// +// The vector base for slave PIC is set as 0x70 for PC-AT compatibility. +// +#define PROTECTED_MODE_BASE_VECTOR_MASTER 0x68 +#define PROTECTED_MODE_BASE_VECTOR_SLAVE 0x70 + +// +// Trace defines +// +// +#define LEGACY_BDA_TRACE 0x000 +#define LEGACY_BIOS_TRACE 0x040 +#define LEGACY_BOOT_TRACE 0x080 +#define LEGACY_CMOS_TRACE 0x0C0 +#define LEGACY_IDE_TRACE 0x100 +#define LEGACY_MP_TRACE 0x140 +#define LEGACY_PCI_TRACE 0x180 +#define LEGACY_SIO_TRACE 0x1C0 + +#define LEGACY_PCI_TRACE_000 LEGACY_PCI_TRACE + 0x00 +#define LEGACY_PCI_TRACE_001 LEGACY_PCI_TRACE + 0x01 +#define LEGACY_PCI_TRACE_002 LEGACY_PCI_TRACE + 0x02 +#define LEGACY_PCI_TRACE_003 LEGACY_PCI_TRACE + 0x03 +#define LEGACY_PCI_TRACE_004 LEGACY_PCI_TRACE + 0x04 +#define LEGACY_PCI_TRACE_005 LEGACY_PCI_TRACE + 0x05 +#define LEGACY_PCI_TRACE_006 LEGACY_PCI_TRACE + 0x06 +#define LEGACY_PCI_TRACE_007 LEGACY_PCI_TRACE + 0x07 +#define LEGACY_PCI_TRACE_008 LEGACY_PCI_TRACE + 0x08 +#define LEGACY_PCI_TRACE_009 LEGACY_PCI_TRACE + 0x09 +#define LEGACY_PCI_TRACE_00A LEGACY_PCI_TRACE + 0x0A +#define LEGACY_PCI_TRACE_00B LEGACY_PCI_TRACE + 0x0B +#define LEGACY_PCI_TRACE_00C LEGACY_PCI_TRACE + 0x0C +#define LEGACY_PCI_TRACE_00D LEGACY_PCI_TRACE + 0x0D +#define LEGACY_PCI_TRACE_00E LEGACY_PCI_TRACE + 0x0E +#define LEGACY_PCI_TRACE_00F LEGACY_PCI_TRACE + 0x0F + + +typedef struct { + UINTN PciSegment; + UINTN PciBus; + UINTN PciDevice; + UINTN PciFunction; + UINT32 ShadowAddress; + UINT32 ShadowedSize; + UINT8 DiskStart; + UINT8 DiskEnd; +} ROM_INSTANCE_ENTRY; + +// +// Values for RealModeGdt +// +#if defined (MDE_CPU_IA32) + +#define NUM_REAL_GDT_ENTRIES 3 +#define CONVENTIONAL_MEMORY_TOP 0xA0000 // 640 KB +#define INITIAL_VALUE_BELOW_1K 0x0 + +#elif defined (MDE_CPU_X64) + +#define NUM_REAL_GDT_ENTRIES 8 +#define CONVENTIONAL_MEMORY_TOP 0xA0000 // 640 KB +#define INITIAL_VALUE_BELOW_1K 0x0 + +#elif defined (MDE_CPU_IPF) + +#define NUM_REAL_GDT_ENTRIES 3 +#define CONVENTIONAL_MEMORY_TOP 0x80000 // 512 KB +#define INITIAL_VALUE_BELOW_1K 0xff + +#endif + +// +// Miscellaneous numbers +// +#define PMM_MEMORY_SIZE 0x400000 // 4 MB + +#pragma pack(1) + +// +// Define what a processor GDT looks like +// +typedef struct { + UINT32 LimitLo : 16; + UINT32 BaseLo : 16; + UINT32 BaseMid : 8; + UINT32 Type : 4; + UINT32 System : 1; + UINT32 Dpl : 2; + UINT32 Present : 1; + UINT32 LimitHi : 4; + UINT32 Software : 1; + UINT32 Reserved : 1; + UINT32 DefaultSize : 1; + UINT32 Granularity : 1; + UINT32 BaseHi : 8; +} GDT32; + +typedef struct { + UINT16 LimitLow; + UINT16 BaseLow; + UINT8 BaseMid; + UINT8 Attribute; + UINT8 LimitHi; + UINT8 BaseHi; +} GDT64; + +// +// Define what a processor descriptor looks like +// This data structure must be kept in sync with ASM STRUCT in Thunk.inc +// +typedef struct { + UINT16 Limit; + UINT64 Base; +} DESCRIPTOR64; + +typedef struct { + UINT16 Limit; + UINT32 Base; +} DESCRIPTOR32; + +// +// Low stub lay out +// +#define LOW_STACK_SIZE (8 * 1024) // 8k? +#define EFI_MAX_E820_ENTRY 100 +#define FIRST_INSTANCE 1 +#define NOT_FIRST_INSTANCE 0 + +#if defined (MDE_CPU_IA32) +typedef struct { + // + // Space for the code + // The address of Code is also the beginning of the relocated Thunk code + // + CHAR8 Code[4096]; // ? + // + // The address of the Reverse Thunk code + // Note that this member CONTAINS the address of the relocated reverse thunk + // code unlike the member variable 'Code', which IS the address of the Thunk + // code. + // + UINT32 LowReverseThunkStart; + + // + // Data for the code (cs releative) + // + DESCRIPTOR32 GdtDesc; // Protected mode GDT + DESCRIPTOR32 IdtDesc; // Protected mode IDT + UINT32 FlatSs; + UINT32 FlatEsp; + + UINT32 LowCodeSelector; // Low code selector in GDT + UINT32 LowDataSelector; // Low data selector in GDT + UINT32 LowStack; + DESCRIPTOR32 RealModeIdtDesc; + + // + // real-mode GDT (temporary GDT with two real mode segment descriptors) + // + GDT32 RealModeGdt[NUM_REAL_GDT_ENTRIES]; + DESCRIPTOR32 RealModeGdtDesc; + + // + // Members specifically for the reverse thunk + // The RevReal* members are used to store the current state of real mode + // before performing the reverse thunk. The RevFlat* members must be set + // before calling the reverse thunk assembly code. + // + UINT16 RevRealDs; + UINT16 RevRealSs; + UINT32 RevRealEsp; + DESCRIPTOR32 RevRealIdtDesc; + UINT16 RevFlatDataSelector; // Flat data selector in GDT + UINT32 RevFlatStack; + + // + // A low memory stack + // + CHAR8 Stack[LOW_STACK_SIZE]; + + // + // Stack for flat mode after reverse thunk + // @bug - This may no longer be necessary if the reverse thunk interface + // is changed to have the flat stack in a different location. + // + CHAR8 RevThunkStack[LOW_STACK_SIZE]; + + // + // Legacy16 Init memory map info + // + EFI_TO_COMPATIBILITY16_INIT_TABLE EfiToLegacy16InitTable; + + EFI_TO_COMPATIBILITY16_BOOT_TABLE EfiToLegacy16BootTable; + + CHAR8 InterruptRedirectionCode[32]; + EFI_LEGACY_INSTALL_PCI_HANDLER PciHandler; + EFI_DISPATCH_OPROM_TABLE DispatchOpromTable; + BBS_TABLE BbsTable[MAX_BBS_ENTRIES]; +} LOW_MEMORY_THUNK; + +#elif defined (MDE_CPU_X64) + +typedef struct { + // + // Space for the code + // The address of Code is also the beginning of the relocated Thunk code + // + CHAR8 Code[4096]; // ? + + // + // Data for the code (cs releative) + // + DESCRIPTOR64 X64GdtDesc; // Protected mode GDT + DESCRIPTOR64 X64IdtDesc; // Protected mode IDT + UINTN X64Ss; + UINTN X64Esp; + + UINTN RealStack; + DESCRIPTOR32 RealModeIdtDesc; + DESCRIPTOR32 RealModeGdtDesc; + + // + // real-mode GDT (temporary GDT with two real mode segment descriptors) + // + GDT64 RealModeGdt[NUM_REAL_GDT_ENTRIES]; + UINT64 PageMapLevel4; + + // + // A low memory stack + // + CHAR8 Stack[LOW_STACK_SIZE]; + + // + // Legacy16 Init memory map info + // + EFI_TO_COMPATIBILITY16_INIT_TABLE EfiToLegacy16InitTable; + + EFI_TO_COMPATIBILITY16_BOOT_TABLE EfiToLegacy16BootTable; + + CHAR8 InterruptRedirectionCode[32]; + EFI_LEGACY_INSTALL_PCI_HANDLER PciHandler; + EFI_DISPATCH_OPROM_TABLE DispatchOpromTable; + BBS_TABLE BbsTable[MAX_BBS_ENTRIES]; +} LOW_MEMORY_THUNK; + +#elif defined (MDE_CPU_IPF) + +typedef struct { + // + // Space for the code + // The address of Code is also the beginning of the relocated Thunk code + // + CHAR8 Code[4096]; // ? + // + // The address of the Reverse Thunk code + // Note that this member CONTAINS the address of the relocated reverse thunk + // code unlike the member variable 'Code', which IS the address of the Thunk + // code. + // + UINT32 LowReverseThunkStart; + + // + // Data for the code (cs releative) + // + DESCRIPTOR32 GdtDesc; // Protected mode GDT + DESCRIPTOR32 IdtDesc; // Protected mode IDT + UINT32 FlatSs; + UINT32 FlatEsp; + + UINT32 LowCodeSelector; // Low code selector in GDT + UINT32 LowDataSelector; // Low data selector in GDT + UINT32 LowStack; + DESCRIPTOR32 RealModeIdtDesc; + + // + // real-mode GDT (temporary GDT with two real mode segment descriptors) + // + GDT32 RealModeGdt[NUM_REAL_GDT_ENTRIES]; + DESCRIPTOR32 RealModeGdtDesc; + + // + // Members specifically for the reverse thunk + // The RevReal* members are used to store the current state of real mode + // before performing the reverse thunk. The RevFlat* members must be set + // before calling the reverse thunk assembly code. + // + UINT16 RevRealDs; + UINT16 RevRealSs; + UINT32 RevRealEsp; + DESCRIPTOR32 RevRealIdtDesc; + UINT16 RevFlatDataSelector; // Flat data selector in GDT + UINT32 RevFlatStack; + + // + // A low memory stack + // + CHAR8 Stack[LOW_STACK_SIZE]; + + // + // Stack for flat mode after reverse thunk + // @bug - This may no longer be necessary if the reverse thunk interface + // is changed to have the flat stack in a different location. + // + CHAR8 RevThunkStack[LOW_STACK_SIZE]; + + // + // Legacy16 Init memory map info + // + EFI_TO_COMPATIBILITY16_INIT_TABLE EfiToLegacy16InitTable; + + EFI_TO_COMPATIBILITY16_BOOT_TABLE EfiToLegacy16BootTable; + + CHAR8 InterruptRedirectionCode[32]; + EFI_LEGACY_INSTALL_PCI_HANDLER PciHandler; + EFI_DISPATCH_OPROM_TABLE DispatchOpromTable; + BBS_TABLE BbsTable[MAX_BBS_ENTRIES]; +} LOW_MEMORY_THUNK; + +#endif + +// +// PnP Expansion Header +// +typedef struct { + UINT32 PnpSignature; + UINT8 Revision; + UINT8 Length; + UINT16 NextHeader; + UINT8 Reserved1; + UINT8 Checksum; + UINT32 DeviceId; + UINT16 MfgPointer; + UINT16 ProductNamePointer; + UINT8 Class; + UINT8 SubClass; + UINT8 Interface; + UINT8 DeviceIndicators; + UINT16 Bcv; + UINT16 DisconnectVector; + UINT16 Bev; + UINT16 Reserved2; + UINT16 StaticResourceVector; +} LEGACY_PNP_EXPANSION_HEADER; + +typedef struct { + UINT8 PciSegment; + UINT8 PciBus; + UINT8 PciDevice; + UINT8 PciFunction; + UINT16 Vid; + UINT16 Did; + UINT16 SysSid; + UINT16 SVid; + UINT8 Class; + UINT8 SubClass; + UINT8 Interface; + UINT8 Reserved; + UINTN RomStart; + UINTN ManufacturerString; + UINTN ProductNameString; +} LEGACY_ROM_AND_BBS_TABLE; + +// +// Structure how EFI has mapped a devices HDD drive numbers. +// Boot to EFI aware OS or shell requires this mapping when +// 16-bit CSM assigns drive numbers. +// This mapping is ignored booting to a legacy OS. +// +typedef struct { + UINT8 PciSegment; + UINT8 PciBus; + UINT8 PciDevice; + UINT8 PciFunction; + UINT8 StartDriveNumber; + UINT8 EndDriveNumber; +} LEGACY_EFI_HDD_TABLE; + +// +// This data is passed to Leacy16Boot +// +typedef enum { + EfiAcpiAddressRangeMemory = 1, + EfiAcpiAddressRangeReserved = 2, + EfiAcpiAddressRangeACPI = 3, + EfiAcpiAddressRangeNVS = 4 +} EFI_ACPI_MEMORY_TYPE; + +typedef struct { + UINT64 BaseAddr; + UINT64 Length; + EFI_ACPI_MEMORY_TYPE Type; +} EFI_E820_ENTRY64; + +typedef struct { + UINT32 BassAddrLow; + UINT32 BaseAddrHigh; + UINT32 LengthLow; + UINT32 LengthHigh; + EFI_ACPI_MEMORY_TYPE Type; +} EFI_E820_ENTRY; + +#pragma pack() + +extern BBS_TABLE *mBbsTable; + +extern EFI_GENERIC_MEMORY_TEST_PROTOCOL *gGenMemoryTest; + +extern UINTN mEndOpromShadowAddress; + +#define PORT_70 0x70 +#define PORT_71 0x71 + +#define CMOS_0A 0x0a ///< Status register A +#define CMOS_0D 0x0d ///< Status register D +#define CMOS_0E 0x0e ///< Diagnostic Status +#define CMOS_0F 0x0f ///< Shutdown status +#define CMOS_10 0x10 ///< Floppy type +#define CMOS_12 0x12 ///< IDE type +#define CMOS_14 0x14 ///< Same as BDA 40:10 +#define CMOS_15 0x15 ///< Low byte of base memory in 1k increments +#define CMOS_16 0x16 ///< High byte of base memory in 1k increments +#define CMOS_17 0x17 ///< Low byte of 1MB+ memory in 1k increments - max 15 MB +#define CMOS_18 0x18 ///< High byte of 1MB+ memory in 1k increments - max 15 MB +#define CMOS_19 0x19 ///< C: extended drive type +#define CMOS_1A 0x1a ///< D: extended drive type +#define CMOS_2E 0x2e ///< Most significient byte of standard checksum +#define CMOS_2F 0x2f ///< Least significient byte of standard checksum +#define CMOS_30 0x30 ///< CMOS 0x17 +#define CMOS_31 0x31 ///< CMOS 0x18 +#define CMOS_32 0x32 ///< Century byte + + +#define LEGACY_BIOS_INSTANCE_SIGNATURE SIGNATURE_32 ('L', 'B', 'I', 'T') +typedef struct { + UINTN Signature; + + EFI_HANDLE Handle; + EFI_LEGACY_BIOS_PROTOCOL LegacyBios; + + EFI_HANDLE ImageHandle; + + // + // CPU Architectural Protocol + // + EFI_CPU_ARCH_PROTOCOL *Cpu; + + // + // Protocol to Lock and Unlock 0xc0000 - 0xfffff + // + EFI_LEGACY_REGION2_PROTOCOL *LegacyRegion; + + EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform; + + // + // Interrupt control for thunk and PCI IRQ + // + EFI_LEGACY_8259_PROTOCOL *Legacy8259; + + // + // PCI Interrupt PIRQ control + // + EFI_LEGACY_INTERRUPT_PROTOCOL *LegacyInterrupt; + + // + // Generic Memory Test + // + EFI_GENERIC_MEMORY_TEST_PROTOCOL *GenericMemoryTest; + + // + // TRUE if PCI Interupt Line registers have been programmed. + // + BOOLEAN PciInterruptLine; + + // + // Code space below 1MB needed by thunker to transition to real mode. + // Contains stack and real mode code fragments + // + LOW_MEMORY_THUNK *IntThunk; + + // + // Starting shadow address of the Legacy BIOS + // + UINT32 BiosStart; + UINT32 LegacyBiosImageSize; + + // + // Start of variables used by CsmItp.mac ITP macro file and/os LegacyBios + // + UINT8 Dump[4]; + + // + // $EFI Legacy16 code entry info in memory < 1 MB; + // + EFI_COMPATIBILITY16_TABLE *Legacy16Table; + VOID *Legacy16InitPtr; + VOID *Legacy16BootPtr; + VOID *InternalIrqRoutingTable; + UINT32 NumberIrqRoutingEntries; + VOID *BbsTablePtr; + VOID *HddTablePtr; + UINT32 NumberHddControllers; + + // + // Cached copy of Legacy16 entry point + // + UINT16 Legacy16CallSegment; + UINT16 Legacy16CallOffset; + + // + // Returned from $EFI and passed in to OPROMS + // + UINT16 PnPInstallationCheckSegment; + UINT16 PnPInstallationCheckOffset; + + // + // E820 table + // + EFI_E820_ENTRY E820Table[EFI_MAX_E820_ENTRY]; + UINT32 NumberE820Entries; + + // + // True if legacy VGA INT 10h handler installed + // + BOOLEAN VgaInstalled; + + // + // Number of IDE drives + // + UINT8 IdeDriveCount; + + // + // Current Free Option ROM space. An option ROM must NOT go past + // BiosStart. + // + UINT32 OptionRom; + + // + // Save Legacy16 unexpected interrupt vector. Reprogram INT 68-6F from + // EFI values to legacy value just before boot. + // + UINT32 BiosUnexpectedInt; + UINT32 ThunkSavedInt[8]; + UINT16 ThunkSeg; + LEGACY_EFI_HDD_TABLE *LegacyEfiHddTable; + UINT16 LegacyEfiHddTableIndex; + UINT8 DiskEnd; + UINT8 Disk4075; + UINT16 TraceIndex; + UINT16 Trace[0x200]; + + // + // Indicate that whether GenericLegacyBoot is entered or not + // + BOOLEAN LegacyBootEntered; + + // + // CSM16 PCI Interface Version + // + UINT16 Csm16PciInterfaceVersion; + +} LEGACY_BIOS_INSTANCE; + + +#pragma pack(1) + +/* + 40:00-01 Com1 + 40:02-03 Com2 + 40:04-05 Com3 + 40:06-07 Com4 + 40:08-09 Lpt1 + 40:0A-0B Lpt2 + 40:0C-0D Lpt3 + 40:0E-0E Ebda segment + 40:10-11 MachineConfig + 40:12 Bda12 - skip + 40:13-14 MemSize below 1MB + 40:15-16 Bda15_16 - skip + 40:17 Keyboard Shift status + 40:18-19 Bda18_19 - skip + 40:1A-1B Key buffer head + 40:1C-1D Key buffer tail + 40:1E-3D Bda1E_3D- key buffer -skip + 40:3E-3F FloppyData 3E = Calibration status 3F = Motor status + 40:40 FloppyTimeout + 40:41-74 Bda41_74 - skip + 40:75 Number of HDD drives + 40:76-77 Bda76_77 - skip + 40:78-79 78 = Lpt1 timeout, 79 = Lpt2 timeout + 40:7A-7B 7A = Lpt3 timeout, 7B = Lpt4 timeout + 40:7C-7D 7C = Com1 timeout, 7D = Com2 timeout + 40:7E-7F 7E = Com3 timeout, 7F = Com4 timeout + 40:80-81 Pointer to start of key buffer + 40:82-83 Pointer to end of key buffer + 40:84-87 Bda84_87 - skip + 40:88 HDD Data Xmit rate + 40:89-8f skip + 40:90 Floppy data rate + 40:91-95 skip + 40:96 Keyboard Status + 40:97 LED Status + 40:98-101 skip +*/ +typedef struct { + UINT16 Com1; + UINT16 Com2; + UINT16 Com3; + UINT16 Com4; + UINT16 Lpt1; + UINT16 Lpt2; + UINT16 Lpt3; + UINT16 Ebda; + UINT16 MachineConfig; + UINT8 Bda12; + UINT16 MemSize; + UINT8 Bda15_16[0x02]; + UINT8 ShiftStatus; + UINT8 Bda18_19[0x02]; + UINT16 KeyHead; + UINT16 KeyTail; + UINT16 Bda1E_3D[0x10]; + UINT16 FloppyData; + UINT8 FloppyTimeout; + UINT8 Bda41_74[0x34]; + UINT8 NumberOfDrives; + UINT8 Bda76_77[0x02]; + UINT16 Lpt1_2Timeout; + UINT16 Lpt3_4Timeout; + UINT16 Com1_2Timeout; + UINT16 Com3_4Timeout; + UINT16 KeyStart; + UINT16 KeyEnd; + UINT8 Bda84_87[0x4]; + UINT8 DataXmit; + UINT8 Bda89_8F[0x07]; + UINT8 FloppyXRate; + UINT8 Bda91_95[0x05]; + UINT8 KeyboardStatus; + UINT8 LedStatus; +} BDA_STRUC; +#pragma pack() + +#define LEGACY_BIOS_INSTANCE_FROM_THIS(this) CR (this, LEGACY_BIOS_INSTANCE, LegacyBios, LEGACY_BIOS_INSTANCE_SIGNATURE) + +/** + Thunk to 16-bit real mode and execute a software interrupt with a vector + of BiosInt. Regs will contain the 16-bit register context on entry and + exit. + + @param This Protocol instance pointer. + @param BiosInt Processor interrupt vector to invoke + @param Regs Register contexted passed into (and returned) from thunk to + 16-bit mode + + @retval FALSE Thunk completed, and there were no BIOS errors in the target code. + See Regs for status. + @retval TRUE There was a BIOS erro in the target code. + +**/ +BOOLEAN +EFIAPI +LegacyBiosInt86 ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINT8 BiosInt, + IN EFI_IA32_REGISTER_SET *Regs + ); + + +/** + Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the + 16-bit register context on entry and exit. Arguments can be passed on + the Stack argument + + @param This Protocol instance pointer. + @param Segment Segemnt of 16-bit mode call + @param Offset Offset of 16-bit mdoe call + @param Regs Register contexted passed into (and returned) from + thunk to 16-bit mode + @param Stack Caller allocated stack used to pass arguments + @param StackSize Size of Stack in bytes + + @retval FALSE Thunk completed, and there were no BIOS errors in + the target code. See Regs for status. + @retval TRUE There was a BIOS erro in the target code. + +**/ +BOOLEAN +EFIAPI +LegacyBiosFarCall86 ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINT16 Segment, + IN UINT16 Offset, + IN EFI_IA32_REGISTER_SET *Regs, + IN VOID *Stack, + IN UINTN StackSize + ); + + +/** + Test to see if a legacy PCI ROM exists for this device. Optionally return + the Legacy ROM instance for this PCI device. + + @param This Protocol instance pointer. + @param PciHandle The PCI PC-AT OPROM from this devices ROM BAR will + be loaded + @param RomImage Return the legacy PCI ROM for this device + @param RomSize Size of ROM Image + @param Flags Indicates if ROM found and if PC-AT. + + @retval EFI_SUCCESS Legacy Option ROM availible for this device + @retval EFI_UNSUPPORTED Legacy Option ROM not supported. + +**/ +EFI_STATUS +EFIAPI +LegacyBiosCheckPciRom ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN EFI_HANDLE PciHandle, + OUT VOID **RomImage, OPTIONAL + OUT UINTN *RomSize, OPTIONAL + OUT UINTN *Flags + ); + + +/** + Assign drive number to legacy HDD drives prior to booting an EFI + aware OS so the OS can access drives without an EFI driver. + Note: BBS compliant drives ARE NOT available until this call by + either shell or EFI. + + @param This Protocol instance pointer. + @param BbsCount Number of BBS_TABLE structures + @param BbsTable List BBS entries + + @retval EFI_SUCCESS Drive numbers assigned + +**/ +EFI_STATUS +EFIAPI +LegacyBiosPrepareToBootEfi ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + OUT UINT16 *BbsCount, + OUT BBS_TABLE **BbsTable + ); + + +/** + To boot from an unconventional device like parties and/or execute + HDD diagnostics. + + @param This Protocol instance pointer. + @param Attributes How to interpret the other input parameters + @param BbsEntry The 0-based index into the BbsTable for the parent + device. + @param BeerData Pointer to the 128 bytes of ram BEER data. + @param ServiceAreaData Pointer to the 64 bytes of raw Service Area data. + The caller must provide a pointer to the specific + Service Area and not the start all Service Areas. + EFI_INVALID_PARAMETER if error. Does NOT return if no error. + +**/ +EFI_STATUS +EFIAPI +LegacyBiosBootUnconventionalDevice ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UDC_ATTRIBUTES Attributes, + IN UINTN BbsEntry, + IN VOID *BeerData, + IN VOID *ServiceAreaData + ); + + +/** + Load a legacy PC-AT OPROM on the PciHandle device. Return information + about how many disks were added by the OPROM and the shadow address and + size. DiskStart & DiskEnd are INT 13h drive letters. Thus 0x80 is C: + + @param This Protocol instance pointer. + @param PciHandle The PCI PC-AT OPROM from this devices ROM BAR will + be loaded. This value is NULL if RomImage is + non-NULL. This is the normal case. + @param RomImage A PCI PC-AT ROM image. This argument is non-NULL + if there is no hardware associated with the ROM + and thus no PciHandle, otherwise is must be NULL. + Example is PXE base code. + @param Flags Indicates if ROM found and if PC-AT. + @param DiskStart Disk number of first device hooked by the ROM. If + DiskStart is the same as DiskEnd no disked were + hooked. + @param DiskEnd Disk number of the last device hooked by the ROM. + @param RomShadowAddress Shadow address of PC-AT ROM + @param RomShadowedSize Size of RomShadowAddress in bytes + + @retval EFI_SUCCESS Legacy ROM loaded for this device + @retval EFI_INVALID_PARAMETER PciHandle not found + @retval EFI_UNSUPPORTED There is no PCI ROM in the ROM BAR or no onboard + ROM + +**/ +EFI_STATUS +EFIAPI +LegacyBiosInstallPciRom ( + IN EFI_LEGACY_BIOS_PROTOCOL * This, + IN EFI_HANDLE PciHandle, + IN VOID **RomImage, + OUT UINTN *Flags, + OUT UINT8 *DiskStart, OPTIONAL + OUT UINT8 *DiskEnd, OPTIONAL + OUT VOID **RomShadowAddress, OPTIONAL + OUT UINT32 *RomShadowedSize OPTIONAL + ); + + +/** + Fill in the standard BDA for Keyboard LEDs + + @param This Protocol instance pointer. + @param Leds Current LED status + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +EFIAPI +LegacyBiosUpdateKeyboardLedStatus ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINT8 Leds + ); + + +/** + Get all BBS info + + @param This Protocol instance pointer. + @param HddCount Number of HDD_INFO structures + @param HddInfo Onboard IDE controller information + @param BbsCount Number of BBS_TABLE structures + @param BbsTable List BBS entries + + @retval EFI_SUCCESS Tables returned + @retval EFI_NOT_FOUND resource not found + @retval EFI_DEVICE_ERROR can not get BBS table + +**/ +EFI_STATUS +EFIAPI +LegacyBiosGetBbsInfo ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + OUT UINT16 *HddCount, + OUT HDD_INFO **HddInfo, + OUT UINT16 *BbsCount, + OUT BBS_TABLE **BbsTable + ); + + +/** + Shadow all legacy16 OPROMs that haven't been shadowed. + Warning: Use this with caution. This routine disconnects all EFI + drivers. If used externally then caller must re-connect EFI + drivers. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS OPROMs shadowed + +**/ +EFI_STATUS +EFIAPI +LegacyBiosShadowAllLegacyOproms ( + IN EFI_LEGACY_BIOS_PROTOCOL *This + ); + + +/** + Attempt to legacy boot the BootOption. If the EFI contexted has been + compromised this function will not return. + + @param This Protocol instance pointer. + @param BbsDevicePath EFI Device Path from BootXXXX variable. + @param LoadOptionsSize Size of LoadOption in size. + @param LoadOptions LoadOption from BootXXXX variable + + @retval EFI_SUCCESS Removable media not present + +**/ +EFI_STATUS +EFIAPI +LegacyBiosLegacyBoot ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN BBS_BBS_DEVICE_PATH *BbsDevicePath, + IN UINT32 LoadOptionsSize, + IN VOID *LoadOptions + ); + + +/** + Allocate memory < 1 MB and copy the thunker code into low memory. Se up + all the descriptors. + + @param Private Private context for Legacy BIOS + + @retval EFI_SUCCESS Should only pass. + +**/ +EFI_STATUS +LegacyBiosInitializeThunk ( + IN LEGACY_BIOS_INSTANCE *Private + ); + + +/** + Fill in the standard BDA and EBDA stuff before Legacy16 load + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosInitBda ( + IN LEGACY_BIOS_INSTANCE *Private + ); + + +/** + Collect IDE Inquiry data from the IDE disks + + @param Private Legacy BIOS Instance data + @param HddInfo Hdd Information + @param Flag Reconnect IdeController or not + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosBuildIdeData ( + IN LEGACY_BIOS_INSTANCE *Private, + IN HDD_INFO **HddInfo, + IN UINT16 Flag + ); + + +/** + Enable ide controller. This gets disabled when LegacyBoot.c is about + to run the Option ROMs. + + @param Private Legacy BIOS Instance data + + +**/ +VOID +EnableIdeController ( + IN LEGACY_BIOS_INSTANCE *Private + ); + + +/** + If the IDE channel is in compatibility (legacy) mode, remove all + PCI I/O BAR addresses from the controller. + + @param IdeController The handle of target IDE controller + + +**/ +VOID +InitLegacyIdeController ( + IN EFI_HANDLE IdeController + ); + + +/** + Program the interrupt routing register in all the PCI devices. On a PC AT system + this register contains the 8259 IRQ vector that matches it's PCI interrupt. + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS Succeed. + @retval EFI_ALREADY_STARTED All PCI devices have been processed. + +**/ +EFI_STATUS +PciProgramAllInterruptLineRegisters ( + IN LEGACY_BIOS_INSTANCE *Private + ); + + +/** + Collect EFI Info about legacy devices. + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosBuildSioData ( + IN LEGACY_BIOS_INSTANCE *Private + ); + + +/** + Shadow all the PCI legacy ROMs. Use data from the Legacy BIOS Protocol + to chose the order. Skip any devices that have already have legacy + BIOS run. + + @param Private Protocol instance pointer. + + @retval EFI_SUCCESS Succeed. + @retval EFI_UNSUPPORTED Cannot get VGA device handle. + +**/ +EFI_STATUS +PciShadowRoms ( + IN LEGACY_BIOS_INSTANCE *Private + ); + + +/** + Fill in the standard BDA and EBDA stuff prior to legacy Boot + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosCompleteBdaBeforeBoot ( + IN LEGACY_BIOS_INSTANCE *Private + ); + + +/** + Fill in the standard CMOS stuff before Legacy16 load + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosInitCmos ( + IN LEGACY_BIOS_INSTANCE *Private + ); + + +/** + Fill in the standard CMOS stuff prior to legacy Boot + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosCompleteStandardCmosBeforeBoot ( + IN LEGACY_BIOS_INSTANCE *Private + ); + + +/** + Contains the code that is copied into low memory (below 640K). + This code reflects interrupts 0x68-0x6f to interrupts 0x08-0x0f. + This template must be copied into low memory, and the IDT entries + 0x68-0x6F must be point to the low memory copy of this code. Each + entry is 4 bytes long, so IDT entries 0x68-0x6F can be easily + computed. + +**/ +VOID +InterruptRedirectionTemplate ( + VOID + ); + + +/** + Build the E820 table. + + @param Private Legacy BIOS Instance data + @param Size Size of E820 Table + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosBuildE820 ( + IN LEGACY_BIOS_INSTANCE *Private, + OUT UINTN *Size + ); + +/** + This function is to put all AP in halt state. + + @param Private Legacy BIOS Instance data + +**/ +VOID +ShutdownAPs ( + IN LEGACY_BIOS_INSTANCE *Private + ); + +/** + Worker function for LegacyBiosGetFlatDescs, retrieving content of + specific registers. + + @param IntThunk Pointer to IntThunk of Legacy BIOS context. + +**/ +VOID +GetRegisters ( + LOW_MEMORY_THUNK *IntThunk + ); + +/** + Routine for calling real thunk code. + + @param RealCode The address of thunk code. + @param BiosInt The Bios interrupt vector number. + @param CallAddress The address of 16-bit mode call. + + @return Status returned by real thunk code + +**/ +UINTN +CallRealThunkCode ( + UINT8 *RealCode, + UINT8 BiosInt, + UINT32 CallAddress + ); + +/** + Routine for generating soft interrupt. + + @param Vector The interrupt vector number. + +**/ +VOID +GenerateSoftInit ( + UINT8 Vector + ); + +/** + Do an AllocatePages () of type AllocateMaxAddress for EfiBootServicesCode + memory. + + @param AllocateType Allocated Legacy Memory Type + @param StartPageAddress Start address of range + @param Pages Number of pages to allocate + @param Result Result of allocation + + @retval EFI_SUCCESS Legacy16 code loaded + @retval Other No protocol installed, unload driver. + +**/ +EFI_STATUS +AllocateLegacyMemory ( + IN EFI_ALLOCATE_TYPE AllocateType, + IN EFI_PHYSICAL_ADDRESS StartPageAddress, + IN UINTN Pages, + OUT EFI_PHYSICAL_ADDRESS *Result + ); + +/** + Get a region from the LegacyBios for Tiano usage. Can only be invoked once. + + @param This Protocol instance pointer. + @param LegacyMemorySize Size of required region + @param Region Region to use. 00 = Either 0xE0000 or 0xF0000 + block Bit0 = 1 0xF0000 block Bit1 = 1 0xE0000 + block + @param Alignment Address alignment. Bit mapped. First non-zero + bit from right is alignment. + @param LegacyMemoryAddress Region Assigned + + @retval EFI_SUCCESS Region assigned + @retval EFI_ACCESS_DENIED Procedure previously invoked + @retval Other Region not assigned + +**/ +EFI_STATUS +EFIAPI +LegacyBiosGetLegacyRegion ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINTN LegacyMemorySize, + IN UINTN Region, + IN UINTN Alignment, + OUT VOID **LegacyMemoryAddress + ); + +/** + Get a region from the LegacyBios for Tiano usage. Can only be invoked once. + + @param This Protocol instance pointer. + @param LegacyMemorySize Size of data to copy + @param LegacyMemoryAddress Legacy Region destination address Note: must + be in region assigned by + LegacyBiosGetLegacyRegion + @param LegacyMemorySourceAddress Source of data + + @retval EFI_SUCCESS Region assigned + @retval EFI_ACCESS_DENIED Destination outside assigned region + +**/ +EFI_STATUS +EFIAPI +LegacyBiosCopyLegacyRegion ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINTN LegacyMemorySize, + IN VOID *LegacyMemoryAddress, + IN VOID *LegacyMemorySourceAddress + ); + +/** + Find Legacy16 BIOS image in the FLASH device and shadow it into memory. Find + the $EFI table in the shadow area. Thunk into the Legacy16 code after it had + been shadowed. + + @param Private Legacy BIOS context data + + @retval EFI_SUCCESS Legacy16 code loaded + @retval Other No protocol installed, unload driver. + +**/ +EFI_STATUS +ShadowAndStartLegacy16 ( + IN LEGACY_BIOS_INSTANCE *Private + ); + +/** + Checks the state of the floppy and if media is inserted. + + This routine checks the state of the floppy and if media is inserted. + There are 3 cases: + No floppy present - Set BBS entry to ignore + Floppy present & no media - Set BBS entry to lowest priority. We cannot + set it to ignore since 16-bit CSM will + indicate no floppy and thus drive A: is + unusable. CSM-16 will not try floppy since + lowest priority and thus not incur boot + time penality. + Floppy present & media - Set BBS entry to some priority. + + @return State of floppy media + +**/ +UINT8 +HasMediaInFloppy ( + VOID + ); + +/** + Identify drive data must be updated to actual parameters before boot. + This requires updating the checksum, if it exists. + + @param IdentifyDriveData ATA Identify Data + @param Checksum checksum of the ATA Identify Data + + @retval EFI_SUCCESS checksum calculated + @retval EFI_SECURITY_VIOLATION IdentifyData invalid + +**/ +EFI_STATUS +CalculateIdentifyDriveChecksum ( + IN UINT8 *IdentifyDriveData, + OUT UINT8 *Checksum + ); + +/** + Identify drive data must be updated to actual parameters before boot. + + @param IdentifyDriveData ATA Identify Data + +**/ +VOID +UpdateIdentifyDriveData ( + IN UINT8 *IdentifyDriveData + ); + +/** + Complete build of BBS TABLE. + + @param Private Legacy BIOS Instance data + @param BbsTable BBS Table passed to 16-bit code + + @retval EFI_SUCCESS Removable media not present + +**/ +EFI_STATUS +LegacyBiosBuildBbs ( + IN LEGACY_BIOS_INSTANCE *Private, + IN BBS_TABLE *BbsTable + ); + +/** + Read CMOS register through index/data port. + + @param[in] Index The index of the CMOS register to read. + + @return The data value from the CMOS register specified by Index. + +**/ +UINT8 +LegacyReadStandardCmos ( + IN UINT8 Index + ); + +/** + Write CMOS register through index/data port. + + @param[in] Index The index of the CMOS register to write. + @param[in] Value The value of CMOS register to write. + + @return The value written to the CMOS register specified by Index. + +**/ +UINT8 +LegacyWriteStandardCmos ( + IN UINT8 Index, + IN UINT8 Value + ); + +/** + Calculate the new standard CMOS checksum and write it. + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS Calculate 16-bit checksum successfully + +**/ +EFI_STATUS +LegacyCalculateWriteStandardCmosChecksum ( + VOID + ); + +/** + Test to see if a legacy PCI ROM exists for this device. Optionally return + the Legacy ROM instance for this PCI device. + + @param[in] This Protocol instance pointer. + @param[in] PciHandle The PCI PC-AT OPROM from this devices ROM BAR will be loaded + @param[out] RomImage Return the legacy PCI ROM for this device + @param[out] RomSize Size of ROM Image + @param[out] RuntimeImageLength Runtime size of ROM Image + @param[out] Flags Indicates if ROM found and if PC-AT. + @param[out] OpromRevision Revision of the PCI Rom + @param[out] ConfigUtilityCodeHeaderPointer of Configuration Utility Code Header + + @return EFI_SUCCESS Legacy Option ROM availible for this device + @return EFI_ALREADY_STARTED This device is already managed by its Oprom + @return EFI_UNSUPPORTED Legacy Option ROM not supported. + +**/ +EFI_STATUS +LegacyBiosCheckPciRomEx ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN EFI_HANDLE PciHandle, + OUT VOID **RomImage, OPTIONAL + OUT UINTN *RomSize, OPTIONAL + OUT UINTN *RuntimeImageLength, OPTIONAL + OUT UINTN *Flags, OPTIONAL + OUT UINT8 *OpromRevision, OPTIONAL + OUT VOID **ConfigUtilityCodeHeader OPTIONAL + ); + +/** + Relocate this image under 4G memory for IPF. + + @param ImageHandle Handle of driver image. + @param SystemTable Pointer to system table. + + @retval EFI_SUCCESS Image successfully relocated. + @retval EFI_ABORTED Failed to relocate image. + +**/ +EFI_STATUS +RelocateImageUnder4GIfNeeded ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +/** + Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the + 16-bit register context on entry and exit. Arguments can be passed on + the Stack argument + + @param This Protocol instance pointer. + @param Segment Segemnt of 16-bit mode call + @param Offset Offset of 16-bit mdoe call + @param Regs Register contexted passed into (and returned) from thunk to + 16-bit mode + @param Stack Caller allocated stack used to pass arguments + @param StackSize Size of Stack in bytes + + @retval FALSE Thunk completed, and there were no BIOS errors in the target code. + See Regs for status. + @retval TRUE There was a BIOS erro in the target code. + +**/ +BOOLEAN +EFIAPI +InternalLegacyBiosFarCall ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINT16 Segment, + IN UINT16 Offset, + IN EFI_IA32_REGISTER_SET *Regs, + IN VOID *Stack, + IN UINTN StackSize + ); + +#endif diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBootSupport.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBootSupport.c new file mode 100644 index 0000000000..40d027a523 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBootSupport.c @@ -0,0 +1,2032 @@ +/** @file + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "LegacyBiosInterface.h" +#include + +#define BOOT_LEGACY_OS 0 +#define BOOT_EFI_OS 1 +#define BOOT_UNCONVENTIONAL_DEVICE 2 + +UINT32 mLoadOptionsSize = 0; +UINTN mBootMode = BOOT_LEGACY_OS; +VOID *mLoadOptions = NULL; +BBS_BBS_DEVICE_PATH *mBbsDevicePathPtr = NULL; +BBS_BBS_DEVICE_PATH mBbsDevicePathNode; +UDC_ATTRIBUTES mAttributes = { 0, 0, 0, 0 }; +UINTN mBbsEntry = 0; +VOID *mBeerData = NULL; +VOID *mServiceAreaData = NULL; +UINT64 mLowWater = 0xffffffffffffffffULL; + +extern BBS_TABLE *mBbsTable; + +/** + Print the BBS Table. + + @param BbsTable The BBS table. + + +**/ +VOID +PrintBbsTable ( + IN BBS_TABLE *BbsTable + ) +{ + UINT16 Index; + UINT16 SubIndex; + CHAR8 *String; + + DEBUG ((EFI_D_INFO, "\n")); + DEBUG ((EFI_D_INFO, " NO Prio bb/dd/ff cl/sc Type Stat segm:offs mfgs:mfgo dess:deso\n")); + DEBUG ((EFI_D_INFO, "=================================================================\n")); + for (Index = 0; Index < MAX_BBS_ENTRIES; Index++) { + // + // Filter + // + if (BbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) { + continue; + } + + DEBUG (( + EFI_D_INFO, + " %02x: %04x %02x/%02x/%02x %02x/%02x %04x %04x", + (UINTN) Index, + (UINTN) BbsTable[Index].BootPriority, + (UINTN) BbsTable[Index].Bus, + (UINTN) BbsTable[Index].Device, + (UINTN) BbsTable[Index].Function, + (UINTN) BbsTable[Index].Class, + (UINTN) BbsTable[Index].SubClass, + (UINTN) BbsTable[Index].DeviceType, + (UINTN) * (UINT16 *) &BbsTable[Index].StatusFlags + )); + DEBUG (( + EFI_D_INFO, + " %04x:%04x %04x:%04x %04x:%04x", + (UINTN) BbsTable[Index].BootHandlerSegment, + (UINTN) BbsTable[Index].BootHandlerOffset, + (UINTN) BbsTable[Index].MfgStringSegment, + (UINTN) BbsTable[Index].MfgStringOffset, + (UINTN) BbsTable[Index].DescStringSegment, + (UINTN) BbsTable[Index].DescStringOffset + )); + + // + // Print DescString + // + String = (CHAR8 *)(UINTN)((BbsTable[Index].DescStringSegment << 4) + BbsTable[Index].DescStringOffset); + if (String != NULL) { + DEBUG ((EFI_D_INFO," (")); + for (SubIndex = 0; String[SubIndex] != 0; SubIndex++) { + DEBUG ((EFI_D_INFO, "%c", String[SubIndex])); + } + DEBUG ((EFI_D_INFO,")")); + } + DEBUG ((EFI_D_INFO,"\n")); + } + + DEBUG ((EFI_D_INFO, "\n")); + + return ; +} + +/** + Print the BBS Table. + + @param HddInfo The HddInfo table. + + +**/ +VOID +PrintHddInfo ( + IN HDD_INFO *HddInfo + ) +{ + UINTN Index; + + DEBUG ((EFI_D_INFO, "\n")); + for (Index = 0; Index < MAX_IDE_CONTROLLER; Index++) { + DEBUG ((EFI_D_INFO, "Index - %04x\n", Index)); + DEBUG ((EFI_D_INFO, " Status - %04x\n", (UINTN)HddInfo[Index].Status)); + DEBUG ((EFI_D_INFO, " B/D/F - %02x/%02x/%02x\n", (UINTN)HddInfo[Index].Bus, (UINTN)HddInfo[Index].Device, (UINTN)HddInfo[Index].Function)); + DEBUG ((EFI_D_INFO, " Command - %04x\n", HddInfo[Index].CommandBaseAddress)); + DEBUG ((EFI_D_INFO, " Control - %04x\n", HddInfo[Index].ControlBaseAddress)); + DEBUG ((EFI_D_INFO, " BusMaster - %04x\n", HddInfo[Index].BusMasterAddress)); + DEBUG ((EFI_D_INFO, " HddIrq - %02x\n", HddInfo[Index].HddIrq)); + DEBUG ((EFI_D_INFO, " IdentifyDrive[0].Raw[0] - %x\n", HddInfo[Index].IdentifyDrive[0].Raw[0])); + DEBUG ((EFI_D_INFO, " IdentifyDrive[1].Raw[0] - %x\n", HddInfo[Index].IdentifyDrive[1].Raw[0])); + } + + DEBUG ((EFI_D_INFO, "\n")); + + return ; +} + +/** + Identify drive data must be updated to actual parameters before boot. + + @param IdentifyDriveData ATA Identify Data + +**/ +VOID +UpdateIdentifyDriveData ( + IN UINT8 *IdentifyDriveData + ); + +/** + Update SIO data. + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS Removable media not present + +**/ +EFI_STATUS +UpdateSioData ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN Index1; + UINT8 LegacyInterrupts[16]; + EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable; + UINTN RoutingTableEntries; + EFI_LEGACY_IRQ_PRIORITY_TABLE_ENTRY *IrqPriorityTable; + UINTN NumberPriorityEntries; + EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable; + UINT8 HddIrq; + UINT16 LegacyInt; + UINT16 LegMask; + UINT32 Register; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + EFI_ISA_IO_PROTOCOL *IsaIo; + + LegacyInt = 0; + HandleBuffer = NULL; + + EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable; + LegacyBiosBuildSioData (Private); + SetMem (LegacyInterrupts, sizeof (LegacyInterrupts), 0); + + // + // Create list of legacy interrupts. + // + for (Index = 0; Index < 4; Index++) { + LegacyInterrupts[Index] = EfiToLegacy16BootTable->SioData.Serial[Index].Irq; + } + + for (Index = 4; Index < 7; Index++) { + LegacyInterrupts[Index] = EfiToLegacy16BootTable->SioData.Parallel[Index - 4].Irq; + } + + LegacyInterrupts[7] = EfiToLegacy16BootTable->SioData.Floppy.Irq; + + // + // Get Legacy Hdd IRQs. If native mode treat as PCI + // + for (Index = 0; Index < 2; Index++) { + HddIrq = EfiToLegacy16BootTable->HddInfo[Index].HddIrq; + if ((HddIrq != 0) && ((HddIrq == 15) || (HddIrq == 14))) { + LegacyInterrupts[Index + 8] = HddIrq; + } + } + + Private->LegacyBiosPlatform->GetRoutingTable ( + Private->LegacyBiosPlatform, + (VOID *) &RoutingTable, + &RoutingTableEntries, + NULL, + NULL, + (VOID **) &IrqPriorityTable, + &NumberPriorityEntries + ); + // + // Remove legacy interrupts from the list of PCI interrupts available. + // + for (Index = 0; Index <= 0x0b; Index++) { + for (Index1 = 0; Index1 <= NumberPriorityEntries; Index1++) { + if (LegacyInterrupts[Index] != 0) { + LegacyInt = (UINT16) (LegacyInt | (1 << LegacyInterrupts[Index])); + if (LegacyInterrupts[Index] == IrqPriorityTable[Index1].Irq) { + IrqPriorityTable[Index1].Used = LEGACY_USED; + } + } + } + } + + Private->Legacy8259->GetMask ( + Private->Legacy8259, + &LegMask, + NULL, + NULL, + NULL + ); + + // + // Set SIO interrupts and disable mouse. Let mouse driver + // re-enable it. + // + LegMask = (UINT16) ((LegMask &~LegacyInt) | 0x1000); + Private->Legacy8259->SetMask ( + Private->Legacy8259, + &LegMask, + NULL, + NULL, + NULL + ); + + // + // Disable mouse in keyboard controller + // + Register = 0xA7; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiIsaIoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiIsaIoProtocolGuid, + (VOID **) &IsaIo + ); + ASSERT_EFI_ERROR (Status); + IsaIo->Io.Write (IsaIo, EfiIsaIoWidthUint8, 0x64, 1, &Register); + + } + + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + return EFI_SUCCESS; + +} + +/** + Identify drive data must be updated to actual parameters before boot. + This requires updating the checksum, if it exists. + + @param IdentifyDriveData ATA Identify Data + @param Checksum checksum of the ATA Identify Data + + @retval EFI_SUCCESS checksum calculated + @retval EFI_SECURITY_VIOLATION IdentifyData invalid + +**/ +EFI_STATUS +CalculateIdentifyDriveChecksum ( + IN UINT8 *IdentifyDriveData, + OUT UINT8 *Checksum + ) +{ + UINTN Index; + UINT8 LocalChecksum; + LocalChecksum = 0; + *Checksum = 0; + if (IdentifyDriveData[510] != 0xA5) { + return EFI_SECURITY_VIOLATION; + } + + for (Index = 0; Index < 512; Index++) { + LocalChecksum = (UINT8) (LocalChecksum + IdentifyDriveData[Index]); + } + + *Checksum = LocalChecksum; + return EFI_SUCCESS; +} + + +/** + Identify drive data must be updated to actual parameters before boot. + + @param IdentifyDriveData ATA Identify Data + + +**/ +VOID +UpdateIdentifyDriveData ( + IN UINT8 *IdentifyDriveData + ) +{ + UINT16 NumberCylinders; + UINT16 NumberHeads; + UINT16 NumberSectorsTrack; + UINT32 CapacityInSectors; + UINT8 OriginalChecksum; + UINT8 FinalChecksum; + EFI_STATUS Status; + ATAPI_IDENTIFY *ReadInfo; + + // + // Status indicates if Integrity byte is correct. Checksum should be + // 0 if valid. + // + ReadInfo = (ATAPI_IDENTIFY *) IdentifyDriveData; + Status = CalculateIdentifyDriveChecksum (IdentifyDriveData, &OriginalChecksum); + if (OriginalChecksum != 0) { + Status = EFI_SECURITY_VIOLATION; + } + // + // If NumberCylinders = 0 then do data(Controller present but don drive attached). + // + NumberCylinders = ReadInfo->Raw[1]; + if (NumberCylinders != 0) { + ReadInfo->Raw[54] = NumberCylinders; + + NumberHeads = ReadInfo->Raw[3]; + ReadInfo->Raw[55] = NumberHeads; + + NumberSectorsTrack = ReadInfo->Raw[6]; + ReadInfo->Raw[56] = NumberSectorsTrack; + + // + // Copy Multisector info and set valid bit. + // + ReadInfo->Raw[59] = (UINT16) (ReadInfo->Raw[47] + 0x100); + CapacityInSectors = (UINT32) ((UINT32) (NumberCylinders) * (UINT32) (NumberHeads) * (UINT32) (NumberSectorsTrack)); + ReadInfo->Raw[57] = (UINT16) (CapacityInSectors >> 16); + ReadInfo->Raw[58] = (UINT16) (CapacityInSectors & 0xffff); + if (Status == EFI_SUCCESS) { + // + // Forece checksum byte to 0 and get new checksum. + // + ReadInfo->Raw[255] &= 0xff; + CalculateIdentifyDriveChecksum (IdentifyDriveData, &FinalChecksum); + + // + // Force new checksum such that sum is 0. + // + FinalChecksum = (UINT8) ((UINT8)0 - FinalChecksum); + ReadInfo->Raw[255] = (UINT16) (ReadInfo->Raw[255] | (FinalChecksum << 8)); + } + } +} + +/** + Identify drive data must be updated to actual parameters before boot. + Do for all drives. + + @param Private Legacy BIOS Instance data + + +**/ +VOID +UpdateAllIdentifyDriveData ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + UINTN Index; + HDD_INFO *HddInfo; + + HddInfo = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo[0]; + + for (Index = 0; Index < MAX_IDE_CONTROLLER; Index++) { + // + // Each controller can have 2 devices. Update for each device + // + if ((HddInfo[Index].Status & HDD_MASTER_IDE) != 0) { + UpdateIdentifyDriveData ((UINT8 *) (&HddInfo[Index].IdentifyDrive[0].Raw[0])); + } + + if ((HddInfo[Index].Status & HDD_SLAVE_IDE) != 0) { + UpdateIdentifyDriveData ((UINT8 *) (&HddInfo[Index].IdentifyDrive[1].Raw[0])); + } + } +} + +/** + Enable ide controller. This gets disabled when LegacyBoot.c is about + to run the Option ROMs. + + @param Private Legacy BIOS Instance data + + +**/ +VOID +EnableIdeController ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + EFI_HANDLE IdeController; + UINT8 ByteBuffer; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + + Status = Private->LegacyBiosPlatform->GetPlatformHandle ( + Private->LegacyBiosPlatform, + EfiGetPlatformIdeHandle, + 0, + &HandleBuffer, + &HandleCount, + NULL + ); + if (!EFI_ERROR (Status)) { + IdeController = HandleBuffer[0]; + Status = gBS->HandleProtocol ( + IdeController, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + ByteBuffer = 0x1f; + if (!EFI_ERROR (Status)) { + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x04, 1, &ByteBuffer); + } + } +} + + +/** + Enable ide controller. This gets disabled when LegacyBoot.c is about + to run the Option ROMs. + + @param Private Legacy BIOS Instance data + + +**/ +VOID +EnableAllControllers ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE01 PciConfigHeader; + EFI_STATUS Status; + + // + // + // + EnableIdeController (Private); + + // + // Assumption is table is built from low bus to high bus numbers. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciIoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + ASSERT_EFI_ERROR (Status); + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + ASSERT_EFI_ERROR (Status); + + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (PciConfigHeader) / sizeof (UINT32), + &PciConfigHeader + ); + + // + // We do not enable PPB here. This is for HotPlug Consideration. + // The Platform HotPlug Driver is responsible for Padding enough hot plug + // resources. It is also responsible for enable this bridge. If it + // does not pad it. It will cause some early Windows fail to installation. + // If the platform driver does not pad resource for PPB, PPB should be in + // un-enabled state to let Windows know that this PPB is not configured by + // BIOS. So Windows will allocate default resource for PPB. + // + // The reason for why we enable the command register is: + // The CSM will use the IO bar to detect some IRQ status, if the command + // is disabled, the IO resource will be out of scope. + // For example: + // We installed a legacy IRQ handle for a PCI IDE controller. When IRQ + // comes up, the handle will check the IO space to identify is the + // controller generated the IRQ source. + // If the IO command is not enabled, the IRQ handler will has wrong + // information. It will cause IRQ storm when the correctly IRQ handler fails + // to run. + // + if (!(IS_PCI_VGA (&PciConfigHeader) || + IS_PCI_OLD_VGA (&PciConfigHeader) || + IS_PCI_IDE (&PciConfigHeader) || + IS_PCI_P2P (&PciConfigHeader) || + IS_PCI_P2P_SUB (&PciConfigHeader) || + IS_PCI_LPC (&PciConfigHeader) )) { + + PciConfigHeader.Hdr.Command |= 0x1f; + + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 4, 1, &PciConfigHeader.Hdr.Command); + } + } +} + +/** + The following routines are identical in operation, so combine + for code compaction: + EfiGetPlatformBinaryGetMpTable + EfiGetPlatformBinaryGetOemIntData + EfiGetPlatformBinaryGetOem32Data + EfiGetPlatformBinaryGetOem16Data + + @param This Protocol instance pointer. + @param Id Table/Data identifier + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER Invalid ID + @retval EFI_OUT_OF_RESOURCES no resource to get data or table + +**/ +EFI_STATUS +LegacyGetDataOrTable ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN EFI_GET_PLATFORM_INFO_MODE Id + ) +{ + VOID *Table; + UINT32 TablePtr; + UINTN TableSize; + UINTN Alignment; + UINTN Location; + EFI_STATUS Status; + EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform; + EFI_COMPATIBILITY16_TABLE *Legacy16Table; + EFI_IA32_REGISTER_SET Regs; + LEGACY_BIOS_INSTANCE *Private; + + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + + LegacyBiosPlatform = Private->LegacyBiosPlatform; + Legacy16Table = Private->Legacy16Table; + + // + // Phase 1 - get an address allocated in 16-bit code + // + while (TRUE) { + switch (Id) { + case EfiGetPlatformBinaryMpTable: + case EfiGetPlatformBinaryOemIntData: + case EfiGetPlatformBinaryOem32Data: + case EfiGetPlatformBinaryOem16Data: + { + Status = LegacyBiosPlatform->GetPlatformInfo ( + LegacyBiosPlatform, + Id, + (VOID *) &Table, + &TableSize, + &Location, + &Alignment, + 0, + 0 + ); + DEBUG ((EFI_D_INFO, "LegacyGetDataOrTable - ID: %x, %r\n", (UINTN)Id, Status)); + DEBUG ((EFI_D_INFO, " Table - %x, Size - %x, Location - %x, Alignment - %x\n", (UINTN)Table, (UINTN)TableSize, (UINTN)Location, (UINTN)Alignment)); + break; + } + + default: + { + return EFI_INVALID_PARAMETER; + } + } + + if (EFI_ERROR (Status)) { + return Status; + } + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + Regs.X.AX = Legacy16GetTableAddress; + Regs.X.CX = (UINT16) TableSize; + Regs.X.BX = (UINT16) Location; + Regs.X.DX = (UINT16) Alignment; + Private->LegacyBios.FarCall86 ( + This, + Private->Legacy16CallSegment, + Private->Legacy16CallOffset, + &Regs, + NULL, + 0 + ); + + if (Regs.X.AX != 0) { + DEBUG ((EFI_D_ERROR, "Table ID %x length insufficient\n", Id)); + return EFI_OUT_OF_RESOURCES; + } else { + break; + } + } + // + // Phase 2 Call routine second time with address to allow address adjustment + // + Status = LegacyBiosPlatform->GetPlatformInfo ( + LegacyBiosPlatform, + Id, + (VOID *) &Table, + &TableSize, + &Location, + &Alignment, + Regs.X.DS, + Regs.X.BX + ); + switch (Id) { + case EfiGetPlatformBinaryMpTable: + { + Legacy16Table->MpTablePtr = (UINT32) (Regs.X.DS * 16 + Regs.X.BX); + Legacy16Table->MpTableLength = (UINT32)TableSize; + DEBUG ((EFI_D_INFO, "MP table in legacy region - %x\n", (UINTN)Legacy16Table->MpTablePtr)); + break; + } + + case EfiGetPlatformBinaryOemIntData: + { + + Legacy16Table->OemIntSegment = Regs.X.DS; + Legacy16Table->OemIntOffset = Regs.X.BX; + DEBUG ((EFI_D_INFO, "OemInt table in legacy region - %04x:%04x\n", (UINTN)Legacy16Table->OemIntSegment, (UINTN)Legacy16Table->OemIntOffset)); + break; + } + + case EfiGetPlatformBinaryOem32Data: + { + Legacy16Table->Oem32Segment = Regs.X.DS; + Legacy16Table->Oem32Offset = Regs.X.BX; + DEBUG ((EFI_D_INFO, "Oem32 table in legacy region - %04x:%04x\n", (UINTN)Legacy16Table->Oem32Segment, (UINTN)Legacy16Table->Oem32Offset)); + break; + } + + case EfiGetPlatformBinaryOem16Data: + { + // + // Legacy16Table->Oem16Segment = Regs.X.DS; + // Legacy16Table->Oem16Offset = Regs.X.BX; + DEBUG ((EFI_D_INFO, "Oem16 table in legacy region - %04x:%04x\n", (UINTN)Legacy16Table->Oem16Segment, (UINTN)Legacy16Table->Oem16Offset)); + break; + } + + default: + { + return EFI_INVALID_PARAMETER; + } + } + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Phase 3 Copy table to final location + // + TablePtr = (UINT32) (Regs.X.DS * 16 + Regs.X.BX); + + CopyMem ( + (VOID *) (UINTN)TablePtr, + Table, + TableSize + ); + + return EFI_SUCCESS; +} + + +/** + Assign drive number to legacy HDD drives prior to booting an EFI + aware OS so the OS can access drives without an EFI driver. + Note: BBS compliant drives ARE NOT available until this call by + either shell or EFI. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS Drive numbers assigned + +**/ +EFI_STATUS +GenericLegacyBoot ( + IN EFI_LEGACY_BIOS_PROTOCOL *This + ) +{ + EFI_STATUS Status; + LEGACY_BIOS_INSTANCE *Private; + EFI_IA32_REGISTER_SET Regs; + EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable; + EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform; + UINTN CopySize; + VOID *AcpiPtr; + HDD_INFO *HddInfo; + HDD_INFO *LocalHddInfo; + UINTN Index; + EFI_COMPATIBILITY16_TABLE *Legacy16Table; + UINT32 *BdaPtr; + UINT16 HddCount; + UINT16 BbsCount; + BBS_TABLE *LocalBbsTable; + UINT32 *BaseVectorMaster; + EFI_TIME BootTime; + UINT32 LocalTime; + EFI_HANDLE IdeController; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + VOID *SmbiosTable; + VOID *AcpiTable; + UINTN ShadowAddress; + UINT32 Granularity; + EFI_TIMER_ARCH_PROTOCOL *Timer; + UINT64 TimerPeriod; + + LocalHddInfo = NULL; + HddCount = 0; + BbsCount = 0; + LocalBbsTable = NULL; + TimerPeriod = 0; + + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + DEBUG_CODE ( + DEBUG ((EFI_D_ERROR, "Start of legacy boot\n")); + ); + + Legacy16Table = Private->Legacy16Table; + EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable; + HddInfo = &EfiToLegacy16BootTable->HddInfo[0]; + + LegacyBiosPlatform = Private->LegacyBiosPlatform; + + EfiToLegacy16BootTable->MajorVersion = EFI_TO_LEGACY_MAJOR_VERSION; + EfiToLegacy16BootTable->MinorVersion = EFI_TO_LEGACY_MINOR_VERSION; + + // + // Before starting the Legacy boot check the system ticker. + // + Status = gBS->LocateProtocol ( + &gEfiTimerArchProtocolGuid, + NULL, + (VOID **) &Timer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Timer->GetTimerPeriod ( + Timer, + &TimerPeriod + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (TimerPeriod != DEFAULT_LAGACY_TIMER_TICK_DURATION) { + Status = Timer->SetTimerPeriod ( + Timer, + DEFAULT_LAGACY_TIMER_TICK_DURATION + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // If booting to a legacy OS then force HDD drives to the appropriate + // boot mode by calling GetIdeHandle. + // A reconnect -r can force all HDDs back to native mode. + // + IdeController = NULL; + if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) { + Status = LegacyBiosPlatform->GetPlatformHandle ( + Private->LegacyBiosPlatform, + EfiGetPlatformIdeHandle, + 0, + &HandleBuffer, + &HandleCount, + NULL + ); + if (!EFI_ERROR (Status)) { + IdeController = HandleBuffer[0]; + } + } + // + // Unlock the Legacy BIOS region + // + Private->LegacyRegion->UnLock ( + Private->LegacyRegion, + 0xE0000, + 0x20000, + &Granularity + ); + + // + // Reconstruct the Legacy16 boot memory map + // + LegacyBiosBuildE820 (Private, &CopySize); + if (CopySize > Private->Legacy16Table->E820Length) { + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + Regs.X.AX = Legacy16GetTableAddress; + Regs.X.CX = (UINT16) CopySize; + Private->LegacyBios.FarCall86 ( + &Private->LegacyBios, + Private->Legacy16Table->Compatibility16CallSegment, + Private->Legacy16Table->Compatibility16CallOffset, + &Regs, + NULL, + 0 + ); + + Private->Legacy16Table->E820Pointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX); + Private->Legacy16Table->E820Length = (UINT32) CopySize; + if (Regs.X.AX != 0) { + DEBUG ((EFI_D_ERROR, "Legacy16 E820 length insufficient\n")); + } else { + CopyMem ( + (VOID *)(UINTN) Private->Legacy16Table->E820Pointer, + Private->E820Table, + CopySize + ); + } + } else { + CopyMem ( + (VOID *)(UINTN) Private->Legacy16Table->E820Pointer, + Private->E820Table, + CopySize + ); + Private->Legacy16Table->E820Length = (UINT32) CopySize; + } + // + // Get SMBIOS and ACPI table pointers + // + SmbiosTable = NULL; + EfiGetSystemConfigurationTable ( + &gEfiSmbiosTableGuid, + &SmbiosTable + ); + // + // We do not ASSERT if SmbiosTable not found. It is possbile that a platform does not produce SmbiosTable. + // + if (SmbiosTable == NULL) { + DEBUG ((EFI_D_INFO, "Smbios table is not found!\n")); + } + EfiToLegacy16BootTable->SmbiosTable = (UINT32)(UINTN)SmbiosTable; + + AcpiTable = NULL; + Status = EfiGetSystemConfigurationTable ( + &gEfiAcpi20TableGuid, + &AcpiTable + ); + if (EFI_ERROR (Status)) { + Status = EfiGetSystemConfigurationTable ( + &gEfiAcpi10TableGuid, + &AcpiTable + ); + } + // + // We do not ASSERT if AcpiTable not found. It is possbile that a platform does not produce AcpiTable. + // + if (AcpiTable == NULL) { + DEBUG ((EFI_D_INFO, "ACPI table is not found!\n")); + } + EfiToLegacy16BootTable->AcpiTable = (UINT32)(UINTN)AcpiTable; + + // + // Get RSD Ptr table rev at offset 15 decimal + // Rev = 0 Length is 20 decimal + // Rev != 0 Length is UINT32 at offset 20 decimal + // + if (AcpiTable != NULL) { + + AcpiPtr = AcpiTable; + if (*((UINT8 *) AcpiPtr + 15) == 0) { + CopySize = 20; + } else { + AcpiPtr = ((UINT8 *) AcpiPtr + 20); + CopySize = (*(UINT32 *) AcpiPtr); + } + + CopyMem ( + (VOID *)(UINTN) Private->Legacy16Table->AcpiRsdPtrPointer, + AcpiTable, + CopySize + ); + } + // + // Make sure all PCI Interrupt Line register are programmed to match 8259 + // + PciProgramAllInterruptLineRegisters (Private); + + // + // Unlock the Legacy BIOS region as PciProgramAllInterruptLineRegisters + // can lock it. + // + Private->LegacyRegion->UnLock ( + Private->LegacyRegion, + Private->BiosStart, + Private->LegacyBiosImageSize, + &Granularity + ); + + // + // Configure Legacy Device Magic + // + // Only do this code if booting legacy OS + // + if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) { + UpdateSioData (Private); + } + // + // Setup BDA and EBDA standard areas before Legacy Boot + // + LegacyBiosCompleteBdaBeforeBoot (Private); + LegacyBiosCompleteStandardCmosBeforeBoot (Private); + + // + // We must build IDE data, if it hasn't been done, before PciShadowRoms + // to insure EFI drivers are connected. + // + LegacyBiosBuildIdeData (Private, &HddInfo, 1); + UpdateAllIdentifyDriveData (Private); + + // + // Clear IO BAR, if IDE controller in legacy mode. + // + InitLegacyIdeController (IdeController); + + // + // Generate number of ticks since midnight for BDA. DOS requires this + // for its time. We have to make assumptions as to how long following + // code takes since after PciShadowRoms PciIo is gone. Place result in + // 40:6C-6F + // + // Adjust value by 1 second. + // + gRT->GetTime (&BootTime, NULL); + LocalTime = BootTime.Hour * 3600 + BootTime.Minute * 60 + BootTime.Second; + LocalTime += 1; + + // + // Multiply result by 18.2 for number of ticks since midnight. + // Use 182/10 to avoid floating point math. + // + LocalTime = (LocalTime * 182) / 10; + BdaPtr = (UINT32 *) (UINTN)0x46C; + *BdaPtr = LocalTime; + + // + // Shadow PCI ROMs. We must do this near the end since this will kick + // of Native EFI drivers that may be needed to collect info for Legacy16 + // + // WARNING: PciIo is gone after this call. + // + PciShadowRoms (Private); + + // + // Shadow PXE base code, BIS etc. + // + Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xc0000, 0x40000, &Granularity); + ShadowAddress = Private->OptionRom; + Private->LegacyBiosPlatform->PlatformHooks ( + Private->LegacyBiosPlatform, + EfiPlatformHookShadowServiceRoms, + 0, + 0, + &ShadowAddress, + Legacy16Table, + NULL + ); + Private->OptionRom = (UINT32)ShadowAddress; + // + // Register Legacy SMI Handler + // + LegacyBiosPlatform->SmmInit ( + LegacyBiosPlatform, + EfiToLegacy16BootTable + ); + + // + // Let platform code know the boot options + // + LegacyBiosGetBbsInfo ( + This, + &HddCount, + &LocalHddInfo, + &BbsCount, + &LocalBbsTable + ); + + PrintBbsTable (LocalBbsTable); + PrintHddInfo (LocalHddInfo); + + // + // If drive wasn't spun up then BuildIdeData may have found new drives. + // Need to update BBS boot priority. + // + for (Index = 0; Index < MAX_IDE_CONTROLLER; Index++) { + if ((LocalHddInfo[Index].IdentifyDrive[0].Raw[0] != 0) && + (LocalBbsTable[2 * Index + 1].BootPriority == BBS_IGNORE_ENTRY) + ) { + LocalBbsTable[2 * Index + 1].BootPriority = BBS_UNPRIORITIZED_ENTRY; + } + + if ((LocalHddInfo[Index].IdentifyDrive[1].Raw[0] != 0) && + (LocalBbsTable[2 * Index + 2].BootPriority == BBS_IGNORE_ENTRY) + ) { + LocalBbsTable[2 * Index + 2].BootPriority = BBS_UNPRIORITIZED_ENTRY; + } + } + + Private->LegacyRegion->UnLock ( + Private->LegacyRegion, + 0xc0000, + 0x40000, + &Granularity + ); + + LegacyBiosPlatform->PrepareToBoot ( + LegacyBiosPlatform, + mBbsDevicePathPtr, + mBbsTable, + mLoadOptionsSize, + mLoadOptions, + (VOID *) &Private->IntThunk->EfiToLegacy16BootTable + ); + + // + // If no boot device return to BDS + // + if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) { + for (Index = 0; Index < BbsCount; Index++){ + if ((LocalBbsTable[Index].BootPriority != BBS_DO_NOT_BOOT_FROM) && + (LocalBbsTable[Index].BootPriority != BBS_UNPRIORITIZED_ENTRY) && + (LocalBbsTable[Index].BootPriority != BBS_IGNORE_ENTRY)) { + break; + } + } + if (Index == BbsCount) { + return EFI_DEVICE_ERROR; + } + } + // + // Let the Legacy16 code know the device path type for legacy boot + // + EfiToLegacy16BootTable->DevicePathType = mBbsDevicePathPtr->DeviceType; + + // + // Copy MP table, if it exists. + // + LegacyGetDataOrTable (This, EfiGetPlatformBinaryMpTable); + + if (!Private->LegacyBootEntered) { + // + // Copy OEM INT Data, if it exists. Note: This code treats any data + // as a bag of bits and knows nothing of the contents nor cares. + // Contents are IBV specific. + // + LegacyGetDataOrTable (This, EfiGetPlatformBinaryOemIntData); + + // + // Copy OEM16 Data, if it exists.Note: This code treats any data + // as a bag of bits and knows nothing of the contents nor cares. + // Contents are IBV specific. + // + LegacyGetDataOrTable (This, EfiGetPlatformBinaryOem16Data); + + // + // Copy OEM32 Data, if it exists.Note: This code treats any data + // as a bag of bits and knows nothing of the contents nor cares. + // Contents are IBV specific. + // + LegacyGetDataOrTable (This, EfiGetPlatformBinaryOem32Data); + } + + // + // Call into Legacy16 code to prepare for INT 19h + // + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + Regs.X.AX = Legacy16PrepareToBoot; + + // + // Pass in handoff data + // + Regs.X.ES = EFI_SEGMENT ((UINTN)EfiToLegacy16BootTable); + Regs.X.BX = EFI_OFFSET ((UINTN)EfiToLegacy16BootTable); + + Private->LegacyBios.FarCall86 ( + This, + Private->Legacy16CallSegment, + Private->Legacy16CallOffset, + &Regs, + NULL, + 0 + ); + + if (Regs.X.AX != 0) { + return EFI_DEVICE_ERROR; + } + // + // Lock the Legacy BIOS region + // + Private->LegacyRegion->Lock ( + Private->LegacyRegion, + 0xc0000, + 0x40000, + &Granularity + ); + // + // Lock attributes of the Legacy Region if chipset supports + // + Private->LegacyRegion->BootLock ( + Private->LegacyRegion, + 0xc0000, + 0x40000, + &Granularity + ); + + // + // Call into Legacy16 code to do the INT 19h + // + EnableAllControllers (Private); + if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) { + // + // Signal all the events that are waiting on EVT_SIGNAL_LEGACY_BOOT + // + EfiSignalEventLegacyBoot (); + DEBUG ((EFI_D_INFO, "Legacy INT19 Boot...\n")); + // + // Raise TPL to high level to disable CPU interrupts + // + gBS->RaiseTPL (TPL_HIGH_LEVEL); + + // + // Put the 8259 into its legacy mode by reprogramming the vector bases + // + Private->Legacy8259->SetVectorBase (Private->Legacy8259, LEGACY_MODE_BASE_VECTOR_MASTER, LEGACY_MODE_BASE_VECTOR_SLAVE); + // + // PC History + // The original PC used INT8-F for master PIC. Since these mapped over + // processor exceptions TIANO moved the master PIC to INT68-6F. + // We need to set these back to the Legacy16 unexpected interrupt(saved + // in LegacyBios.c) since some OS see that these have values different from + // what is expected and invoke them. Since the legacy OS corrupts EFI + // memory, there is no handler for these interrupts and OS blows up. + // + // We need to save the TIANO values for the rare case that the Legacy16 + // code cannot boot but knows memory hasn't been destroyed. + // + // To compound the problem, video takes over one of these INTS and must be + // be left. + // @bug - determine if video hooks INT(in which case we must find new + // set of TIANO vectors) or takes it over. + // + // + BaseVectorMaster = (UINT32 *) (sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER); + for (Index = 0; Index < 8; Index++) { + Private->ThunkSavedInt[Index] = BaseVectorMaster[Index]; + if (Private->ThunkSeg == (UINT16) (BaseVectorMaster[Index] >> 16)) { + BaseVectorMaster[Index] = (UINT32) (Private->BiosUnexpectedInt); + } + } + + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + Regs.X.AX = Legacy16Boot; + + Private->LegacyBios.FarCall86 ( + This, + Private->Legacy16CallSegment, + Private->Legacy16CallOffset, + &Regs, + NULL, + 0 + ); + + BaseVectorMaster = (UINT32 *) (sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER); + for (Index = 0; Index < 8; Index++) { + BaseVectorMaster[Index] = Private->ThunkSavedInt[Index]; + } + } + Private->LegacyBootEntered = TRUE; + if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) { + // + // Should never return unless never passed control to 0:7c00(first stage + // OS loader) and only then if no bootable device found. + // + return EFI_DEVICE_ERROR; + } else { + // + // If boot to EFI then expect to return to caller + // + return EFI_SUCCESS; + } +} + + +/** + Assign drive number to legacy HDD drives prior to booting an EFI + aware OS so the OS can access drives without an EFI driver. + Note: BBS compliant drives ARE NOT available until this call by + either shell or EFI. + + @param This Protocol instance pointer. + @param BbsCount Number of BBS_TABLE structures + @param BbsTable List BBS entries + + @retval EFI_SUCCESS Drive numbers assigned + +**/ +EFI_STATUS +EFIAPI +LegacyBiosPrepareToBootEfi ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + OUT UINT16 *BbsCount, + OUT BBS_TABLE **BbsTable + ) +{ + EFI_STATUS Status; + EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable; + LEGACY_BIOS_INSTANCE *Private; + + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable; + mBootMode = BOOT_EFI_OS; + mBbsDevicePathPtr = NULL; + Status = GenericLegacyBoot (This); + *BbsTable = (BBS_TABLE*)(UINTN)EfiToLegacy16BootTable->BbsTable; + *BbsCount = (UINT16) (sizeof (Private->IntThunk->BbsTable) / sizeof (BBS_TABLE)); + return Status; +} + +/** + To boot from an unconventional device like parties and/or execute HDD diagnostics. + + @param This Protocol instance pointer. + @param Attributes How to interpret the other input parameters + @param BbsEntry The 0-based index into the BbsTable for the parent + device. + @param BeerData Pointer to the 128 bytes of ram BEER data. + @param ServiceAreaData Pointer to the 64 bytes of raw Service Area data. The + caller must provide a pointer to the specific Service + Area and not the start all Service Areas. + + @retval EFI_INVALID_PARAMETER if error. Does NOT return if no error. + +***/ +EFI_STATUS +EFIAPI +LegacyBiosBootUnconventionalDevice ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UDC_ATTRIBUTES Attributes, + IN UINTN BbsEntry, + IN VOID *BeerData, + IN VOID *ServiceAreaData + ) +{ + EFI_STATUS Status; + EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable; + LEGACY_BIOS_INSTANCE *Private; + UD_TABLE *UcdTable; + UINTN Index; + UINT16 BootPriority; + BBS_TABLE *BbsTable; + + BootPriority = 0; + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + mBootMode = BOOT_UNCONVENTIONAL_DEVICE; + mBbsDevicePathPtr = &mBbsDevicePathNode; + mAttributes = Attributes; + mBbsEntry = BbsEntry; + mBeerData = BeerData, mServiceAreaData = ServiceAreaData; + + EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable; + + // + // Do input parameter checking + // + if ((Attributes.DirectoryServiceValidity == 0) && + (Attributes.RabcaUsedFlag == 0) && + (Attributes.ExecuteHddDiagnosticsFlag == 0) + ) { + return EFI_INVALID_PARAMETER; + } + + if (((Attributes.DirectoryServiceValidity != 0) && (ServiceAreaData == NULL)) || + (((Attributes.DirectoryServiceValidity | Attributes.RabcaUsedFlag) != 0) && (BeerData == NULL)) + ) { + return EFI_INVALID_PARAMETER; + } + + UcdTable = (UD_TABLE *) AllocatePool ( + sizeof (UD_TABLE) + ); + if (NULL == UcdTable) { + return EFI_OUT_OF_RESOURCES; + } + + EfiToLegacy16BootTable->UnconventionalDeviceTable = (UINT32)(UINTN)UcdTable; + UcdTable->Attributes = Attributes; + UcdTable->BbsTableEntryNumberForParentDevice = (UINT8) BbsEntry; + // + // Force all existing BBS entries to DoNotBoot. This allows 16-bit CSM + // to assign drive numbers but bot boot from. Only newly created entries + // will be valid. + // + BbsTable = (BBS_TABLE*)(UINTN)EfiToLegacy16BootTable->BbsTable; + for (Index = 0; Index < EfiToLegacy16BootTable->NumberBbsEntries; Index++) { + BbsTable[Index].BootPriority = BBS_DO_NOT_BOOT_FROM; + } + // + // If parent is onboard IDE then assign controller & device number + // else they are 0. + // + if (BbsEntry < MAX_IDE_CONTROLLER * 2) { + UcdTable->DeviceNumber = (UINT8) ((BbsEntry - 1) % 2); + } + + if (BeerData != NULL) { + CopyMem ( + (VOID *) UcdTable->BeerData, + BeerData, + (UINTN) 128 + ); + } + + if (ServiceAreaData != NULL) { + CopyMem ( + (VOID *) UcdTable->ServiceAreaData, + ServiceAreaData, + (UINTN) 64 + ); + } + // + // For each new entry do the following: + // 1. Increment current number of BBS entries + // 2. Copy parent entry to new entry. + // 3. Zero out BootHandler Offset & segment + // 4. Set appropriate device type. BEV(0x80) for HDD diagnostics + // and Floppy(0x01) for PARTIES boot. + // 5. Assign new priority. + // + if ((Attributes.ExecuteHddDiagnosticsFlag) != 0) { + EfiToLegacy16BootTable->NumberBbsEntries += 1; + + CopyMem ( + (VOID *) &BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority, + (VOID *) &BbsTable[BbsEntry].BootPriority, + sizeof (BBS_TABLE) + ); + + BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerOffset = 0; + BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerSegment = 0; + BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].DeviceType = 0x80; + + UcdTable->BbsTableEntryNumberForHddDiag = (UINT8) (EfiToLegacy16BootTable->NumberBbsEntries - 1); + + BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority = BootPriority; + BootPriority += 1; + + // + // Set device type as BBS_TYPE_DEV for PARTIES diagnostic + // + mBbsDevicePathNode.DeviceType = BBS_TYPE_BEV; + } + + if (((Attributes.DirectoryServiceValidity | Attributes.RabcaUsedFlag)) != 0) { + EfiToLegacy16BootTable->NumberBbsEntries += 1; + CopyMem ( + (VOID *) &BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority, + (VOID *) &BbsTable[BbsEntry].BootPriority, + sizeof (BBS_TABLE) + ); + + BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerOffset = 0; + BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerSegment = 0; + BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].DeviceType = 0x01; + UcdTable->BbsTableEntryNumberForBoot = (UINT8) (EfiToLegacy16BootTable->NumberBbsEntries - 1); + BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority = BootPriority; + + // + // Set device type as BBS_TYPE_FLOPPY for PARTIES boot as floppy + // + mBbsDevicePathNode.DeviceType = BBS_TYPE_FLOPPY; + } + // + // Build the BBS Device Path for this boot selection + // + mBbsDevicePathNode.Header.Type = BBS_DEVICE_PATH; + mBbsDevicePathNode.Header.SubType = BBS_BBS_DP; + SetDevicePathNodeLength (&mBbsDevicePathNode.Header, sizeof (BBS_BBS_DEVICE_PATH)); + mBbsDevicePathNode.StatusFlag = 0; + mBbsDevicePathNode.String[0] = 0; + + Status = GenericLegacyBoot (This); + return Status; +} + +/** + Attempt to legacy boot the BootOption. If the EFI contexted has been + compromised this function will not return. + + @param This Protocol instance pointer. + @param BbsDevicePath EFI Device Path from BootXXXX variable. + @param LoadOptionsSize Size of LoadOption in size. + @param LoadOptions LoadOption from BootXXXX variable + + @retval EFI_SUCCESS Removable media not present + +**/ +EFI_STATUS +EFIAPI +LegacyBiosLegacyBoot ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN BBS_BBS_DEVICE_PATH *BbsDevicePath, + IN UINT32 LoadOptionsSize, + IN VOID *LoadOptions + ) +{ + EFI_STATUS Status; + + mBbsDevicePathPtr = BbsDevicePath; + mLoadOptionsSize = LoadOptionsSize; + mLoadOptions = LoadOptions; + mBootMode = BOOT_LEGACY_OS; + Status = GenericLegacyBoot (This); + + return Status; +} + +/** + Convert EFI Memory Type to E820 Memory Type. + + @param Type EFI Memory Type + + @return ACPI Memory Type for EFI Memory Type + +**/ +EFI_ACPI_MEMORY_TYPE +EfiMemoryTypeToE820Type ( + IN UINT32 Type + ) +{ + switch (Type) { + case EfiLoaderCode: + case EfiLoaderData: + case EfiBootServicesCode: + case EfiBootServicesData: + case EfiConventionalMemory: + case EfiRuntimeServicesCode: + case EfiRuntimeServicesData: + return EfiAcpiAddressRangeMemory; + + case EfiACPIReclaimMemory: + return EfiAcpiAddressRangeACPI; + + case EfiACPIMemoryNVS: + return EfiAcpiAddressRangeNVS; + + // + // All other types map to reserved. + // Adding the code just waists FLASH space. + // + // case EfiReservedMemoryType: + // case EfiUnusableMemory: + // case EfiMemoryMappedIO: + // case EfiMemoryMappedIOPortSpace: + // case EfiPalCode: + // + default: + return EfiAcpiAddressRangeReserved; + } +} + +/** + Build the E820 table. + + @param Private Legacy BIOS Instance data + @param Size Size of E820 Table + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosBuildE820 ( + IN LEGACY_BIOS_INSTANCE *Private, + OUT UINTN *Size + ) +{ + EFI_STATUS Status; + EFI_E820_ENTRY64 *E820Table; + EFI_MEMORY_DESCRIPTOR *EfiMemoryMap; + EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEnd; + EFI_MEMORY_DESCRIPTOR *EfiEntry; + EFI_MEMORY_DESCRIPTOR *NextEfiEntry; + EFI_MEMORY_DESCRIPTOR TempEfiEntry; + UINTN EfiMemoryMapSize; + UINTN EfiMapKey; + UINTN EfiDescriptorSize; + UINT32 EfiDescriptorVersion; + UINTN Index; + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob; + UINTN TempIndex; + UINTN IndexSort; + UINTN TempNextIndex; + EFI_E820_ENTRY64 TempE820; + EFI_ACPI_MEMORY_TYPE TempType; + BOOLEAN ChangedFlag; + UINTN Above1MIndex; + UINT64 MemoryBlockLength; + + E820Table = (EFI_E820_ENTRY64 *) Private->E820Table; + + // + // Get the EFI memory map. + // + EfiMemoryMapSize = 0; + EfiMemoryMap = NULL; + Status = gBS->GetMemoryMap ( + &EfiMemoryMapSize, + EfiMemoryMap, + &EfiMapKey, + &EfiDescriptorSize, + &EfiDescriptorVersion + ); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + do { + // + // Use size returned back plus 1 descriptor for the AllocatePool. + // We don't just multiply by 2 since the "for" loop below terminates on + // EfiMemoryMapEnd which is dependent upon EfiMemoryMapSize. Otherwize + // we process bogus entries and create bogus E820 entries. + // + EfiMemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (EfiMemoryMapSize); + ASSERT (EfiMemoryMap != NULL); + Status = gBS->GetMemoryMap ( + &EfiMemoryMapSize, + EfiMemoryMap, + &EfiMapKey, + &EfiDescriptorSize, + &EfiDescriptorVersion + ); + if (EFI_ERROR (Status)) { + FreePool (EfiMemoryMap); + } + } while (Status == EFI_BUFFER_TOO_SMALL); + + ASSERT_EFI_ERROR (Status); + + // + // Punch in the E820 table for memory less than 1 MB. + // Assume ZeroMem () has been done on data structure. + // + // + // First entry is 0 to (640k - EBDA) + // + E820Table[0].BaseAddr = 0; + E820Table[0].Length = (UINT64) ((*(UINT16 *) (UINTN)0x40E) << 4); + E820Table[0].Type = EfiAcpiAddressRangeMemory; + + // + // Second entry is (640k - EBDA) to 640k + // + E820Table[1].BaseAddr = E820Table[0].Length; + E820Table[1].Length = (UINT64) ((640 * 1024) - E820Table[0].Length); + E820Table[1].Type = EfiAcpiAddressRangeReserved; + + // + // Third Entry is legacy BIOS + // DO NOT CLAIM region from 0xA0000-0xDFFFF. OS can use free areas + // to page in memory under 1MB. + // Omit region from 0xE0000 to start of BIOS, if any. This can be + // used for a multiple reasons including OPROMS. + // + + // + // The CSM binary image size is not the actually size that CSM binary used, + // to avoid memory corrupt, we declare the 0E0000 - 0FFFFF is used by CSM binary. + // + E820Table[2].BaseAddr = 0xE0000; + E820Table[2].Length = 0x20000; + E820Table[2].Type = EfiAcpiAddressRangeReserved; + + Above1MIndex = 2; + + // + // Process the EFI map to produce E820 map; + // + + // + // Sort memory map from low to high + // + EfiEntry = EfiMemoryMap; + NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize); + EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize); + while (EfiEntry < EfiMemoryMapEnd) { + while (NextEfiEntry < EfiMemoryMapEnd) { + if (EfiEntry->PhysicalStart > NextEfiEntry->PhysicalStart) { + CopyMem (&TempEfiEntry, EfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR)); + CopyMem (EfiEntry, NextEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR)); + CopyMem (NextEfiEntry, &TempEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR)); + } + + NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (NextEfiEntry, EfiDescriptorSize); + } + + EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize); + NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize); + } + + EfiEntry = EfiMemoryMap; + EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize); + for (Index = Above1MIndex; (EfiEntry < EfiMemoryMapEnd) && (Index < EFI_MAX_E820_ENTRY - 1); ) { + MemoryBlockLength = (UINT64) (LShiftU64 (EfiEntry->NumberOfPages, 12)); + if ((EfiEntry->PhysicalStart + MemoryBlockLength) < 0x100000) { + // + // Skip the memory block is under 1MB + // + } else { + if (EfiEntry->PhysicalStart < 0x100000) { + // + // When the memory block spans below 1MB, ensure the memory block start address is at least 1MB + // + MemoryBlockLength -= 0x100000 - EfiEntry->PhysicalStart; + EfiEntry->PhysicalStart = 0x100000; + } + + // + // Convert memory type to E820 type + // + TempType = EfiMemoryTypeToE820Type (EfiEntry->Type); + + if ((E820Table[Index].Type == TempType) && (EfiEntry->PhysicalStart == (E820Table[Index].BaseAddr + E820Table[Index].Length))) { + // + // Grow an existing entry + // + E820Table[Index].Length += MemoryBlockLength; + } else { + // + // Make a new entry + // + ++Index; + E820Table[Index].BaseAddr = EfiEntry->PhysicalStart; + E820Table[Index].Length = MemoryBlockLength; + E820Table[Index].Type = TempType; + } + } + EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize); + } + + FreePool (EfiMemoryMap); + + // + // Process the reserved memory map to produce E820 map ; + // + for (Hob.Raw = GetHobList (); !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { + if (Hob.Raw != NULL && GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { + ResourceHob = Hob.ResourceDescriptor; + if (((ResourceHob->ResourceType == EFI_RESOURCE_MEMORY_MAPPED_IO) || + (ResourceHob->ResourceType == EFI_RESOURCE_FIRMWARE_DEVICE) || + (ResourceHob->ResourceType == EFI_RESOURCE_MEMORY_RESERVED) ) && + (ResourceHob->PhysicalStart > 0x100000) && + (Index < EFI_MAX_E820_ENTRY - 1)) { + ++Index; + E820Table[Index].BaseAddr = ResourceHob->PhysicalStart; + E820Table[Index].Length = ResourceHob->ResourceLength; + E820Table[Index].Type = EfiAcpiAddressRangeReserved; + } + } + } + + Index ++; + Private->IntThunk->EfiToLegacy16InitTable.NumberE820Entries = (UINT32)Index; + Private->IntThunk->EfiToLegacy16BootTable.NumberE820Entries = (UINT32)Index; + Private->NumberE820Entries = (UINT32)Index; + *Size = (UINTN) (Index * sizeof (EFI_E820_ENTRY64)); + + // + // Sort E820Table from low to high + // + for (TempIndex = 0; TempIndex < Index; TempIndex++) { + ChangedFlag = FALSE; + for (TempNextIndex = 1; TempNextIndex < Index - TempIndex; TempNextIndex++) { + if (E820Table[TempNextIndex - 1].BaseAddr > E820Table[TempNextIndex].BaseAddr) { + ChangedFlag = TRUE; + TempE820.BaseAddr = E820Table[TempNextIndex - 1].BaseAddr; + TempE820.Length = E820Table[TempNextIndex - 1].Length; + TempE820.Type = E820Table[TempNextIndex - 1].Type; + + E820Table[TempNextIndex - 1].BaseAddr = E820Table[TempNextIndex].BaseAddr; + E820Table[TempNextIndex - 1].Length = E820Table[TempNextIndex].Length; + E820Table[TempNextIndex - 1].Type = E820Table[TempNextIndex].Type; + + E820Table[TempNextIndex].BaseAddr = TempE820.BaseAddr; + E820Table[TempNextIndex].Length = TempE820.Length; + E820Table[TempNextIndex].Type = TempE820.Type; + } + } + + if (!ChangedFlag) { + break; + } + } + + // + // Remove the overlap range + // + for (TempIndex = 1; TempIndex < Index; TempIndex++) { + if (E820Table[TempIndex - 1].BaseAddr <= E820Table[TempIndex].BaseAddr && + ((E820Table[TempIndex - 1].BaseAddr + E820Table[TempIndex - 1].Length) >= + (E820Table[TempIndex].BaseAddr +E820Table[TempIndex].Length))) { + // + //Overlap range is found + // + ASSERT (E820Table[TempIndex - 1].Type == E820Table[TempIndex].Type); + + if (TempIndex == Index - 1) { + E820Table[TempIndex].BaseAddr = 0; + E820Table[TempIndex].Length = 0; + E820Table[TempIndex].Type = (EFI_ACPI_MEMORY_TYPE) 0; + Index--; + break; + } else { + for (IndexSort = TempIndex; IndexSort < Index - 1; IndexSort ++) { + E820Table[IndexSort].BaseAddr = E820Table[IndexSort + 1].BaseAddr; + E820Table[IndexSort].Length = E820Table[IndexSort + 1].Length; + E820Table[IndexSort].Type = E820Table[IndexSort + 1].Type; + } + Index--; + } + } + } + + + + Private->IntThunk->EfiToLegacy16InitTable.NumberE820Entries = (UINT32)Index; + Private->IntThunk->EfiToLegacy16BootTable.NumberE820Entries = (UINT32)Index; + Private->NumberE820Entries = (UINT32)Index; + *Size = (UINTN) (Index * sizeof (EFI_E820_ENTRY64)); + + // + // Determine OS usable memory above 1Mb + // + Private->IntThunk->EfiToLegacy16BootTable.OsMemoryAbove1Mb = 0x0000; + for (TempIndex = Above1MIndex; TempIndex < Index; TempIndex++) { + if (E820Table[TempIndex].BaseAddr >= 0x100000 && E820Table[TempIndex].BaseAddr < 0x100000000ULL) { // not include above 4G memory + // + // ACPIReclaimMemory is also usable memory for ACPI OS, after OS dumps all ACPI tables. + // + if ((E820Table[TempIndex].Type == EfiAcpiAddressRangeMemory) || (E820Table[TempIndex].Type == EfiAcpiAddressRangeACPI)) { + Private->IntThunk->EfiToLegacy16BootTable.OsMemoryAbove1Mb += (UINT32) (E820Table[TempIndex].Length); + } else { + break; // break at first not normal memory, because SMM may use reserved memory. + } + } + } + + Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb = Private->IntThunk->EfiToLegacy16BootTable.OsMemoryAbove1Mb; + + // + // Print DEBUG information + // + for (TempIndex = 0; TempIndex < Index; TempIndex++) { + DEBUG((EFI_D_INFO, "E820[%2d]: 0x%16lx ---- 0x%16lx, Type = 0x%x \n", + TempIndex, + E820Table[TempIndex].BaseAddr, + (E820Table[TempIndex].BaseAddr + E820Table[TempIndex].Length), + E820Table[TempIndex].Type + )); + } + + return EFI_SUCCESS; +} + + +/** + Fill in the standard BDA and EBDA stuff prior to legacy Boot + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosCompleteBdaBeforeBoot ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + BDA_STRUC *Bda; + UINT16 MachineConfig; + DEVICE_PRODUCER_DATA_HEADER *SioPtr; + + Bda = (BDA_STRUC *) ((UINTN) 0x400); + MachineConfig = 0; + + SioPtr = &(Private->IntThunk->EfiToLegacy16BootTable.SioData); + Bda->Com1 = SioPtr->Serial[0].Address; + Bda->Com2 = SioPtr->Serial[1].Address; + Bda->Com3 = SioPtr->Serial[2].Address; + Bda->Com4 = SioPtr->Serial[3].Address; + + if (SioPtr->Serial[0].Address != 0x00) { + MachineConfig += 0x200; + } + + if (SioPtr->Serial[1].Address != 0x00) { + MachineConfig += 0x200; + } + + if (SioPtr->Serial[2].Address != 0x00) { + MachineConfig += 0x200; + } + + if (SioPtr->Serial[3].Address != 0x00) { + MachineConfig += 0x200; + } + + Bda->Lpt1 = SioPtr->Parallel[0].Address; + Bda->Lpt2 = SioPtr->Parallel[1].Address; + Bda->Lpt3 = SioPtr->Parallel[2].Address; + + if (SioPtr->Parallel[0].Address != 0x00) { + MachineConfig += 0x4000; + } + + if (SioPtr->Parallel[1].Address != 0x00) { + MachineConfig += 0x4000; + } + + if (SioPtr->Parallel[2].Address != 0x00) { + MachineConfig += 0x4000; + } + + Bda->NumberOfDrives = (UINT8) (Bda->NumberOfDrives + Private->IdeDriveCount); + if (SioPtr->Floppy.NumberOfFloppy != 0x00) { + MachineConfig = (UINT16) (MachineConfig + 0x01 + (SioPtr->Floppy.NumberOfFloppy - 1) * 0x40); + Bda->FloppyXRate = 0x07; + } + + Bda->Lpt1_2Timeout = 0x1414; + Bda->Lpt3_4Timeout = 0x1414; + Bda->Com1_2Timeout = 0x0101; + Bda->Com3_4Timeout = 0x0101; + + // + // Force VGA and Coprocessor, indicate 101/102 keyboard + // + MachineConfig = (UINT16) (MachineConfig + 0x00 + 0x02 + (SioPtr->MousePresent * 0x04)); + Bda->MachineConfig = MachineConfig; + + return EFI_SUCCESS; +} + +/** + Fill in the standard BDA for Keyboard LEDs + + @param This Protocol instance pointer. + @param Leds Current LED status + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +EFIAPI +LegacyBiosUpdateKeyboardLedStatus ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINT8 Leds + ) +{ + LEGACY_BIOS_INSTANCE *Private; + BDA_STRUC *Bda; + UINT8 LocalLeds; + EFI_IA32_REGISTER_SET Regs; + + Bda = (BDA_STRUC *) ((UINTN) 0x400); + + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + LocalLeds = Leds; + Bda->LedStatus = (UINT8) ((Bda->LedStatus &~0x07) | LocalLeds); + LocalLeds = (UINT8) (LocalLeds << 4); + Bda->ShiftStatus = (UINT8) ((Bda->ShiftStatus &~0x70) | LocalLeds); + LocalLeds = (UINT8) (Leds & 0x20); + Bda->KeyboardStatus = (UINT8) ((Bda->KeyboardStatus &~0x20) | LocalLeds); + // + // Call into Legacy16 code to allow it to do any processing + // + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + Regs.X.AX = Legacy16SetKeyboardLeds; + Regs.H.CL = Leds; + + Private->LegacyBios.FarCall86 ( + &Private->LegacyBios, + Private->Legacy16Table->Compatibility16CallSegment, + Private->Legacy16Table->Compatibility16CallOffset, + &Regs, + NULL, + 0 + ); + + return EFI_SUCCESS; +} + + +/** + Fill in the standard CMOS stuff prior to legacy Boot + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosCompleteStandardCmosBeforeBoot ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + UINT8 Bda; + UINT8 Floppy; + UINT32 Size; + + // + // Update CMOS locations + // 10 floppy + // 12,19,1A - ignore as OS don't use them and there is no standard due + // to large capacity drives + // CMOS 14 = BDA 40:10 plus bit 3(display enabled) + // + Bda = (UINT8)(*((UINT8 *)((UINTN)0x410)) | BIT3); + + // + // Force display enabled + // + Floppy = 0x00; + if ((Bda & BIT0) != 0) { + Floppy = BIT6; + } + + // + // Check if 2.88MB floppy set + // + if ((Bda & (BIT7 | BIT6)) != 0) { + Floppy = (UINT8)(Floppy | BIT1); + } + + LegacyWriteStandardCmos (CMOS_10, Floppy); + LegacyWriteStandardCmos (CMOS_14, Bda); + + // + // Force Status Register A to set rate selection bits and divider + // + LegacyWriteStandardCmos (CMOS_0A, 0x26); + + // + // redo memory size since it can change + // + Size = 15 * SIZE_1MB; + if (Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb < (15 * SIZE_1MB)) { + Size = Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb >> 10; + } + + LegacyWriteStandardCmos (CMOS_17, (UINT8)(Size & 0xFF)); + LegacyWriteStandardCmos (CMOS_30, (UINT8)(Size & 0xFF)); + LegacyWriteStandardCmos (CMOS_18, (UINT8)(Size >> 8)); + LegacyWriteStandardCmos (CMOS_31, (UINT8)(Size >> 8)); + + LegacyCalculateWriteStandardCmosChecksum (); + + return EFI_SUCCESS; +} + +/** + Relocate this image under 4G memory for IPF. + + @param ImageHandle Handle of driver image. + @param SystemTable Pointer to system table. + + @retval EFI_SUCCESS Image successfully relocated. + @retval EFI_ABORTED Failed to relocate image. + +**/ +EFI_STATUS +RelocateImageUnder4GIfNeeded ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EFI_SUCCESS; +} diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyCmos.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyCmos.c new file mode 100644 index 0000000000..0fbf902813 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyCmos.c @@ -0,0 +1,124 @@ +/** @file + This code fills in standard CMOS values and updates the standard CMOS + checksum. The Legacy16 code or LegacyBiosPlatform.c is responsible for + non-standard CMOS locations and non-standard checksums. + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "LegacyBiosInterface.h" + +/** + Read CMOS register through index/data port. + + @param[in] Index The index of the CMOS register to read. + + @return The data value from the CMOS register specified by Index. + +**/ +UINT8 +LegacyReadStandardCmos ( + IN UINT8 Index + ) +{ + IoWrite8 (PORT_70, Index); + return IoRead8 (PORT_71); +} + +/** + Write CMOS register through index/data port. + + @param[in] Index The index of the CMOS register to write. + @param[in] Value The value of CMOS register to write. + + @return The value written to the CMOS register specified by Index. + +**/ +UINT8 +LegacyWriteStandardCmos ( + IN UINT8 Index, + IN UINT8 Value + ) +{ + IoWrite8 (PORT_70, Index); + return IoWrite8 (PORT_71, Value); +} + +/** + Calculate the new standard CMOS checksum and write it. + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS Calculate 16-bit checksum successfully + +**/ +EFI_STATUS +LegacyCalculateWriteStandardCmosChecksum ( + VOID + ) +{ + UINT8 Register; + UINT16 Checksum; + + for (Checksum = 0, Register = 0x10; Register < 0x2e; Register++) { + Checksum = (UINT16)(Checksum + LegacyReadStandardCmos (Register)); + } + LegacyWriteStandardCmos (CMOS_2E, (UINT8)(Checksum >> 8)); + LegacyWriteStandardCmos (CMOS_2F, (UINT8)(Checksum & 0xff)); + return EFI_SUCCESS; +} + + +/** + Fill in the standard CMOS stuff before Legacy16 load + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosInitCmos ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + UINT32 Size; + + // + // Clear all errors except RTC lost power + // + LegacyWriteStandardCmos (CMOS_0E, (UINT8)(LegacyReadStandardCmos (CMOS_0E) & BIT7)); + + // + // Update CMOS locations 15,16,17,18,30,31 and 32 + // CMOS 16,15 = 640Kb = 0x280 + // CMOS 18,17 = 31,30 = 15Mb max in 1Kb increments =0x3C00 max + // CMOS 32 = 0x20 + // + LegacyWriteStandardCmos (CMOS_15, 0x80); + LegacyWriteStandardCmos (CMOS_16, 0x02); + + Size = 15 * SIZE_1MB; + if (Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb < (15 * SIZE_1MB)) { + Size = Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb >> 10; + } + + LegacyWriteStandardCmos (CMOS_17, (UINT8)(Size & 0xFF)); + LegacyWriteStandardCmos (CMOS_30, (UINT8)(Size & 0xFF)); + LegacyWriteStandardCmos (CMOS_18, (UINT8)(Size >> 8)); + LegacyWriteStandardCmos (CMOS_31, (UINT8)(Size >> 8)); + + LegacyCalculateWriteStandardCmosChecksum (); + + return EFI_SUCCESS; +} diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyIde.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyIde.c new file mode 100644 index 0000000000..4e52fe9c03 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyIde.c @@ -0,0 +1,300 @@ +/** @file + Collect IDE information from Native EFI Driver + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "LegacyBiosInterface.h" + +BOOLEAN mIdeDataBuiltFlag = FALSE; + +/** + Collect IDE Inquiry data from the IDE disks + + @param Private Legacy BIOS Instance data + @param HddInfo Hdd Information + @param Flag Reconnect IdeController or not + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosBuildIdeData ( + IN LEGACY_BIOS_INSTANCE *Private, + IN HDD_INFO **HddInfo, + IN UINT16 Flag + ) +{ + EFI_STATUS Status; + EFI_HANDLE IdeController; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + EFI_DISK_INFO_PROTOCOL *DiskInfo; + UINT32 IdeChannel; + UINT32 IdeDevice; + UINT32 Size; + UINT8 *InquiryData; + UINT32 InquiryDataSize; + HDD_INFO *LocalHddInfo; + UINT32 PciIndex; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePathNode; + PCI_DEVICE_PATH *PciDevicePath; + + // + // Only build data once + // We have a problem with GetBbsInfo in that it can be invoked two + // places. Once in BDS, when all EFI drivers are connected and once in + // LegacyBoot after all EFI drivers are disconnected causing this routine + // to hang. In LegacyBoot this function is also called before EFI drivers + // are disconnected. + // Cases covered + // GetBbsInfo invoked in BDS. Both invocations in LegacyBoot ignored. + // GetBbsInfo not invoked in BDS. First invocation of this function + // proceeds normally and second via GetBbsInfo ignored. + // + PciDevicePath = NULL; + LocalHddInfo = *HddInfo; + Status = Private->LegacyBiosPlatform->GetPlatformHandle ( + Private->LegacyBiosPlatform, + EfiGetPlatformIdeHandle, + 0, + &HandleBuffer, + &HandleCount, + (VOID *) &LocalHddInfo + ); + if (!EFI_ERROR (Status)) { + IdeController = HandleBuffer[0]; + // + // Force IDE drive spin up! + // + if (Flag != 0) { + gBS->DisconnectController ( + IdeController, + NULL, + NULL + ); + } + + gBS->ConnectController (IdeController, NULL, NULL, FALSE); + + // + // Do GetIdeHandle twice since disconnect/reconnect will switch to native mode + // And GetIdeHandle will switch to Legacy mode, if required. + // + Private->LegacyBiosPlatform->GetPlatformHandle ( + Private->LegacyBiosPlatform, + EfiGetPlatformIdeHandle, + 0, + &HandleBuffer, + &HandleCount, + (VOID *) &LocalHddInfo + ); + } + + mIdeDataBuiltFlag = TRUE; + + // + // Get Identity command from all drives + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiDiskInfoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + + Private->IdeDriveCount = (UINT8) HandleCount; + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiDiskInfoProtocolGuid, + (VOID **) &DiskInfo + ); + ASSERT_EFI_ERROR (Status); + + if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) { + // + // Locate which PCI device + // + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + (VOID *) &DevicePath + ); + ASSERT_EFI_ERROR (Status); + + DevicePathNode = DevicePath; + while (!IsDevicePathEnd (DevicePathNode)) { + TempDevicePathNode = NextDevicePathNode (DevicePathNode); + if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) && + ( DevicePathSubType (DevicePathNode) == HW_PCI_DP) && + ( DevicePathType(TempDevicePathNode) == MESSAGING_DEVICE_PATH) && + ( DevicePathSubType(TempDevicePathNode) == MSG_ATAPI_DP) ) { + PciDevicePath = (PCI_DEVICE_PATH *) DevicePathNode; + break; + } + DevicePathNode = NextDevicePathNode (DevicePathNode); + } + + if (PciDevicePath == NULL) { + continue; + } + + // + // Find start of PCI device in HddInfo. The assumption of the data + // structure is 2 controllers(channels) per PCI device and each + // controller can have 2 drives(devices). + // HddInfo[PciIndex+0].[0] = Channel[0].Device[0] Primary Master + // HddInfo[PciIndex+0].[1] = Channel[0].Device[1] Primary Slave + // HddInfo[PciIndex+1].[0] = Channel[1].Device[0] Secondary Master + // HddInfo[PciIndex+1].[1] = Channel[1].Device[1] Secondary Slave + // @bug eventually need to pass in max number of entries + // for end of for loop + // + for (PciIndex = 0; PciIndex < 8; PciIndex++) { + if ((PciDevicePath->Device == LocalHddInfo[PciIndex].Device) && + (PciDevicePath->Function == LocalHddInfo[PciIndex].Function) + ) { + break; + } + } + + if (PciIndex == 8) { + continue; + } + + Status = DiskInfo->WhichIde (DiskInfo, &IdeChannel, &IdeDevice); + if (!EFI_ERROR (Status)) { + Size = sizeof (ATAPI_IDENTIFY); + DiskInfo->Identify ( + DiskInfo, + &LocalHddInfo[PciIndex + IdeChannel].IdentifyDrive[IdeDevice], + &Size + ); + if (IdeChannel == 0) { + LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_PRIMARY; + } else if (IdeChannel == 1) { + LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SECONDARY; + } + + InquiryData = NULL; + InquiryDataSize = 0; + Status = DiskInfo->Inquiry ( + DiskInfo, + NULL, + &InquiryDataSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + InquiryData = (UINT8 *) AllocatePool ( + InquiryDataSize + ); + if (InquiryData != NULL) { + Status = DiskInfo->Inquiry ( + DiskInfo, + InquiryData, + &InquiryDataSize + ); + } + } else { + Status = EFI_DEVICE_ERROR; + } + + // + // If ATAPI device then Inquiry will pass and ATA fail. + // + if (!EFI_ERROR (Status)) { + ASSERT (InquiryData != NULL); + // + // If IdeDevice = 0 then set master bit, else slave bit + // + if (IdeDevice == 0) { + if ((InquiryData[0] & 0x1f) == 0x05) { + LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_ATAPI_CDROM; + } else if ((InquiryData[0] & 0x1f) == 0x00) { + LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_ATAPI_ZIPDISK; + } + } else { + if ((InquiryData[0] & 0x1f) == 0x05) { + LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_ATAPI_CDROM; + } else if ((InquiryData[0] & 0x1f) == 0x00) { + LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_ATAPI_ZIPDISK; + } + } + FreePool (InquiryData); + } else { + if (IdeDevice == 0) { + LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_IDE; + } else { + LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_IDE; + } + } + } + } + } + + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + return EFI_SUCCESS; +} + + +/** + If the IDE channel is in compatibility (legacy) mode, remove all + PCI I/O BAR addresses from the controller. + + @param IdeController The handle of target IDE controller + + +**/ +VOID +InitLegacyIdeController ( + IN EFI_HANDLE IdeController + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + UINT8 Pi; + UINT32 IOBarClear; + EFI_STATUS Status; + + // + // If the IDE channel is in compatibility (legacy) mode, remove all + // PCI I/O BAR addresses from the controller. Some software gets + // confused if an IDE controller is in compatibility (legacy) mode + // and has PCI I/O resources allocated + // + Status = gBS->HandleProtocol ( + IdeController, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + if (!EFI_ERROR (Status)) { + IOBarClear = 0x00; + PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x09, 1, &Pi); + if ((Pi & 0x01) == 0) { + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x10, 1, &IOBarClear); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x14, 1, &IOBarClear); + } + if ((Pi & 0x04) == 0) { + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x18, 1, &IOBarClear); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x1C, 1, &IOBarClear); + } + } + + return ; +} diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyPci.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyPci.c new file mode 100644 index 0000000000..a53b57a681 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyPci.c @@ -0,0 +1,2901 @@ +/** @file + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "LegacyBiosInterface.h" +#include + +#define PCI_START_ADDRESS(x) (((x) + 0x7ff) & ~0x7ff) + +#define MAX_BRIDGE_INDEX 0x20 +typedef struct { + UINTN PciSegment; + UINTN PciBus; + UINTN PciDevice; + UINTN PciFunction; + UINT8 PrimaryBus; + UINT8 SecondaryBus; + UINT8 SubordinateBus; +} BRIDGE_TABLE; + +#define ROM_MAX_ENTRIES 24 +BRIDGE_TABLE Bridges[MAX_BRIDGE_INDEX]; +UINTN SortedBridgeIndex[MAX_BRIDGE_INDEX]; +UINTN NumberOfBridges; +LEGACY_PNP_EXPANSION_HEADER *mBasePnpPtr; +UINT16 mBbsRomSegment; +UINTN mHandleCount; +EFI_HANDLE mVgaHandle; +BOOLEAN mIgnoreBbsUpdateFlag; +BOOLEAN mVgaInstallationInProgress = FALSE; +UINT32 mRomCount = 0x00; +ROM_INSTANCE_ENTRY mRomEntry[ROM_MAX_ENTRIES]; + + +/** + Query shadowed legacy ROM parameters registered by RomShadow() previously. + + @param PciHandle PCI device whos ROM has been shadowed + @param DiskStart DiskStart value from EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom + @param DiskEnd DiskEnd value from EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom + @param RomShadowAddress Address where ROM was shadowed + @param ShadowedSize Runtime size of ROM + + @retval EFI_SUCCESS Query Logging successful. + @retval EFI_NOT_FOUND No logged data found about PciHandle. + +**/ +EFI_STATUS +GetShadowedRomParameters ( + IN EFI_HANDLE PciHandle, + OUT UINT8 *DiskStart, OPTIONAL + OUT UINT8 *DiskEnd, OPTIONAL + OUT VOID **RomShadowAddress, OPTIONAL + OUT UINTN *ShadowedSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + UINTN Index; + UINTN PciSegment; + UINTN PciBus; + UINTN PciDevice; + UINTN PciFunction; + + // + // Get the PCI I/O Protocol on PciHandle + // + Status = gBS->HandleProtocol ( + PciHandle, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the location of the PCI device + // + PciIo->GetLocation ( + PciIo, + &PciSegment, + &PciBus, + &PciDevice, + &PciFunction + ); + + for(Index = 0; Index < mRomCount; Index++) { + if ((mRomEntry[Index].PciSegment == PciSegment) && + (mRomEntry[Index].PciBus == PciBus) && + (mRomEntry[Index].PciDevice == PciDevice) && + (mRomEntry[Index].PciFunction == PciFunction)) { + break; + } + } + + if (Index == mRomCount) { + return EFI_NOT_FOUND; + } + + if (DiskStart != NULL) { + *DiskStart = mRomEntry[Index].DiskStart; + } + + if (DiskEnd != NULL) { + *DiskEnd = mRomEntry[Index].DiskEnd; + } + + if (RomShadowAddress != NULL) { + *RomShadowAddress = (VOID *)(UINTN)mRomEntry[Index].ShadowAddress; + } + + if (ShadowedSize != NULL) { + *ShadowedSize = mRomEntry[Index].ShadowedSize; + } + + return EFI_SUCCESS; +} + +/** + Every legacy ROM that is shadowed by the Legacy BIOS driver will be + registered into this API so that the policy code can know what has + happend + + @param PciHandle PCI device whos ROM is being shadowed + @param ShadowAddress Address that ROM was shadowed + @param ShadowedSize Runtime size of ROM + @param DiskStart DiskStart value from + EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom + @param DiskEnd DiskEnd value from + EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom + + @retval EFI_SUCCESS Logging successful. + @retval EFI_OUT_OF_RESOURCES No remaining room for registering another option + ROM. + +**/ +EFI_STATUS +RomShadow ( + IN EFI_HANDLE PciHandle, + IN UINT32 ShadowAddress, + IN UINT32 ShadowedSize, + IN UINT8 DiskStart, + IN UINT8 DiskEnd + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + + // + // See if there is room to register another option ROM + // + if (mRomCount >= ROM_MAX_ENTRIES) { + return EFI_OUT_OF_RESOURCES; + } + // + // Get the PCI I/O Protocol on PciHandle + // + Status = gBS->HandleProtocol ( + PciHandle, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Get the location of the PCI device + // + PciIo->GetLocation ( + PciIo, + &mRomEntry[mRomCount].PciSegment, + &mRomEntry[mRomCount].PciBus, + &mRomEntry[mRomCount].PciDevice, + &mRomEntry[mRomCount].PciFunction + ); + mRomEntry[mRomCount].ShadowAddress = ShadowAddress; + mRomEntry[mRomCount].ShadowedSize = ShadowedSize; + mRomEntry[mRomCount].DiskStart = DiskStart; + mRomEntry[mRomCount].DiskEnd = DiskEnd; + + mRomCount++; + + return EFI_SUCCESS; +} + + +/** + Return EFI_SUCCESS if PciHandle has had a legacy BIOS ROM shadowed. This + information represents every call to RomShadow () + + @param PciHandle PCI device to get status for + + @retval EFI_SUCCESS Legacy ROM loaded for this device + @retval EFI_NOT_FOUND No Legacy ROM loaded for this device + +**/ +EFI_STATUS +IsLegacyRom ( + IN EFI_HANDLE PciHandle + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + UINTN Index; + UINTN Segment; + UINTN Bus; + UINTN Device; + UINTN Function; + + // + // Get the PCI I/O Protocol on PciHandle + // + Status = gBS->HandleProtocol ( + PciHandle, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Get the location of the PCI device + // + PciIo->GetLocation ( + PciIo, + &Segment, + &Bus, + &Device, + &Function + ); + + // + // See if the option ROM from PciHandle has been previously posted + // + for (Index = 0; Index < mRomCount; Index++) { + if (mRomEntry[Index].PciSegment == Segment && + mRomEntry[Index].PciBus == Bus && + mRomEntry[Index].PciDevice == Device && + mRomEntry[Index].PciFunction == Function + ) { + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Find the PC-AT ROM Image in the raw PCI Option ROM. Also return the + related information from the header. + + @param Csm16Revision The PCI interface version of underlying CSM16 + @param VendorId Vendor ID of the PCI device + @param DeviceId Device ID of the PCI device + @param Rom On input pointing to beginning of the raw PCI OpROM + On output pointing to the first legacy PCI OpROM + @param ImageSize On input is the size of Raw PCI Rom + On output is the size of the first legacy PCI ROM + @param MaxRuntimeImageLength The max runtime image length only valid if OpRomRevision >= 3 + @param OpRomRevision Revision of the PCI Rom + @param ConfigUtilityCodeHeader Pointer to Configuration Utility Code Header + + @retval EFI_SUCCESS Successfully find the legacy PCI ROM + @retval EFI_NOT_FOUND Failed to find the legacy PCI ROM + +**/ +EFI_STATUS +GetPciLegacyRom ( + IN UINT16 Csm16Revision, + IN UINT16 VendorId, + IN UINT16 DeviceId, + IN OUT VOID **Rom, + IN OUT UINTN *ImageSize, + OUT UINTN *MaxRuntimeImageLength, OPTIONAL + OUT UINT8 *OpRomRevision, OPTIONAL + OUT VOID **ConfigUtilityCodeHeader OPTIONAL + ) +{ + BOOLEAN Match; + UINT16 *DeviceIdList; + EFI_PCI_ROM_HEADER RomHeader; + PCI_3_0_DATA_STRUCTURE *Pcir; + VOID *BackupImage; + VOID *BestImage; + + + if (*ImageSize < sizeof (EFI_PCI_ROM_HEADER)) { + return EFI_NOT_FOUND; + } + + BestImage = NULL; + BackupImage = NULL; + RomHeader.Raw = *Rom; + while (RomHeader.Generic->Signature == PCI_EXPANSION_ROM_HEADER_SIGNATURE) { + if (*ImageSize < + RomHeader.Raw - (UINT8 *) *Rom + RomHeader.Generic->PcirOffset + sizeof (PCI_DATA_STRUCTURE) + ) { + return EFI_NOT_FOUND; + } + + Pcir = (PCI_3_0_DATA_STRUCTURE *) (RomHeader.Raw + RomHeader.Generic->PcirOffset); + + if (Pcir->CodeType == PCI_CODE_TYPE_PCAT_IMAGE) { + Match = FALSE; + if (Pcir->VendorId == VendorId) { + if (Pcir->DeviceId == DeviceId) { + Match = TRUE; + } else if ((Pcir->Revision >= 3) && (Pcir->DeviceListOffset != 0)) { + DeviceIdList = (UINT16 *)(((UINT8 *) Pcir) + Pcir->DeviceListOffset); + // + // Checking the device list + // + while (*DeviceIdList != 0) { + if (*DeviceIdList == DeviceId) { + Match = TRUE; + break; + } + DeviceIdList ++; + } + } + } + + if (Match) { + if (Csm16Revision >= 0x0300) { + // + // Case 1: CSM16 3.0 + // + if (Pcir->Revision >= 3) { + // + // case 1.1: meets OpRom 3.0 + // Perfect!!! + // + BestImage = RomHeader.Raw; + break; + } else { + // + // case 1.2: meets OpRom 2.x + // Store it and try to find the OpRom 3.0 + // + BackupImage = RomHeader.Raw; + } + } else { + // + // Case 2: CSM16 2.x + // + if (Pcir->Revision >= 3) { + // + // case 2.1: meets OpRom 3.0 + // Store it and try to find the OpRom 2.x + // + BackupImage = RomHeader.Raw; + } else { + // + // case 2.2: meets OpRom 2.x + // Perfect!!! + // + BestImage = RomHeader.Raw; + break; + } + } + } else { + DEBUG ((EFI_D_ERROR, "GetPciLegacyRom - OpRom not match (%04x-%04x)\n", (UINTN)VendorId, (UINTN)DeviceId)); + } + } + + if ((Pcir->Indicator & 0x80) == 0x80) { + break; + } else { + RomHeader.Raw += 512 * Pcir->ImageLength; + } + } + + if (BestImage == NULL) { + if (BackupImage == NULL) { + return EFI_NOT_FOUND; + } + // + // The versions of CSM16 and OpRom don't match exactly + // + BestImage = BackupImage; + } + RomHeader.Raw = BestImage; + Pcir = (PCI_3_0_DATA_STRUCTURE *) (RomHeader.Raw + RomHeader.Generic->PcirOffset); + *Rom = BestImage; + *ImageSize = Pcir->ImageLength * 512; + + if (MaxRuntimeImageLength != NULL) { + if (Pcir->Revision < 3) { + *MaxRuntimeImageLength = 0; + } else { + *MaxRuntimeImageLength = Pcir->MaxRuntimeImageLength * 512; + } + } + + if (OpRomRevision != NULL) { + // + // Optional return PCI Data Structure revision + // + if (Pcir->Length >= 0x1C) { + *OpRomRevision = Pcir->Revision; + } else { + *OpRomRevision = 0; + } + } + + if (ConfigUtilityCodeHeader != NULL) { + // + // Optional return ConfigUtilityCodeHeaderOffset supported by the PC-AT ROM + // + if ((Pcir->Revision < 3) || (Pcir->ConfigUtilityCodeHeaderOffset == 0)) { + *ConfigUtilityCodeHeader = NULL; + } else { + *ConfigUtilityCodeHeader = RomHeader.Raw + Pcir->ConfigUtilityCodeHeaderOffset; + } + } + + return EFI_SUCCESS; +} + +/** + Build a table of bridge info for PIRQ translation. + + @param RoutingTable RoutingTable obtained from Platform. + @param RoutingTableEntries Number of RoutingTable entries. + + @retval EFI_SUCCESS New Subordinate bus. + @retval EFI_NOT_FOUND No more Subordinate busses. + +**/ +EFI_STATUS +CreateBridgeTable ( + IN EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable, + IN UINTN RoutingTableEntries + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN BridgeIndex; + UINTN Index; + UINTN Index1; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE01 PciConfigHeader; + BRIDGE_TABLE SlotBridges[MAX_BRIDGE_INDEX]; + UINTN SlotBridgeIndex; + + BridgeIndex = 0x00; + SlotBridgeIndex = 0x00; + + // + // Assumption is table is built from low bus to high bus numbers. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciIoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + if (EFI_ERROR (Status)) { + continue; + } + + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (PciConfigHeader) / sizeof (UINT32), + &PciConfigHeader + ); + + if (IS_PCI_P2P (&PciConfigHeader) && (BridgeIndex < MAX_BRIDGE_INDEX)) { + PciIo->GetLocation ( + PciIo, + &Bridges[BridgeIndex].PciSegment, + &Bridges[BridgeIndex].PciBus, + &Bridges[BridgeIndex].PciDevice, + &Bridges[BridgeIndex].PciFunction + ); + + Bridges[BridgeIndex].PrimaryBus = PciConfigHeader.Bridge.PrimaryBus; + + Bridges[BridgeIndex].SecondaryBus = PciConfigHeader.Bridge.SecondaryBus; + + Bridges[BridgeIndex].SubordinateBus = PciConfigHeader.Bridge.SubordinateBus; + + for (Index1 = 0; Index1 < RoutingTableEntries; Index1++){ + // + // Test whether we have found the Bridge in the slot, must be the one that directly interfaced to the board + // Once we find one, store it in the SlotBridges[] + // + if ((RoutingTable[Index1].Slot != 0) && (Bridges[BridgeIndex].PrimaryBus == RoutingTable[Index1].Bus) + && ((Bridges[BridgeIndex].PciDevice << 3) == RoutingTable[Index1].Device)) { + CopyMem (&SlotBridges[SlotBridgeIndex], &Bridges[BridgeIndex], sizeof (BRIDGE_TABLE)); + SlotBridgeIndex++; + + break; + } + } + + ++BridgeIndex; + } + } + + // + // Pack up Bridges by removing those useless ones + // + for (Index = 0; Index < BridgeIndex;){ + for (Index1 = 0; Index1 < SlotBridgeIndex; Index1++) { + if (((Bridges[Index].PciBus == SlotBridges[Index1].PrimaryBus) && (Bridges[Index].PciDevice == SlotBridges[Index1].PciDevice)) || + ((Bridges[Index].PciBus >= SlotBridges[Index1].SecondaryBus) && (Bridges[Index].PciBus <= SlotBridges[Index1].SubordinateBus))) { + // + // We have found one that meets our criteria + // + Index++; + break; + } + } + + // + // This one doesn't meet criteria, pack it + // + if (Index1 >= SlotBridgeIndex) { + for (Index1 = Index; BridgeIndex > 1 && Index1 < BridgeIndex - 1 ; Index1++) { + CopyMem (&Bridges[Index1], &Bridges[Index1 + 1], sizeof (BRIDGE_TABLE)); + } + + BridgeIndex--; + } + } + + NumberOfBridges = BridgeIndex; + + // + // Sort bridges low to high by Secondary bus followed by subordinate bus + // + if (NumberOfBridges > 1) { + Index = 0; + do { + SortedBridgeIndex[Index] = Index; + ++Index; + } while (Index < NumberOfBridges); + + for (Index = 0; Index < NumberOfBridges - 1; Index++) { + for (Index1 = Index + 1; Index1 < NumberOfBridges; Index1++) { + if (Bridges[Index].SecondaryBus > Bridges[Index1].SecondaryBus) { + SortedBridgeIndex[Index] = Index1; + SortedBridgeIndex[Index1] = Index; + } + + if ((Bridges[Index].SecondaryBus == Bridges[Index1].SecondaryBus) && + (Bridges[Index].SubordinateBus > Bridges[Index1].SubordinateBus) + ) { + SortedBridgeIndex[Index] = Index1; + SortedBridgeIndex[Index1] = Index; + } + } + } + } + FreePool (HandleBuffer); + return EFI_SUCCESS; +} + + +/** + Find base Bridge for device. + + @param Private Legacy BIOS Instance data + @param PciBus Input = Bus of device. + @param PciDevice Input = Device. + @param RoutingTable The platform specific routing table + @param RoutingTableEntries Number of entries in table + + @retval EFI_SUCCESS At base bus. + @retval EFI_NOT_FOUND Behind a bridge. + +**/ +EFI_STATUS +GetBaseBus ( + IN LEGACY_BIOS_INSTANCE *Private, + IN UINTN PciBus, + IN UINTN PciDevice, + IN EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable, + IN UINTN RoutingTableEntries + ) +{ + UINTN Index; + for (Index = 0; Index < RoutingTableEntries; Index++) { + if ((RoutingTable[Index].Bus == PciBus) && (RoutingTable[Index].Device == (PciDevice << 3))) { + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Translate PIRQ through busses + + @param Private Legacy BIOS Instance data + @param PciBus Input = Bus of device. Output = Translated Bus + @param PciDevice Input = Device. Output = Translated Device + @param PciFunction Input = Function. Output = Translated Function + @param PirqIndex Input = Original PIRQ index. If single function + device then 0, otherwise 0-3. + Output = Translated Index + + @retval EFI_SUCCESS Pirq successfully translated. + @retval EFI_NOT_FOUND The device is not behind any known bridge. + +**/ +EFI_STATUS +TranslateBusPirq ( + IN LEGACY_BIOS_INSTANCE *Private, + IN OUT UINTN *PciBus, + IN OUT UINTN *PciDevice, + IN OUT UINTN *PciFunction, + IN OUT UINT8 *PirqIndex + ) +{ + /* + This routine traverses the PCI busses from base slot + and translates the PIRQ register to the appropriate one. + + Example: + + Bus 0, Device 1 is PCI-PCI bridge that all PCI slots reside on. + Primary bus# = 0 + Secondary bus # = 1 + Subordinate bus # is highest bus # behind this bus + Bus 1, Device 0 is Slot 0 and is not a bridge. + Bus 1, Device 1 is Slot 1 and is a bridge. + Slot PIRQ routing is A,B,C,D. + Primary bus # = 1 + Secondary bus # = 2 + Subordinate bus # = 5 + Bus 2, Device 6 is a bridge. It has no bridges behind it. + Primary bus # = 2 + Secondary bus # = 3 + Subordinate bus # = 3 + Bridge PIRQ routing is C,D,A,B + Bus 2, Device 7 is a bridge. It has 1 bridge behind it. + Primary bus # = 2 + Secondary bus = 4 Device 6 takes bus 2. + Subordinate bus = 5. + Bridge PIRQ routing is D,A,B,C + Bus 4, Device 2 is a bridge. It has no bridges behind it. + Primary bus # = 4 + Secondary bus # = 5 + Subordinate bus = 5 + Bridge PIRQ routing is B,C,D,A + Bus 5, Device 1 is to be programmed. + Device PIRQ routing is C,D,A,B + + +Search busses starting from slot bus for final bus >= Secondary bus and +final bus <= Suborninate bus. Assumption is bus entries increase in bus +number. +Starting PIRQ is A,B,C,D. +Bus 2, Device 7 satisfies search criteria. Rotate (A,B,C,D) left by device + 7 modulo 4 giving (D,A,B,C). +Bus 4, Device 2 satisfies search criteria. Rotate (D,A,B,C) left by 2 giving + (B,C,D,A). +No other busses match criteria. Device to be programmed is Bus 5, Device 1. +Rotate (B,C,D,A) by 1 giving C,D,A,B. Translated PIRQ is C. + +*/ + UINTN LocalBus; + UINTN LocalDevice; + UINTN BaseBus; + UINTN BaseDevice; + UINTN BaseFunction; + UINT8 LocalPirqIndex; + BOOLEAN BaseIndexFlag; + UINTN BridgeIndex; + UINTN SBridgeIndex; + BaseIndexFlag = FALSE; + BridgeIndex = 0x00; + + LocalPirqIndex = *PirqIndex; + LocalBus = *PciBus; + LocalDevice = *PciDevice; + BaseBus = *PciBus; + BaseDevice = *PciDevice; + BaseFunction = *PciFunction; + + // + // LocalPirqIndex list PIRQs in rotated fashion + // = 0 A,B,C,D + // = 1 B,C,D,A + // = 2 C,D,A,B + // = 3 D,A,B,C + // + + for (BridgeIndex = 0; BridgeIndex < NumberOfBridges; BridgeIndex++) { + SBridgeIndex = SortedBridgeIndex[BridgeIndex]; + // + // Check if device behind this bridge + // + if ((LocalBus >= Bridges[SBridgeIndex].SecondaryBus) && (LocalBus <= Bridges[SBridgeIndex].SubordinateBus)) { + // + // If BaseIndexFlag = FALSE then have found base bridge, i.e + // bridge in slot. Save info for use by IRQ routing table. + // + if (!BaseIndexFlag) { + BaseBus = Bridges[SBridgeIndex].PciBus; + BaseDevice = Bridges[SBridgeIndex].PciDevice; + BaseFunction = Bridges[SBridgeIndex].PciFunction; + BaseIndexFlag = TRUE; + } else { + LocalPirqIndex = (UINT8) ((LocalPirqIndex + (UINT8)Bridges[SBridgeIndex].PciDevice)%4); + } + + // + // Check if at device. If not get new PCI location & PIRQ + // + if (Bridges[SBridgeIndex].SecondaryBus == (UINT8) LocalBus) { + // + // Translate PIRQ + // + LocalPirqIndex = (UINT8) ((LocalPirqIndex + (UINT8) (LocalDevice)) % 4); + break; + } + } + } + + // + // In case we fail to find the Bridge just above us, this is some potential error and we want to warn the user + // + if(BridgeIndex >= NumberOfBridges){ + DEBUG ((EFI_D_ERROR, "Cannot Find IRQ Routing for Bus %d, Device %d, Function %d\n", *PciBus, *PciDevice, *PciFunction)); + } + + *PirqIndex = LocalPirqIndex; + *PciBus = BaseBus; + *PciDevice = BaseDevice; + *PciFunction = BaseFunction; + + return EFI_SUCCESS; +} + + +/** + Copy the $PIR table as required. + + @param Private Legacy BIOS Instance data + @param RoutingTable Pointer to IRQ routing table + @param RoutingTableEntries IRQ routing table entries + @param PirqTable Pointer to $PIR table + @param PirqTableSize Length of table + +**/ +VOID +CopyPirqTable ( + IN LEGACY_BIOS_INSTANCE *Private, + IN EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable, + IN UINTN RoutingTableEntries, + IN EFI_LEGACY_PIRQ_TABLE_HEADER *PirqTable, + IN UINTN PirqTableSize + ) +{ + EFI_IA32_REGISTER_SET Regs; + UINT32 Granularity; + + // + // Copy $PIR table, if it exists. + // + if (PirqTable != NULL) { + Private->LegacyRegion->UnLock ( + Private->LegacyRegion, + 0xE0000, + 0x20000, + &Granularity + ); + + Private->InternalIrqRoutingTable = RoutingTable; + Private->NumberIrqRoutingEntries = (UINT16) (RoutingTableEntries); + ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); + + Regs.X.AX = Legacy16GetTableAddress; + Regs.X.CX = (UINT16) PirqTableSize; + // + // Allocate at F segment according to PCI IRQ Routing Table Specification + // + Regs.X.BX = (UINT16) 0x1; + // + // 16-byte boundary alignment requirement according to + // PCI IRQ Routing Table Specification + // + Regs.X.DX = 0x10; + Private->LegacyBios.FarCall86 ( + &Private->LegacyBios, + Private->Legacy16CallSegment, + Private->Legacy16CallOffset, + &Regs, + NULL, + 0 + ); + + Private->Legacy16Table->IrqRoutingTablePointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX); + if (Regs.X.AX != 0) { + DEBUG ((EFI_D_ERROR, "PIRQ table length insufficient - %x\n", PirqTableSize)); + } else { + DEBUG ((EFI_D_INFO, "PIRQ table in legacy region - %x\n", Private->Legacy16Table->IrqRoutingTablePointer)); + Private->Legacy16Table->IrqRoutingTableLength = (UINT32)PirqTableSize; + CopyMem ( + (VOID *) (UINTN)Private->Legacy16Table->IrqRoutingTablePointer, + PirqTable, + PirqTableSize + ); + } + + Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate); + Private->LegacyRegion->Lock ( + Private->LegacyRegion, + 0xE0000, + 0x20000, + &Granularity + ); + } + + Private->PciInterruptLine = TRUE; + mHandleCount = 0; +} + +/** + Dump EFI_LEGACY_INSTALL_PCI_HANDLER structure information. + + @param PciHandle The pointer to EFI_LEGACY_INSTALL_PCI_HANDLER structure + +**/ +VOID +DumpPciHandle ( + IN EFI_LEGACY_INSTALL_PCI_HANDLER *PciHandle + ) +{ + DEBUG ((EFI_D_INFO, "PciBus - %02x\n", (UINTN)PciHandle->PciBus)); + DEBUG ((EFI_D_INFO, "PciDeviceFun - %02x\n", (UINTN)PciHandle->PciDeviceFun)); + DEBUG ((EFI_D_INFO, "PciSegment - %02x\n", (UINTN)PciHandle->PciSegment)); + DEBUG ((EFI_D_INFO, "PciClass - %02x\n", (UINTN)PciHandle->PciClass)); + DEBUG ((EFI_D_INFO, "PciSubclass - %02x\n", (UINTN)PciHandle->PciSubclass)); + DEBUG ((EFI_D_INFO, "PciInterface - %02x\n", (UINTN)PciHandle->PciInterface)); + + DEBUG ((EFI_D_INFO, "PrimaryIrq - %02x\n", (UINTN)PciHandle->PrimaryIrq)); + DEBUG ((EFI_D_INFO, "PrimaryReserved - %02x\n", (UINTN)PciHandle->PrimaryReserved)); + DEBUG ((EFI_D_INFO, "PrimaryControl - %04x\n", (UINTN)PciHandle->PrimaryControl)); + DEBUG ((EFI_D_INFO, "PrimaryBase - %04x\n", (UINTN)PciHandle->PrimaryBase)); + DEBUG ((EFI_D_INFO, "PrimaryBusMaster - %04x\n", (UINTN)PciHandle->PrimaryBusMaster)); + + DEBUG ((EFI_D_INFO, "SecondaryIrq - %02x\n", (UINTN)PciHandle->SecondaryIrq)); + DEBUG ((EFI_D_INFO, "SecondaryReserved - %02x\n", (UINTN)PciHandle->SecondaryReserved)); + DEBUG ((EFI_D_INFO, "SecondaryControl - %04x\n", (UINTN)PciHandle->SecondaryControl)); + DEBUG ((EFI_D_INFO, "SecondaryBase - %04x\n", (UINTN)PciHandle->SecondaryBase)); + DEBUG ((EFI_D_INFO, "SecondaryBusMaster - %04x\n", (UINTN)PciHandle->SecondaryBusMaster)); + return; +} + +/** + Copy the $PIR table as required. + + @param Private Legacy BIOS Instance data + @param PciIo Pointer to PCI_IO protocol + @param PciIrq Pci IRQ number + @param PciConfigHeader Type00 Pci configuration header + +**/ +VOID +InstallLegacyIrqHandler ( + IN LEGACY_BIOS_INSTANCE *Private, + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 PciIrq, + IN PCI_TYPE00 *PciConfigHeader + ) +{ + EFI_IA32_REGISTER_SET Regs; + UINT16 LegMask; + UINTN PciSegment; + UINTN PciBus; + UINTN PciDevice; + UINTN PciFunction; + EFI_LEGACY_8259_PROTOCOL *Legacy8259; + UINT16 PrimaryMaster; + UINT16 SecondaryMaster; + UINTN TempData; + UINTN RegisterAddress; + UINT32 Granularity; + + PrimaryMaster = 0; + SecondaryMaster = 0; + Legacy8259 = Private->Legacy8259; + // + // Disable interrupt in PIC, in case shared, to prevent an + // interrupt from occuring. + // + Legacy8259->GetMask ( + Legacy8259, + &LegMask, + NULL, + NULL, + NULL + ); + + LegMask = (UINT16) (LegMask | (UINT16) (1 << PciIrq)); + + Legacy8259->SetMask ( + Legacy8259, + &LegMask, + NULL, + NULL, + NULL + ); + + PciIo->GetLocation ( + PciIo, + &PciSegment, + &PciBus, + &PciDevice, + &PciFunction + ); + Private->IntThunk->PciHandler.PciBus = (UINT8) PciBus; + Private->IntThunk->PciHandler.PciDeviceFun = (UINT8) ((PciDevice << 3) + PciFunction); + Private->IntThunk->PciHandler.PciSegment = (UINT8) PciSegment; + Private->IntThunk->PciHandler.PciClass = PciConfigHeader->Hdr.ClassCode[2]; + Private->IntThunk->PciHandler.PciSubclass = PciConfigHeader->Hdr.ClassCode[1]; + Private->IntThunk->PciHandler.PciInterface = PciConfigHeader->Hdr.ClassCode[0]; + + // + // Use native mode base address registers in two cases: + // 1. Programming Interface (PI) register indicates Primary Controller is + // in native mode OR + // 2. PCI device Sub Class Code is not IDE + // + Private->IntThunk->PciHandler.PrimaryBusMaster = (UINT16)(PciConfigHeader->Device.Bar[4] & 0xfffc); + if (((PciConfigHeader->Hdr.ClassCode[0] & 0x01) != 0) || (PciConfigHeader->Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE)) { + Private->IntThunk->PciHandler.PrimaryIrq = PciIrq; + Private->IntThunk->PciHandler.PrimaryBase = (UINT16) (PciConfigHeader->Device.Bar[0] & 0xfffc); + Private->IntThunk->PciHandler.PrimaryControl = (UINT16) ((PciConfigHeader->Device.Bar[1] & 0xfffc) + 2); + } else { + Private->IntThunk->PciHandler.PrimaryIrq = 14; + Private->IntThunk->PciHandler.PrimaryBase = 0x1f0; + Private->IntThunk->PciHandler.PrimaryControl = 0x3f6; + } + // + // Secondary controller data + // + if (Private->IntThunk->PciHandler.PrimaryBusMaster != 0) { + Private->IntThunk->PciHandler.SecondaryBusMaster = (UINT16) ((PciConfigHeader->Device.Bar[4] & 0xfffc) + 8); + PrimaryMaster = (UINT16) (Private->IntThunk->PciHandler.PrimaryBusMaster + 2); + SecondaryMaster = (UINT16) (Private->IntThunk->PciHandler.SecondaryBusMaster + 2); + + // + // Clear pending interrupts in Bus Master registers + // + IoWrite16 (PrimaryMaster, 0x04); + IoWrite16 (SecondaryMaster, 0x04); + + } + + // + // Use native mode base address registers in two cases: + // 1. Programming Interface (PI) register indicates Secondary Controller is + // in native mode OR + // 2. PCI device Sub Class Code is not IDE + // + if (((PciConfigHeader->Hdr.ClassCode[0] & 0x04) != 0) || (PciConfigHeader->Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE)) { + Private->IntThunk->PciHandler.SecondaryIrq = PciIrq; + Private->IntThunk->PciHandler.SecondaryBase = (UINT16) (PciConfigHeader->Device.Bar[2] & 0xfffc); + Private->IntThunk->PciHandler.SecondaryControl = (UINT16) ((PciConfigHeader->Device.Bar[3] & 0xfffc) + 2); + } else { + + Private->IntThunk->PciHandler.SecondaryIrq = 15; + Private->IntThunk->PciHandler.SecondaryBase = 0x170; + Private->IntThunk->PciHandler.SecondaryControl = 0x376; + } + + // + // Clear pending interrupts in IDE Command Block Status reg before we + // Thunk to CSM16 below. Don't want a pending Interrupt before we + // install the handlers as wierd corruption would occur and hang system. + // + // + // Read IDE CMD blk status reg to clear out any pending interrupts. + // Do here for Primary and Secondary IDE channels + // + RegisterAddress = (UINT16)Private->IntThunk->PciHandler.PrimaryBase + 0x07; + IoRead8 (RegisterAddress); + RegisterAddress = (UINT16)Private->IntThunk->PciHandler.SecondaryBase + 0x07; + IoRead8 (RegisterAddress); + + Private->IntThunk->PciHandler.PrimaryReserved = 0; + Private->IntThunk->PciHandler.SecondaryReserved = 0; + Private->LegacyRegion->UnLock ( + Private->LegacyRegion, + 0xE0000, + 0x20000, + &Granularity + ); + + Regs.X.AX = Legacy16InstallPciHandler; + TempData = (UINTN) &Private->IntThunk->PciHandler; + Regs.X.ES = EFI_SEGMENT ((UINT32) TempData); + Regs.X.BX = EFI_OFFSET ((UINT32) TempData); + + DumpPciHandle (&Private->IntThunk->PciHandler); + + Private->LegacyBios.FarCall86 ( + &Private->LegacyBios, + Private->Legacy16CallSegment, + Private->Legacy16CallOffset, + &Regs, + NULL, + 0 + ); + + Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate); + Private->LegacyRegion->Lock ( + Private->LegacyRegion, + 0xE0000, + 0x20000, + &Granularity + ); + +} + + +/** + Program the interrupt routing register in all the PCI devices. On a PC AT system + this register contains the 8259 IRQ vector that matches it's PCI interrupt. + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS Succeed. + @retval EFI_ALREADY_STARTED All PCI devices have been processed. + +**/ +EFI_STATUS +PciProgramAllInterruptLineRegisters ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_LEGACY_8259_PROTOCOL *Legacy8259; + EFI_LEGACY_INTERRUPT_PROTOCOL *LegacyInterrupt; + EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform; + UINT8 InterruptPin; + UINTN Index; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN MassStorageHandleCount; + EFI_HANDLE *MassStorageHandleBuffer; + UINTN MassStorageHandleIndex; + UINT8 PciIrq; + UINT16 Command; + UINTN PciSegment; + UINTN PciBus; + UINTN PciDevice; + UINTN PciFunction; + EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable; + UINTN RoutingTableEntries; + UINT16 LegMask; + UINT16 LegEdgeLevel; + PCI_TYPE00 PciConfigHeader; + EFI_LEGACY_PIRQ_TABLE_HEADER *PirqTable; + UINTN PirqTableSize; + UINTN Flags; + HDD_INFO *HddInfo; + UINT64 Supports; + + // + // Note - This routine use to return immediately if Private->PciInterruptLine + // was true. Routine changed since resets etc can cause not all + // PciIo protocols to be registered the first time through. + // New algorithm is to do the copy $PIR table on first pass and save + // HandleCount on first pass. If subsequent passes LocateHandleBuffer gives + // a larger handle count then proceed with body of function else return + // EFI_ALREADY_STARTED. In addition check if PCI device InterruptLine != 0. + // If zero then function unprogrammed else skip function. + // + Legacy8259 = Private->Legacy8259; + LegacyInterrupt = Private->LegacyInterrupt; + LegacyBiosPlatform = Private->LegacyBiosPlatform; + + LegacyBiosPlatform->GetRoutingTable ( + Private->LegacyBiosPlatform, + (VOID *) &RoutingTable, + &RoutingTableEntries, + (VOID *) &PirqTable, + &PirqTableSize, + NULL, + NULL + ); + CreateBridgeTable (RoutingTable, RoutingTableEntries); + + if (!Private->PciInterruptLine) { + CopyPirqTable ( + Private, + RoutingTable, + RoutingTableEntries, + PirqTable, + PirqTableSize + ); + } + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciIoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + if (HandleCount == mHandleCount) { + FreePool (HandleBuffer); + return EFI_ALREADY_STARTED; + } + + if (mHandleCount == 0x00) { + mHandleCount = HandleCount; + } + + for (Index = 0; Index < HandleCount; Index++) { + // + // If VGA then only do VGA to allow drives fore time to spin up + // otherwise assign PCI IRQs to all potential devices. + // + if ((mVgaInstallationInProgress) && (HandleBuffer[Index] != mVgaHandle)) { + continue; + } else { + // + // Force code to go through all handles next time called if video. + // This will catch case where HandleCount doesn't change but want + // to get drive info etc. + // + mHandleCount = 0x00; + } + + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + ASSERT_EFI_ERROR (Status); + + // + // Test whether the device can be enabled or not. + // If it can't be enabled, then just skip it to avoid further operation. + // + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (PciConfigHeader) / sizeof (UINT32), + &PciConfigHeader + ); + Command = PciConfigHeader.Hdr.Command; + + // + // Note PciIo->Attributes does not program the PCI command register + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= EFI_PCI_DEVICE_ENABLE; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x04, 1, &Command); + + if (EFI_ERROR (Status)) { + continue; + } + + InterruptPin = PciConfigHeader.Device.InterruptPin; + + if ((InterruptPin != 0) && (PciConfigHeader.Device.InterruptLine == PCI_INT_LINE_UNKNOWN)) { + PciIo->GetLocation ( + PciIo, + &PciSegment, + &PciBus, + &PciDevice, + &PciFunction + ); + // + // Translate PIRQ index back thru busses to slot bus with InterruptPin + // zero based + // + InterruptPin -= 1; + + Status = GetBaseBus ( + Private, + PciBus, + PciDevice, + RoutingTable, + RoutingTableEntries + ); + + if (Status == EFI_NOT_FOUND) { + TranslateBusPirq ( + Private, + &PciBus, + &PciDevice, + &PciFunction, + &InterruptPin + ); + } + // + // Translate InterruptPin(0-3) into PIRQ + // + Status = LegacyBiosPlatform->TranslatePirq ( + LegacyBiosPlatform, + PciBus, + (PciDevice << 3), + PciFunction, + &InterruptPin, + &PciIrq + ); + // + // TranslatePirq() should never fail or we are in trouble + // If it does return failure status, check your PIRQ routing table to see if some item is missing or incorrect + // + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Translate Pirq Failed - Status = %r\n ", Status)); + continue; + } + + LegacyInterrupt->WritePirq ( + LegacyInterrupt, + InterruptPin, + PciIrq + ); + + // + // Check if device has an OPROM associated with it. + // If not invoke special 16-bit function, to allow 16-bit + // code to install an interrupt handler. + // + Status = LegacyBiosCheckPciRom ( + &Private->LegacyBios, + HandleBuffer[Index], + NULL, + NULL, + &Flags + ); + if ((EFI_ERROR (Status)) && (PciConfigHeader.Hdr.ClassCode[2] == PCI_CLASS_MASS_STORAGE)) { + // + // Device has no OPROM associated with it and is a mass storage + // device. It needs to have an PCI IRQ handler installed. To + // correctly install the handler we need to insure device is + // connected. The device may just have register itself but not + // been connected. Re-read PCI config space after as it can + // change + // + // + // Get IDE Handle. If matches handle then skip ConnectController + // since ConnectController may force native mode and we don't + // want that for primary IDE controller + // + MassStorageHandleCount = 0; + MassStorageHandleBuffer = NULL; + LegacyBiosPlatform->GetPlatformHandle ( + Private->LegacyBiosPlatform, + EfiGetPlatformIdeHandle, + 0, + &MassStorageHandleBuffer, + &MassStorageHandleCount, + NULL + ); + + HddInfo = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo[0]; + + LegacyBiosBuildIdeData (Private, &HddInfo, 0); + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (PciConfigHeader) / sizeof (UINT32), + &PciConfigHeader + ); + + for (MassStorageHandleIndex = 0; MassStorageHandleIndex < MassStorageHandleCount; MassStorageHandleIndex++) { + if (MassStorageHandleBuffer[MassStorageHandleIndex] == HandleBuffer[Index]) { + // + // InstallLegacyIrqHandler according to Platform requirement + // + InstallLegacyIrqHandler ( + Private, + PciIo, + PciIrq, + &PciConfigHeader + ); + break; + } + } + } + // + // Write InterruptPin and enable 8259. + // + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint8, + 0x3c, + 1, + &PciIrq + ); + Private->IntThunk->EfiToLegacy16BootTable.PciIrqMask = (UINT16) (Private->IntThunk->EfiToLegacy16BootTable.PciIrqMask | (UINT16) (1 << PciIrq)); + + Legacy8259->GetMask ( + Legacy8259, + &LegMask, + &LegEdgeLevel, + NULL, + NULL + ); + + LegMask = (UINT16) (LegMask & (UINT16)~(1 << PciIrq)); + LegEdgeLevel = (UINT16) (LegEdgeLevel | (UINT16) (1 << PciIrq)); + Legacy8259->SetMask ( + Legacy8259, + &LegMask, + &LegEdgeLevel, + NULL, + NULL + ); + } + } + FreePool (HandleBuffer); + return EFI_SUCCESS; +} + + +/** + Find & verify PnP Expansion header in ROM image + + @param Private Protocol instance pointer. + @param FirstHeader 1 = Find first header, 0 = Find successive headers + @param PnpPtr Input Rom start if FirstHeader =1, Current Header + otherwise Output Next header, if it exists + + @retval EFI_SUCCESS Next Header found at BasePnpPtr + @retval EFI_NOT_FOUND No more headers + +**/ +EFI_STATUS +FindNextPnpExpansionHeader ( + IN LEGACY_BIOS_INSTANCE *Private, + IN BOOLEAN FirstHeader, + IN OUT LEGACY_PNP_EXPANSION_HEADER **PnpPtr + + ) +{ + UINTN TempData; + LEGACY_PNP_EXPANSION_HEADER *LocalPnpPtr; + LocalPnpPtr = *PnpPtr; + if (FirstHeader == FIRST_INSTANCE) { + mBasePnpPtr = LocalPnpPtr; + mBbsRomSegment = (UINT16) ((UINTN) mBasePnpPtr >> 4); + // + // Offset 0x1a gives offset to PnP expansion header for the first + // instance, there after the structure gives the offset to the next + // structure + // + LocalPnpPtr = (LEGACY_PNP_EXPANSION_HEADER *) ((UINT8 *) LocalPnpPtr + 0x1a); + TempData = (*((UINT16 *) LocalPnpPtr)); + } else { + TempData = (UINT16) LocalPnpPtr->NextHeader; + } + + LocalPnpPtr = (LEGACY_PNP_EXPANSION_HEADER *) (((UINT8 *) mBasePnpPtr + TempData)); + + // + // Search for PnP table in Shadowed ROM + // + *PnpPtr = LocalPnpPtr; + if (*(UINT32 *) LocalPnpPtr == SIGNATURE_32 ('$', 'P', 'n', 'P')) { + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} + + +/** + Update list of Bev or BCV table entries. + + @param Private Protocol instance pointer. + @param RomStart Table of ROM start address in RAM/ROM. PciIo _ + Handle to PCI IO for this device + @param PciIo Instance of PCI I/O Protocol + + @retval EFI_SUCCESS Always should succeed. + +**/ +EFI_STATUS +UpdateBevBcvTable ( + IN LEGACY_BIOS_INSTANCE *Private, + IN EFI_LEGACY_EXPANSION_ROM_HEADER *RomStart, + IN EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + VOID *RomEnd; + BBS_TABLE *BbsTable; + UINTN BbsIndex; + EFI_LEGACY_EXPANSION_ROM_HEADER *PciPtr; + LEGACY_PNP_EXPANSION_HEADER *PnpPtr; + BOOLEAN Instance; + EFI_STATUS Status; + UINTN Segment; + UINTN Bus; + UINTN Device; + UINTN Function; + UINT8 Class; + UINT16 DeviceType; + Segment = 0; + Bus = 0; + Device = 0; + Function = 0; + Class = 0; + DeviceType = BBS_UNKNOWN; + + // + // Skip floppy and 2*onboard IDE controller entries(Master/Slave per + // controller). + // + BbsIndex = Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries; + + BbsTable = (BBS_TABLE*)(UINTN) Private->IntThunk->EfiToLegacy16BootTable.BbsTable; + PnpPtr = (LEGACY_PNP_EXPANSION_HEADER *) RomStart; + PciPtr = (EFI_LEGACY_EXPANSION_ROM_HEADER *) RomStart; + + RomEnd = (VOID *) (PciPtr->Size512 * 512 + (UINTN) PciPtr); + Instance = FIRST_INSTANCE; + // + // OPROMs like PXE may not be tied to a piece of hardware and thus + // don't have a PciIo associated with them + // + if (PciIo != NULL) { + PciIo->GetLocation ( + PciIo, + &Segment, + &Bus, + &Device, + &Function + ); + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + 0x0b, + 1, + &Class + ); + + if (Class == PCI_CLASS_MASS_STORAGE) { + DeviceType = BBS_HARDDISK; + } else { + if (Class == PCI_CLASS_NETWORK) { + DeviceType = BBS_EMBED_NETWORK; + } + } + } + + if (PciPtr >= (EFI_LEGACY_EXPANSION_ROM_HEADER *) ((UINTN) 0xc8000)) { + while (TRUE) { + Status = FindNextPnpExpansionHeader (Private, Instance, &PnpPtr); + Instance = NOT_FIRST_INSTANCE; + if (EFI_ERROR (Status)) { + break; + } + // + // There can be additional $PnP headers within the OPROM. + // Example: SCSI can have one per drive. + // + BbsTable[BbsIndex].BootPriority = BBS_UNPRIORITIZED_ENTRY; + BbsTable[BbsIndex].DeviceType = DeviceType; + BbsTable[BbsIndex].Bus = (UINT32) Bus; + BbsTable[BbsIndex].Device = (UINT32) Device; + BbsTable[BbsIndex].Function = (UINT32) Function; + BbsTable[BbsIndex].StatusFlags.OldPosition = 0; + BbsTable[BbsIndex].StatusFlags.Reserved1 = 0; + BbsTable[BbsIndex].StatusFlags.Enabled = 0; + BbsTable[BbsIndex].StatusFlags.Failed = 0; + BbsTable[BbsIndex].StatusFlags.MediaPresent = 0; + BbsTable[BbsIndex].StatusFlags.Reserved2 = 0; + BbsTable[BbsIndex].Class = PnpPtr->Class; + BbsTable[BbsIndex].SubClass = PnpPtr->SubClass; + BbsTable[BbsIndex].DescStringOffset = PnpPtr->ProductNamePointer; + BbsTable[BbsIndex].DescStringSegment = mBbsRomSegment; + BbsTable[BbsIndex].MfgStringOffset = PnpPtr->MfgPointer; + BbsTable[BbsIndex].MfgStringSegment = mBbsRomSegment; + BbsTable[BbsIndex].BootHandlerSegment = mBbsRomSegment; + + // + // Have seen case where PXE base code have PnP expansion ROM + // header but no Bcv or Bev vectors. + // + if (PnpPtr->Bcv != 0) { + BbsTable[BbsIndex].BootHandlerOffset = PnpPtr->Bcv; + ++BbsIndex; + } + + if (PnpPtr->Bev != 0) { + BbsTable[BbsIndex].BootHandlerOffset = PnpPtr->Bev; + BbsTable[BbsIndex].DeviceType = BBS_BEV_DEVICE; + ++BbsIndex; + } + + if ((PnpPtr == (LEGACY_PNP_EXPANSION_HEADER *) PciPtr) || (PnpPtr > (LEGACY_PNP_EXPANSION_HEADER *) RomEnd)) { + break; + } + } + } + + BbsTable[BbsIndex].BootPriority = BBS_IGNORE_ENTRY; + Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries = (UINT32) BbsIndex; + return EFI_SUCCESS; +} + + +/** + Shadow all the PCI legacy ROMs. Use data from the Legacy BIOS Protocol + to chose the order. Skip any devices that have already have legacy + BIOS run. + + @param Private Protocol instance pointer. + + @retval EFI_SUCCESS Succeed. + @retval EFI_UNSUPPORTED Cannot get VGA device handle. + +**/ +EFI_STATUS +PciShadowRoms ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + UINTN Index; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + EFI_HANDLE VgaHandle; + EFI_HANDLE FirstHandle; + VOID **RomStart; + UINTN Flags; + PCI_TYPE00 PciConfigHeader; + UINT16 *Command; + UINT64 Supports; + + // + // Make the VGA device first + // + Status = Private->LegacyBiosPlatform->GetPlatformHandle ( + Private->LegacyBiosPlatform, + EfiGetPlatformVgaHandle, + 0, + &HandleBuffer, + &HandleCount, + NULL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + VgaHandle = HandleBuffer[0]; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciIoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Place the VGA handle as first. + // + for (Index = 0; Index < HandleCount; Index++) { + if (HandleBuffer[Index] == VgaHandle) { + FirstHandle = HandleBuffer[0]; + HandleBuffer[0] = HandleBuffer[Index]; + HandleBuffer[Index] = FirstHandle; + break; + } + } + // + // Allocate memory to save Command WORD from each device. We do this + // to restore devices to same state as EFI after switching to legacy. + // + Command = (UINT16 *) AllocatePool ( + sizeof (UINT16) * (HandleCount + 1) + ); + if (NULL == Command) { + FreePool (HandleBuffer); + return EFI_OUT_OF_RESOURCES; + } + // + // Disconnect all EFI devices first. This covers cases where alegacy BIOS + // may control multiple PCI devices. + // + for (Index = 0; Index < HandleCount; Index++) { + + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + ASSERT_EFI_ERROR (Status); + + // + // Save command register for "connect" loop + // + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (PciConfigHeader) / sizeof (UINT32), + &PciConfigHeader + ); + Command[Index] = PciConfigHeader.Hdr.Command; + // + // Skip any device that already has a legacy ROM run + // + Status = IsLegacyRom (HandleBuffer[Index]); + if (!EFI_ERROR (Status)) { + continue; + } + // + // Stop EFI Drivers with oprom. + // + gBS->DisconnectController ( + HandleBuffer[Index], + NULL, + NULL + ); + } + // + // For every device that has not had a legacy ROM started. Start a legacy ROM. + // + for (Index = 0; Index < HandleCount; Index++) { + + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + + ASSERT_EFI_ERROR (Status); + + // + // Here make sure if one VGA have been shadowed, + // then wil not shadowed another one. + // + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + + // + // Only one Video OPROM can be given control in BIOS phase. If there are multiple Video devices, + // one will work in legacy mode (OPROM will be given control) and + // other Video devices will work in native mode (OS driver will handle these devices). + // + if (IS_PCI_DISPLAY (&Pci) && Index != 0) { + continue; + } + // + // Skip any device that already has a legacy ROM run + // + Status = IsLegacyRom (HandleBuffer[Index]); + if (!EFI_ERROR (Status)) { + continue; + } + // + // Install legacy ROM + // + Status = LegacyBiosInstallPciRom ( + &Private->LegacyBios, + HandleBuffer[Index], + NULL, + &Flags, + NULL, + NULL, + (VOID **) &RomStart, + NULL + ); + if (EFI_ERROR (Status)) { + if (!((Status == EFI_UNSUPPORTED) && (Flags == NO_ROM))) { + continue; + } + } + // + // Restore Command register so legacy has same devices enabled or disabled + // as EFI. + // If Flags = NO_ROM use command register as is. This covers the + // following cases: + // Device has no ROMs associated with it. + // Device has ROM associated with it but was already + // installed. + // = ROM_FOUND but not VALID_LEGACY_ROM, disable it. + // = ROM_FOUND and VALID_LEGACY_ROM, enable it. + // + if ((Flags & ROM_FOUND) == ROM_FOUND) { + if ((Flags & VALID_LEGACY_ROM) == 0) { + Command[Index] = 0; + } else { + // + // For several VGAs, only one of them can be enabled. + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= EFI_PCI_DEVICE_ENABLE; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } + if (!EFI_ERROR (Status)) { + Command[Index] = 0x1f; + } + } + } + + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + 0x04, + 1, + &Command[Index] + ); + } + + FreePool (Command); + FreePool (HandleBuffer); + return EFI_SUCCESS; +} + + +/** + Test to see if a legacy PCI ROM exists for this device. Optionally return + the Legacy ROM instance for this PCI device. + + @param This Protocol instance pointer. + @param PciHandle The PCI PC-AT OPROM from this devices ROM BAR will + be loaded + @param RomImage Return the legacy PCI ROM for this device + @param RomSize Size of ROM Image + @param Flags Indicates if ROM found and if PC-AT. + + @retval EFI_SUCCESS Legacy Option ROM availible for this device + @retval EFI_UNSUPPORTED Legacy Option ROM not supported. + +**/ +EFI_STATUS +EFIAPI +LegacyBiosCheckPciRom ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN EFI_HANDLE PciHandle, + OUT VOID **RomImage, OPTIONAL + OUT UINTN *RomSize, OPTIONAL + OUT UINTN *Flags + ) +{ + return LegacyBiosCheckPciRomEx ( + This, + PciHandle, + RomImage, + RomSize, + NULL, + Flags, + NULL, + NULL + ); + +} + +/** + + Routine Description: + Test to see if a legacy PCI ROM exists for this device. Optionally return + the Legacy ROM instance for this PCI device. + + @param[in] This Protocol instance pointer. + @param[in] PciHandle The PCI PC-AT OPROM from this devices ROM BAR will be loaded + @param[out] RomImage Return the legacy PCI ROM for this device + @param[out] RomSize Size of ROM Image + @param[out] RuntimeImageLength Runtime size of ROM Image + @param[out] Flags Indicates if ROM found and if PC-AT. + @param[out] OpromRevision Revision of the PCI Rom + @param[out] ConfigUtilityCodeHeaderPointer of Configuration Utility Code Header + + @return EFI_SUCCESS Legacy Option ROM availible for this device + @return EFI_ALREADY_STARTED This device is already managed by its Oprom + @return EFI_UNSUPPORTED Legacy Option ROM not supported. + +**/ +EFI_STATUS +LegacyBiosCheckPciRomEx ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN EFI_HANDLE PciHandle, + OUT VOID **RomImage, OPTIONAL + OUT UINTN *RomSize, OPTIONAL + OUT UINTN *RuntimeImageLength, OPTIONAL + OUT UINTN *Flags, OPTIONAL + OUT UINT8 *OpromRevision, OPTIONAL + OUT VOID **ConfigUtilityCodeHeader OPTIONAL + ) +{ + EFI_STATUS Status; + LEGACY_BIOS_INSTANCE *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + UINTN LocalRomSize; + VOID *LocalRomImage; + PCI_TYPE00 PciConfigHeader; + VOID *LocalConfigUtilityCodeHeader; + + *Flags = NO_ROM; + Status = gBS->HandleProtocol ( + PciHandle, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // See if the option ROM for PciHandle has already been executed + // + Status = IsLegacyRom (PciHandle); + if (!EFI_ERROR (Status)) { + *Flags |= (ROM_FOUND | VALID_LEGACY_ROM); + return EFI_SUCCESS; + } + // + // Check for PCI ROM Bar + // + LocalRomSize = (UINTN) PciIo->RomSize; + LocalRomImage = PciIo->RomImage; + if (LocalRomSize != 0) { + *Flags |= ROM_FOUND; + } + + // + // PCI specification states you should check VendorId and Device Id. + // + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (PciConfigHeader) / sizeof (UINT32), + &PciConfigHeader + ); + + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + Status = GetPciLegacyRom ( + Private->Csm16PciInterfaceVersion, + PciConfigHeader.Hdr.VendorId, + PciConfigHeader.Hdr.DeviceId, + &LocalRomImage, + &LocalRomSize, + RuntimeImageLength, + OpromRevision, + &LocalConfigUtilityCodeHeader + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + *Flags |= VALID_LEGACY_ROM; + + // + // See if Configuration Utility Code Header valid + // + if (LocalConfigUtilityCodeHeader != NULL) { + *Flags |= ROM_WITH_CONFIG; + } + + if (ConfigUtilityCodeHeader != NULL) { + *ConfigUtilityCodeHeader = LocalConfigUtilityCodeHeader; + } + + if (RomImage != NULL) { + *RomImage = LocalRomImage; + } + + if (RomSize != NULL) { + *RomSize = LocalRomSize; + } + + return EFI_SUCCESS; +} + +/** + Load a legacy PC-AT OPROM on the PciHandle device. Return information + about how many disks were added by the OPROM and the shadow address and + size. DiskStart & DiskEnd are INT 13h drive letters. Thus 0x80 is C: + + @retval EFI_SUCCESS Legacy ROM loaded for this device + @retval EFI_NOT_FOUND No PS2 Keyboard found + +**/ +EFI_STATUS +EnablePs2Keyboard ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN HandleCount; + EFI_ISA_IO_PROTOCOL *IsaIo; + UINTN Index; + + // + // Get SimpleTextIn and find PS2 controller + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleTextInProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + for (Index = 0; Index < HandleCount; Index++) { + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + HandleBuffer[Index], + &gEfiIsaIoProtocolGuid, + (VOID **) &IsaIo, + NULL, + HandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + // + // Use the ISA I/O Protocol to see if Controller is the Keyboard + // controller + // + if (IsaIo->ResourceList->Device.HID != EISA_PNP_ID (0x303) || IsaIo->ResourceList->Device.UID != 0) { + Status = EFI_UNSUPPORTED; + } + + gBS->CloseProtocol ( + HandleBuffer[Index], + &gEfiIsaIoProtocolGuid, + NULL, + HandleBuffer[Index] + ); + } + + if (!EFI_ERROR (Status)) { + gBS->ConnectController (HandleBuffer[Index], NULL, NULL, FALSE); + } + } + FreePool (HandleBuffer); + return EFI_SUCCESS; +} + + +/** + Load a legacy PC-AT OpROM for VGA controller. + + @param Private Driver private data. + + @retval EFI_SUCCESS Legacy ROM successfully installed for this device. + @retval EFI_DEVICE_ERROR No VGA device handle found, or native EFI video + driver cannot be successfully disconnected, or VGA + thunk driver cannot be successfully connected. + +**/ +EFI_STATUS +LegacyBiosInstallVgaRom ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + EFI_STATUS Status; + EFI_HANDLE VgaHandle; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + EFI_HANDLE *ConnectHandleBuffer; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 PciConfigHeader; + UINT64 Supports; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + UINTN Index; + VOID *Interface; + + // + // EfiLegacyBiosGuild attached to a device implies that there is a legacy + // BIOS associated with that device. + // + // There are 3 cases to consider. + // Case 1: No EFI driver is controlling the video. + // Action: Return EFI_SUCCESS from DisconnectController, search + // video thunk driver, and connect it. + // Case 2: EFI driver is controlling the video and EfiLegacyBiosGuid is + // not on the image handle. + // Action: Disconnect EFI driver. + // ConnectController for video thunk + // Case 3: EFI driver is controlling the video and EfiLegacyBiosGuid is + // on the image handle. + // Action: Do nothing and set Private->VgaInstalled = TRUE. + // Then this routine is not called any more. + // + // + // Get the VGA device. + // + Status = Private->LegacyBiosPlatform->GetPlatformHandle ( + Private->LegacyBiosPlatform, + EfiGetPlatformVgaHandle, + 0, + &HandleBuffer, + &HandleCount, + NULL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + VgaHandle = HandleBuffer[0]; + + // + // Check whether video thunk driver already starts. + // + Status = gBS->OpenProtocolInformation ( + VgaHandle, + &gEfiPciIoProtocolGuid, + &OpenInfoBuffer, + &EntryCount + ); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < EntryCount; Index++) { + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) { + Status = gBS->HandleProtocol ( + OpenInfoBuffer[Index].AgentHandle, + &gEfiLegacyBiosGuid, + (VOID **) &Interface + ); + if (!EFI_ERROR (Status)) { + // + // This should be video thunk driver which is managing video device + // So it need not start again + // + DEBUG ((EFI_D_INFO, "Video thunk driver already start! Return!\n")); + Private->VgaInstalled = TRUE; + return EFI_SUCCESS; + } + } + } + + // + // Kick off the native EFI driver + // + Status = gBS->DisconnectController ( + VgaHandle, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + if (Status != EFI_NOT_FOUND) { + return EFI_DEVICE_ERROR; + } else { + return Status; + } + } + // + // Find all the Thunk Driver + // + HandleBuffer = NULL; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiLegacyBiosGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + ASSERT_EFI_ERROR (Status); + ConnectHandleBuffer = (EFI_HANDLE *) AllocatePool (sizeof (EFI_HANDLE) * (HandleCount + 1)); + ASSERT (ConnectHandleBuffer != NULL); + + CopyMem ( + ConnectHandleBuffer, + HandleBuffer, + sizeof (EFI_HANDLE) * HandleCount + ); + ConnectHandleBuffer[HandleCount] = NULL; + + FreePool (HandleBuffer); + + // + // Enable the device and make sure VGA cycles are being forwarded to this VGA device + // + Status = gBS->HandleProtocol ( + VgaHandle, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + ASSERT_EFI_ERROR (Status); + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (PciConfigHeader) / sizeof (UINT32), + &PciConfigHeader + ); + + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | \ + EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } + + if (Status == EFI_SUCCESS) { + Private->VgaInstalled = TRUE; + + // + // Attach the VGA thunk driver. + // Assume the video is installed. This prevents potential of infinite recursion. + // + Status = gBS->ConnectController ( + VgaHandle, + ConnectHandleBuffer, + NULL, + TRUE + ); + } + + FreePool (ConnectHandleBuffer); + + if (EFI_ERROR (Status)) { + + Private->VgaInstalled = FALSE; + + // + // Reconnect the EFI VGA driver. + // + gBS->ConnectController (VgaHandle, NULL, NULL, TRUE); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Load a legacy PC-AT OpROM. + + @param This Protocol instance pointer. + @param Private Driver's private data. + @param PciHandle The EFI handle for the PCI device. It could be + NULL if the OpROM image is not associated with + any device. + @param OpromRevision The revision of PCI PC-AT ROM image. + @param RomImage Pointer to PCI PC-AT ROM image header. It must not + be NULL. + @param ImageSize Size of the PCI PC-AT ROM image. + @param RuntimeImageLength On input is the max runtime image length indicated by the PCIR structure + On output is the actual runtime image length + @param DiskStart Disk number of first device hooked by the ROM. If + DiskStart is the same as DiskEnd no disked were + hooked. + @param DiskEnd Disk number of the last device hooked by the ROM. + @param RomShadowAddress Shadow address of PC-AT ROM + + @retval EFI_SUCCESS Legacy ROM loaded for this device + @retval EFI_OUT_OF_RESOURCES No more space for this ROM + +**/ +EFI_STATUS +EFIAPI +LegacyBiosInstallRom ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN LEGACY_BIOS_INSTANCE *Private, + IN EFI_HANDLE PciHandle, + IN UINT8 OpromRevision, + IN VOID *RomImage, + IN UINTN ImageSize, + IN OUT UINTN *RuntimeImageLength, + OUT UINT8 *DiskStart, OPTIONAL + OUT UINT8 *DiskEnd, OPTIONAL + OUT VOID **RomShadowAddress OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_STATUS PciEnableStatus; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT8 LocalDiskStart; + UINT8 LocalDiskEnd; + UINTN Segment; + UINTN Bus; + UINTN Device; + UINTN Function; + EFI_IA32_REGISTER_SET Regs; + UINT8 VideoMode; + EFI_TIME BootTime; + UINT32 *BdaPtr; + UINT32 LocalTime; + UINT32 StartBbsIndex; + UINT32 EndBbsIndex; + UINTN TempData; + UINTN InitAddress; + UINTN RuntimeAddress; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + UINT32 Granularity; + + PciIo = NULL; + LocalDiskStart = 0; + LocalDiskEnd = 0; + Segment = 0; + Bus = 0; + Device = 0; + Function = 0; + VideoMode = 0; + PhysicalAddress = 0; + + PciProgramAllInterruptLineRegisters (Private); + + if ((OpromRevision >= 3) && (Private->Csm16PciInterfaceVersion >= 0x0300)) { + // + // CSM16 3.0 meets PCI 3.0 OpROM + // first test if there is enough space for its INIT code + // + PhysicalAddress = CONVENTIONAL_MEMORY_TOP; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiBootServicesCode, + EFI_SIZE_TO_PAGES (ImageSize), + &PhysicalAddress + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "return LegacyBiosInstallRom(%d): EFI_OUT_OF_RESOURCES (no more space for OpROM)\n", __LINE__)); + return EFI_OUT_OF_RESOURCES; + } + InitAddress = (UINTN) PhysicalAddress; + // + // then test if there is enough space for its RT code + // + RuntimeAddress = Private->OptionRom; + if (RuntimeAddress + *RuntimeImageLength > mEndOpromShadowAddress) { + DEBUG ((EFI_D_ERROR, "return LegacyBiosInstallRom(%d): EFI_OUT_OF_RESOURCES (no more space for OpROM)\n", __LINE__)); + gBS->FreePages (PhysicalAddress, EFI_SIZE_TO_PAGES (ImageSize)); + return EFI_OUT_OF_RESOURCES; + } + } else { + // CSM16 3.0 meets PCI 2.x OpROM + // CSM16 2.x meets PCI 2.x/3.0 OpROM + // test if there is enough space for its INIT code + // + InitAddress = PCI_START_ADDRESS (Private->OptionRom); + if (InitAddress + ImageSize > mEndOpromShadowAddress) { + DEBUG ((EFI_D_ERROR, "return LegacyBiosInstallRom(%d): EFI_OUT_OF_RESOURCES (no more space for OpROM)\n", __LINE__)); + return EFI_OUT_OF_RESOURCES; + } + + RuntimeAddress = InitAddress; + } + + Private->LegacyRegion->UnLock ( + Private->LegacyRegion, + 0xE0000, + 0x20000, + &Granularity + ); + + Private->LegacyRegion->UnLock ( + Private->LegacyRegion, + (UINT32) RuntimeAddress, + (UINT32) ImageSize, + &Granularity + ); + + DEBUG ((EFI_D_INFO, " Shadowing OpROM init/runtime/isize = %x/%x/%x\n", InitAddress, RuntimeAddress, ImageSize)); + + CopyMem ((VOID *) InitAddress, RomImage, ImageSize); + + // + // Read the highest disk number "installed: and assume a new disk will + // show up on the first drive past the current value. + // There are several considerations here: + // 1. Non-BBS compliant drives will change 40:75 but 16-bit CSM will undo + // the change until boot selection time frame. + // 2. BBS compliants drives will not change 40:75 until boot time. + // 3. Onboard IDE controllers will change 40:75 + // + LocalDiskStart = (UINT8) ((*(UINT8 *) ((UINTN) 0x475)) + 0x80); + if ((Private->Disk4075 + 0x80) < LocalDiskStart) { + // + // Update table since onboard IDE drives found + // + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciSegment = 0xff; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciBus = 0xff; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciDevice = 0xff; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciFunction = 0xff; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].StartDriveNumber = (UINT8) (Private->Disk4075 + 0x80); + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].EndDriveNumber = LocalDiskStart; + Private->LegacyEfiHddTableIndex ++; + Private->Disk4075 = (UINT8) (LocalDiskStart & 0x7f); + Private->DiskEnd = LocalDiskStart; + } + + if (PciHandle != mVgaHandle) { + + EnablePs2Keyboard (); + + // + // Store current mode settings since PrepareToScanRom may change mode. + // + VideoMode = *(UINT8 *) ((UINTN) 0x449); + } + // + // Notify the platform that we are about to scan the ROM + // + Status = Private->LegacyBiosPlatform->PlatformHooks ( + Private->LegacyBiosPlatform, + EfiPlatformHookPrepareToScanRom, + 0, + PciHandle, + &InitAddress, + NULL, + NULL + ); + + // + // If Status returned is EFI_UNSUPPORTED then abort due to platform + // policy. + // + if (Status == EFI_UNSUPPORTED) { + goto Done; + } + + // + // Report corresponding status code + // + REPORT_STATUS_CODE ( + EFI_PROGRESS_CODE, + (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_CSM_LEGACY_ROM_INIT) + ); + + // + // Generate number of ticks since midnight for BDA. Some OPROMs require + // this. Place result in 40:6C-6F + // + gRT->GetTime (&BootTime, NULL); + LocalTime = BootTime.Hour * 3600 + BootTime.Minute * 60 + BootTime.Second; + + // + // Multiply result by 18.2 for number of ticks since midnight. + // Use 182/10 to avoid floating point math. + // + LocalTime = (LocalTime * 182) / 10; + BdaPtr = (UINT32 *) ((UINTN) 0x46C); + *BdaPtr = LocalTime; + + // + // Pass in handoff data + // + PciEnableStatus = EFI_UNSUPPORTED; + ZeroMem (&Regs, sizeof (Regs)); + if (PciHandle != NULL) { + + Status = gBS->HandleProtocol ( + PciHandle, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + ASSERT_EFI_ERROR (Status); + + // + // Enable command register. + // + PciEnableStatus = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_DEVICE_ENABLE, + NULL + ); + + PciIo->GetLocation ( + PciIo, + &Segment, + &Bus, + &Device, + &Function + ); + DEBUG ((EFI_D_INFO, "Shadowing OpROM on the PCI device %x/%x/%x\n", Bus, Device, Function)); + } + + mIgnoreBbsUpdateFlag = FALSE; + Regs.X.AX = Legacy16DispatchOprom; + + // + // Generate DispatchOpRomTable data + // + Private->IntThunk->DispatchOpromTable.PnPInstallationCheckSegment = Private->Legacy16Table->PnPInstallationCheckSegment; + Private->IntThunk->DispatchOpromTable.PnPInstallationCheckOffset = Private->Legacy16Table->PnPInstallationCheckOffset; + Private->IntThunk->DispatchOpromTable.OpromSegment = (UINT16) (InitAddress >> 4); + Private->IntThunk->DispatchOpromTable.PciBus = (UINT8) Bus; + Private->IntThunk->DispatchOpromTable.PciDeviceFunction = (UINT8) ((Device << 3) | Function); + Private->IntThunk->DispatchOpromTable.NumberBbsEntries = (UINT8) Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries; + Private->IntThunk->DispatchOpromTable.BbsTablePointer = (UINT32) (UINTN) Private->BbsTablePtr; + Private->IntThunk->DispatchOpromTable.RuntimeSegment = (UINT16)((OpromRevision < 3) ? 0xffff : (RuntimeAddress >> 4)); + TempData = (UINTN) &Private->IntThunk->DispatchOpromTable; + Regs.X.ES = EFI_SEGMENT ((UINT32) TempData); + Regs.X.BX = EFI_OFFSET ((UINT32) TempData); + // + // Skip dispatching ROM for those PCI devices that can not be enabled by PciIo->Attributes + // Otherwise, it may cause the system to hang in some cases + // + if (!EFI_ERROR (PciEnableStatus)) { + DEBUG ((EFI_D_INFO, " Legacy16DispatchOprom - %02x/%02x/%02x\n", Bus, Device, Function)); + Private->LegacyBios.FarCall86 ( + &Private->LegacyBios, + Private->Legacy16CallSegment, + Private->Legacy16CallOffset, + &Regs, + NULL, + 0 + ); + } else { + Regs.X.BX = 0; + } + + if (Private->IntThunk->DispatchOpromTable.NumberBbsEntries != (UINT8) Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries) { + Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries = (UINT8) Private->IntThunk->DispatchOpromTable.NumberBbsEntries; + mIgnoreBbsUpdateFlag = TRUE; + } + // + // Check if non-BBS compliant drives found + // + if (Regs.X.BX != 0) { + LocalDiskEnd = (UINT8) (LocalDiskStart + Regs.H.BL); + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciSegment = (UINT8) Segment; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciBus = (UINT8) Bus; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciDevice = (UINT8) Device; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciFunction = (UINT8) Function; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].StartDriveNumber = Private->DiskEnd; + Private->DiskEnd = LocalDiskEnd; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].EndDriveNumber = Private->DiskEnd; + Private->LegacyEfiHddTableIndex += 1; + } + // + // Skip video mode set, if installing VGA + // + if (PciHandle != mVgaHandle) { + // + // Set mode settings since PrepareToScanRom may change mode + // + Regs.H.AH = 0x00; + Regs.H.AL = VideoMode; + Private->LegacyBios.Int86 (&Private->LegacyBios, 0x10, &Regs); + } + // + // Regs.X.AX from the adapter initializion is ignored since some adapters + // do not follow the standard of setting AX = 0 on success. + // + // + // The ROM could have updated it's size so we need to read again. + // + *RuntimeImageLength = ((EFI_LEGACY_EXPANSION_ROM_HEADER *) (RuntimeAddress))->Size512 * 512; + DEBUG ((EFI_D_INFO, " fsize = %x\n", *RuntimeImageLength)); + + // + // If OpROM runs in 2.0 mode + // + if (PhysicalAddress == 0) { + if (*RuntimeImageLength < ImageSize) { + // + // Make area from end of shadowed rom to end of original rom all ffs + // + gBS->SetMem ((VOID *) (InitAddress + *RuntimeImageLength), ImageSize - *RuntimeImageLength, 0xff); + } + } + + LocalDiskEnd = (UINT8) ((*(UINT8 *) ((UINTN) 0x475)) + 0x80); + + // + // Allow platform to perform any required actions after the + // OPROM has been initialized. + // + Status = Private->LegacyBiosPlatform->PlatformHooks ( + Private->LegacyBiosPlatform, + EfiPlatformHookAfterRomInit, + 0, + PciHandle, + &RuntimeAddress, + NULL, + NULL + ); + if (PciHandle != NULL) { + // + // If no PCI Handle then no header or Bevs. + // + if ((*RuntimeImageLength != 0) && (!mIgnoreBbsUpdateFlag)) { + StartBbsIndex = Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries; + TempData = RuntimeAddress; + UpdateBevBcvTable ( + Private, + (EFI_LEGACY_EXPANSION_ROM_HEADER *) TempData, + PciIo + ); + EndBbsIndex = Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries; + LocalDiskEnd = (UINT8) (LocalDiskStart + (UINT8) (EndBbsIndex - StartBbsIndex)); + if (LocalDiskEnd != LocalDiskStart) { + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciSegment = (UINT8) Segment; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciBus = (UINT8) Bus; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciDevice = (UINT8) Device; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciFunction = (UINT8) Function; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].StartDriveNumber = Private->DiskEnd; + Private->DiskEnd = LocalDiskEnd; + Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].EndDriveNumber = Private->DiskEnd; + Private->LegacyEfiHddTableIndex += 1; + } + } + // + // Mark PCI device as having a legacy BIOS ROM loaded. + // + RomShadow ( + PciHandle, + (UINT32) RuntimeAddress, + (UINT32) *RuntimeImageLength, + LocalDiskStart, + LocalDiskEnd + ); + } + + // + // Stuff caller's OPTIONAL return parameters. + // + if (RomShadowAddress != NULL) { + *RomShadowAddress = (VOID *) RuntimeAddress; + } + + if (DiskStart != NULL) { + *DiskStart = LocalDiskStart; + } + + if (DiskEnd != NULL) { + *DiskEnd = LocalDiskEnd; + } + + Private->OptionRom = (UINT32) (RuntimeAddress + *RuntimeImageLength); + + Status = EFI_SUCCESS; + +Done: + if (PhysicalAddress != 0) { + // + // Free pages when OpROM is 3.0 + // + gBS->FreePages (PhysicalAddress, EFI_SIZE_TO_PAGES (ImageSize)); + } + + // + // Insure all shadowed areas are locked + // + Private->LegacyRegion->Lock ( + Private->LegacyRegion, + 0xC0000, + 0x40000, + &Granularity + ); + + return Status; +} + +/** + Load a legacy PC-AT OPROM on the PciHandle device. Return information + about how many disks were added by the OPROM and the shadow address and + size. DiskStart & DiskEnd are INT 13h drive letters. Thus 0x80 is C: + + @param This Protocol instance pointer. + @param PciHandle The PCI PC-AT OPROM from this devices ROM BAR will + be loaded. This value is NULL if RomImage is + non-NULL. This is the normal case. + @param RomImage A PCI PC-AT ROM image. This argument is non-NULL + if there is no hardware associated with the ROM + and thus no PciHandle, otherwise is must be NULL. + Example is PXE base code. + @param Flags Indicates if ROM found and if PC-AT. + @param DiskStart Disk number of first device hooked by the ROM. If + DiskStart is the same as DiskEnd no disked were + hooked. + @param DiskEnd Disk number of the last device hooked by the ROM. + @param RomShadowAddress Shadow address of PC-AT ROM + @param RomShadowedSize Size of RomShadowAddress in bytes + + @retval EFI_SUCCESS Legacy ROM loaded for this device + @retval EFI_INVALID_PARAMETER PciHandle not found + @retval EFI_UNSUPPORTED There is no PCI ROM in the ROM BAR or no onboard + ROM + +**/ +EFI_STATUS +EFIAPI +LegacyBiosInstallPciRom ( + IN EFI_LEGACY_BIOS_PROTOCOL * This, + IN EFI_HANDLE PciHandle, + IN VOID **RomImage, + OUT UINTN *Flags, + OUT UINT8 *DiskStart, OPTIONAL + OUT UINT8 *DiskEnd, OPTIONAL + OUT VOID **RomShadowAddress, OPTIONAL + OUT UINT32 *RomShadowedSize OPTIONAL + ) +{ + EFI_STATUS Status; + LEGACY_BIOS_INSTANCE *Private; + VOID *LocalRomImage; + UINTN ImageSize; + UINTN RuntimeImageLength; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE01 PciConfigHeader; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN PciSegment; + UINTN PciBus; + UINTN PciDevice; + UINTN PciFunction; + UINTN LastBus; + UINTN Index; + UINT8 OpromRevision; + UINT32 Granularity; + PCI_3_0_DATA_STRUCTURE *Pcir; + + OpromRevision = 0; + + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + if (Private->Legacy16Table->LastPciBus == 0) { + // + // Get last bus number if not already found + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciIoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + + LastBus = 0; + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + if (EFI_ERROR (Status)) { + continue; + } + + Status = PciIo->GetLocation ( + PciIo, + &PciSegment, + &PciBus, + &PciDevice, + &PciFunction + ); + if (PciBus > LastBus) { + LastBus = PciBus; + } + } + + Private->LegacyRegion->UnLock ( + Private->LegacyRegion, + 0xE0000, + 0x20000, + &Granularity + ); + Private->Legacy16Table->LastPciBus = (UINT8) LastBus; + Private->LegacyRegion->Lock ( + Private->LegacyRegion, + 0xE0000, + 0x20000, + &Granularity + ); + } + + *Flags = 0; + if ((PciHandle != NULL) && (RomImage == NULL)) { + // + // If PciHandle has OpRom to Execute + // and OpRom are all associated with Hardware + // + Status = gBS->HandleProtocol ( + PciHandle, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + + if (!EFI_ERROR (Status)) { + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (PciConfigHeader) / sizeof (UINT32), + &PciConfigHeader + ); + + // + // if video installed & OPROM is video return + // + if ( + ( + ((PciConfigHeader.Hdr.ClassCode[2] == PCI_CLASS_OLD) && + (PciConfigHeader.Hdr.ClassCode[1] == PCI_CLASS_OLD_VGA)) + || + ((PciConfigHeader.Hdr.ClassCode[2] == PCI_CLASS_DISPLAY) && + (PciConfigHeader.Hdr.ClassCode[1] == PCI_CLASS_DISPLAY_VGA)) + ) + && + (!Private->VgaInstalled) + ) { + mVgaInstallationInProgress = TRUE; + + // + // return EFI_UNSUPPORTED; + // + } + } + // + // To run any legacy image, the VGA needs to be installed first. + // if installing the video, then don't need the thunk as already installed. + // + Status = Private->LegacyBiosPlatform->GetPlatformHandle ( + Private->LegacyBiosPlatform, + EfiGetPlatformVgaHandle, + 0, + &HandleBuffer, + &HandleCount, + NULL + ); + + if (!EFI_ERROR (Status)) { + mVgaHandle = HandleBuffer[0]; + if ((!Private->VgaInstalled) && (PciHandle != mVgaHandle)) { + // + // A return status of EFI_NOT_FOUND is considered valid (No EFI + // driver is controlling video. + // + mVgaInstallationInProgress = TRUE; + Status = LegacyBiosInstallVgaRom (Private); + if (EFI_ERROR (Status)) { + if (Status != EFI_NOT_FOUND) { + mVgaInstallationInProgress = FALSE; + return Status; + } + } else { + mVgaInstallationInProgress = FALSE; + } + } + } + // + // See if the option ROM for PciHandle has already been executed + // + Status = IsLegacyRom (PciHandle); + + if (!EFI_ERROR (Status)) { + mVgaInstallationInProgress = FALSE; + GetShadowedRomParameters ( + PciHandle, + DiskStart, + DiskEnd, + RomShadowAddress, + (UINTN *) RomShadowedSize + ); + return EFI_SUCCESS; + } + + Status = LegacyBiosCheckPciRomEx ( + &Private->LegacyBios, + PciHandle, + &LocalRomImage, + &ImageSize, + &RuntimeImageLength, + Flags, + &OpromRevision, + NULL + ); + if (EFI_ERROR (Status)) { + // + // There is no PCI ROM in the ROM BAR or no onboard ROM + // + mVgaInstallationInProgress = FALSE; + return EFI_UNSUPPORTED; + } + } else { + if (*RomImage == NULL) { + // + // If PciHandle is NULL, and no OpRom is to be associated + // + mVgaInstallationInProgress = FALSE; + return EFI_UNSUPPORTED; + } + + LocalRomImage = *RomImage; + Pcir = (PCI_3_0_DATA_STRUCTURE *) + ((UINT8 *) LocalRomImage + ((PCI_EXPANSION_ROM_HEADER *) LocalRomImage)->PcirOffset); + ImageSize = Pcir->ImageLength * 512; + if (Pcir->Length >= 0x1C) { + OpromRevision = Pcir->Revision; + } else { + OpromRevision = 0; + } + if (Pcir->Revision < 3) { + RuntimeImageLength = 0; + } else { + RuntimeImageLength = Pcir->MaxRuntimeImageLength * 512; + } + } + // + // Shadow and initialize the OpROM. + // + ASSERT (Private->TraceIndex < 0x200); + Private->Trace[Private->TraceIndex] = LEGACY_PCI_TRACE_000; + Private->TraceIndex ++; + Private->TraceIndex = (UINT16) (Private->TraceIndex % 0x200); + Status = LegacyBiosInstallRom ( + This, + Private, + PciHandle, + OpromRevision, + LocalRomImage, + ImageSize, + &RuntimeImageLength, + DiskStart, + DiskEnd, + RomShadowAddress + ); + if (RomShadowedSize != NULL) { + *RomShadowedSize = (UINT32) RuntimeImageLength; + } + + mVgaInstallationInProgress = FALSE; + return Status; +} + diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacySio.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacySio.c new file mode 100644 index 0000000000..f82121c607 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacySio.c @@ -0,0 +1,234 @@ +/** @file + Collect Sio information from Native EFI Drivers. + Sio is floppy, parallel, serial, ... hardware + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "LegacyBiosInterface.h" + + +/** + Collect EFI Info about legacy devices. + + @param Private Legacy BIOS Instance data + + @retval EFI_SUCCESS It should always work. + +**/ +EFI_STATUS +LegacyBiosBuildSioData ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + EFI_STATUS Status; + DEVICE_PRODUCER_DATA_HEADER *SioPtr; + DEVICE_PRODUCER_SERIAL *Sio1Ptr; + DEVICE_PRODUCER_PARALLEL *Sio2Ptr; + DEVICE_PRODUCER_FLOPPY *Sio3Ptr; + EFI_HANDLE IsaBusController; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + UINTN ResourceIndex; + UINTN ChildIndex; + EFI_ISA_IO_PROTOCOL *IsaIo; + EFI_ISA_ACPI_RESOURCE_LIST *ResourceList; + EFI_ISA_ACPI_RESOURCE *IoResource; + EFI_ISA_ACPI_RESOURCE *DmaResource; + EFI_ISA_ACPI_RESOURCE *InterruptResource; + UINTN EntryCount; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + // + // Get the pointer to the SIO data structure + // + SioPtr = &Private->IntThunk->EfiToLegacy16BootTable.SioData; + + // + // Zero the data in the SIO data structure + // + gBS->SetMem (SioPtr, sizeof (DEVICE_PRODUCER_DATA_HEADER), 0); + + // + // Find the ISA Bus Controller used for legacy + // + Status = Private->LegacyBiosPlatform->GetPlatformHandle ( + Private->LegacyBiosPlatform, + EfiGetPlatformIsaBusHandle, + 0, + &HandleBuffer, + &HandleCount, + NULL + ); + IsaBusController = HandleBuffer[0]; + if (!EFI_ERROR (Status)) { + // + // Force ISA Bus Controller to produce all ISA devices + // + gBS->ConnectController (IsaBusController, NULL, NULL, TRUE); + } + // + // Get the list of ISA controllers in the system + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiIsaIoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + // + // Collect legacy information from each of the ISA controllers in the system + // + for (Index = 0; Index < HandleCount; Index++) { + + Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiIsaIoProtocolGuid, (VOID **) &IsaIo); + if (EFI_ERROR (Status)) { + continue; + } + + ResourceList = IsaIo->ResourceList; + + if (ResourceList == NULL) { + continue; + } + // + // Collect the resource types neededto fill in the SIO data structure + // + IoResource = NULL; + DmaResource = NULL; + InterruptResource = NULL; + for (ResourceIndex = 0; + ResourceList->ResourceItem[ResourceIndex].Type != EfiIsaAcpiResourceEndOfList; + ResourceIndex++ + ) { + switch (ResourceList->ResourceItem[ResourceIndex].Type) { + case EfiIsaAcpiResourceIo: + IoResource = &ResourceList->ResourceItem[ResourceIndex]; + break; + + case EfiIsaAcpiResourceMemory: + break; + + case EfiIsaAcpiResourceDma: + DmaResource = &ResourceList->ResourceItem[ResourceIndex]; + break; + + case EfiIsaAcpiResourceInterrupt: + InterruptResource = &ResourceList->ResourceItem[ResourceIndex]; + break; + + default: + break; + } + } + // + // See if this is an ISA serial port + // + // Ignore DMA resource since it is always returned NULL + // + if (ResourceList->Device.HID == EISA_PNP_ID (0x500) || ResourceList->Device.HID == EISA_PNP_ID (0x501)) { + + if (ResourceList->Device.UID <= 3 && + IoResource != NULL && + InterruptResource != NULL + ) { + // + // Get the handle of the child device that has opened the ISA I/O Protocol + // + Status = gBS->OpenProtocolInformation ( + HandleBuffer[Index], + &gEfiIsaIoProtocolGuid, + &OpenInfoBuffer, + &EntryCount + ); + if (EFI_ERROR (Status)) { + continue; + } + // + // We want resource for legacy even if no 32-bit driver installed + // + for (ChildIndex = 0; ChildIndex < EntryCount; ChildIndex++) { + Sio1Ptr = &SioPtr->Serial[ResourceList->Device.UID]; + Sio1Ptr->Address = (UINT16) IoResource->StartRange; + Sio1Ptr->Irq = (UINT8) InterruptResource->StartRange; + Sio1Ptr->Mode = DEVICE_SERIAL_MODE_NORMAL | DEVICE_SERIAL_MODE_DUPLEX_HALF; + } + + FreePool (OpenInfoBuffer); + } + } + // + // See if this is an ISA parallel port + // + // Ignore DMA resource since it is always returned NULL, port + // only used in output mode. + // + if (ResourceList->Device.HID == EISA_PNP_ID (0x400) || ResourceList->Device.HID == EISA_PNP_ID (0x401)) { + if (ResourceList->Device.UID <= 2 && + IoResource != NULL && + InterruptResource != NULL && + DmaResource != NULL + ) { + Sio2Ptr = &SioPtr->Parallel[ResourceList->Device.UID]; + Sio2Ptr->Address = (UINT16) IoResource->StartRange; + Sio2Ptr->Irq = (UINT8) InterruptResource->StartRange; + Sio2Ptr->Dma = (UINT8) DmaResource->StartRange; + Sio2Ptr->Mode = DEVICE_PARALLEL_MODE_MODE_OUTPUT_ONLY; + } + } + // + // See if this is an ISA floppy controller + // + if (ResourceList->Device.HID == EISA_PNP_ID (0x604)) { + if (IoResource != NULL && InterruptResource != NULL && DmaResource != NULL) { + Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); + if (!EFI_ERROR (Status)) { + Sio3Ptr = &SioPtr->Floppy; + Sio3Ptr->Address = (UINT16) IoResource->StartRange; + Sio3Ptr->Irq = (UINT8) InterruptResource->StartRange; + Sio3Ptr->Dma = (UINT8) DmaResource->StartRange; + Sio3Ptr->NumberOfFloppy++; + } + } + } + // + // See if this is a mouse + // Always set mouse found so USB hot plug will work + // + // Ignore lower byte of HID. Pnp0fxx is any type of mouse. + // + // Hid = ResourceList->Device.HID & 0xff00ffff; + // PnpId = EISA_PNP_ID(0x0f00); + // if (Hid == PnpId) { + // if (ResourceList->Device.UID == 1) { + // Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSimplePointerProtocolGuid, &SimplePointer); + // if (!EFI_ERROR (Status)) { + // + SioPtr->MousePresent = 0x01; + // + // } + // } + // } + // + } + + FreePool (HandleBuffer); + + return EFI_SUCCESS; +} diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Thunk.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Thunk.c new file mode 100644 index 0000000000..c33288a2dd --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Thunk.c @@ -0,0 +1,284 @@ +/** @file + Call into 16-bit BIOS code, Use AsmThunk16 function of BaseLib. + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "LegacyBiosInterface.h" + +THUNK_CONTEXT mThunkContext; + +/** + Thunk to 16-bit real mode and execute a software interrupt with a vector + of BiosInt. Regs will contain the 16-bit register context on entry and + exit. + + @param This Protocol instance pointer. + @param BiosInt Processor interrupt vector to invoke + @param Regs Register contexted passed into (and returned) from thunk to + 16-bit mode + + @retval FALSE Thunk completed, and there were no BIOS errors in the target code. + See Regs for status. + @retval TRUE There was a BIOS erro in the target code. + +**/ +BOOLEAN +EFIAPI +LegacyBiosInt86 ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINT8 BiosInt, + IN EFI_IA32_REGISTER_SET *Regs + ) +{ + Regs->X.Flags.Reserved1 = 1; + Regs->X.Flags.Reserved2 = 0; + Regs->X.Flags.Reserved3 = 0; + Regs->X.Flags.Reserved4 = 0; + Regs->X.Flags.IOPL = 3; + Regs->X.Flags.NT = 0; + Regs->X.Flags.IF = 0; + Regs->X.Flags.TF = 0; + Regs->X.Flags.CF = 0; + + return InternalLegacyBiosFarCall ( + This, + (UINT16) (((UINT32 *)NULL)[BiosInt] >> 16), + (UINT16) ((UINT32 *)NULL)[BiosInt], + Regs, + &Regs->X.Flags, + sizeof (Regs->X.Flags) + ); +} + +/** + Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the + 16-bit register context on entry and exit. Arguments can be passed on + the Stack argument + + @param This Protocol instance pointer. + @param Segment Segemnt of 16-bit mode call + @param Offset Offset of 16-bit mdoe call + @param Regs Register contexted passed into (and returned) from + thunk to 16-bit mode + @param Stack Caller allocated stack used to pass arguments + @param StackSize Size of Stack in bytes + + @retval FALSE Thunk completed, and there were no BIOS errors in + the target code. See Regs for status. + @retval TRUE There was a BIOS erro in the target code. + +**/ +BOOLEAN +EFIAPI +LegacyBiosFarCall86 ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINT16 Segment, + IN UINT16 Offset, + IN EFI_IA32_REGISTER_SET *Regs, + IN VOID *Stack, + IN UINTN StackSize + ) +{ + Regs->X.Flags.Reserved1 = 1; + Regs->X.Flags.Reserved2 = 0; + Regs->X.Flags.Reserved3 = 0; + Regs->X.Flags.Reserved4 = 0; + Regs->X.Flags.IOPL = 3; + Regs->X.Flags.NT = 0; + Regs->X.Flags.IF = 1; + Regs->X.Flags.TF = 0; + Regs->X.Flags.CF = 0; + + return InternalLegacyBiosFarCall (This, Segment, Offset, Regs, Stack, StackSize); +} + +/** + Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the + 16-bit register context on entry and exit. Arguments can be passed on + the Stack argument + + @param This Protocol instance pointer. + @param Segment Segemnt of 16-bit mode call + @param Offset Offset of 16-bit mdoe call + @param Regs Register contexted passed into (and returned) from thunk to + 16-bit mode + @param Stack Caller allocated stack used to pass arguments + @param StackSize Size of Stack in bytes + + @retval FALSE Thunk completed, and there were no BIOS errors in the target code. + See Regs for status. + @retval TRUE There was a BIOS erro in the target code. + +**/ +BOOLEAN +EFIAPI +InternalLegacyBiosFarCall ( + IN EFI_LEGACY_BIOS_PROTOCOL *This, + IN UINT16 Segment, + IN UINT16 Offset, + IN EFI_IA32_REGISTER_SET *Regs, + IN VOID *Stack, + IN UINTN StackSize + ) +{ + UINTN Status; + LEGACY_BIOS_INSTANCE *Private; + UINT16 *Stack16; + EFI_TPL OriginalTpl; + IA32_REGISTER_SET ThunkRegSet; + BOOLEAN InterruptState; + + Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); + + ZeroMem (&ThunkRegSet, sizeof (ThunkRegSet)); + ThunkRegSet.X.DI = Regs->X.DI; + ThunkRegSet.X.SI = Regs->X.SI; + ThunkRegSet.X.BP = Regs->X.BP; + ThunkRegSet.X.BX = Regs->X.BX; + ThunkRegSet.X.DX = Regs->X.DX; + // + // Sometimes, ECX is used to pass in 32 bit data. For example, INT 1Ah, AX = B10Dh is + // "PCI BIOS v2.0c + Write Configuration DWORD" and ECX has the dword to write. + // + ThunkRegSet.E.ECX = Regs->E.ECX; + ThunkRegSet.X.AX = Regs->X.AX; + ThunkRegSet.E.DS = Regs->X.DS; + ThunkRegSet.E.ES = Regs->X.ES; + + CopyMem (&(ThunkRegSet.E.EFLAGS.UintN), &(Regs->X.Flags), sizeof (Regs->X.Flags)); + + // + // Clear the error flag; thunk code may set it. Stack16 should be the high address + // Make Statk16 address the low 16 bit must be not zero. + // + Stack16 = (UINT16 *)((UINT8 *) mThunkContext.RealModeBuffer + mThunkContext.RealModeBufferSize - sizeof (UINT16)); + + // + // Save and disable interrutp of debug timer + // + InterruptState = SaveAndSetDebugTimerInterrupt (FALSE); + + // + // The call to Legacy16 is a critical section to EFI + // + OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + if (Stack != NULL && StackSize != 0) { + // + // Copy Stack to low memory stack + // + Stack16 -= StackSize / sizeof (UINT16); + CopyMem (Stack16, Stack, StackSize); + } + + ThunkRegSet.E.SS = (UINT16) (((UINTN) Stack16 >> 16) << 12); + ThunkRegSet.E.ESP = (UINT16) (UINTN) Stack16; + ThunkRegSet.E.CS = Segment; + ThunkRegSet.E.Eip = Offset; + + mThunkContext.RealModeState = &ThunkRegSet; + + // + // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases. + // + Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259LegacyMode, NULL, NULL); + ASSERT_EFI_ERROR (Status); + + AsmThunk16 (&mThunkContext); + + // + // OPROM may allocate EBDA range by itself and change EBDA base and EBDA size. + // Get the current EBDA base address, and compared with pre-allocate minimum + // EBDA base address, if the current EBDA base address is smaller, it indicates + // PcdEbdaReservedMemorySize should be adjusted to larger for more OPROMs. + // + DEBUG_CODE ( + { + UINTN EbdaBaseAddress; + UINTN ReservedEbdaBaseAddress; + + EbdaBaseAddress = (*(UINT16 *) (UINTN) 0x40E) << 4; + ReservedEbdaBaseAddress = CONVENTIONAL_MEMORY_TOP - PcdGet32 (PcdEbdaReservedMemorySize); + ASSERT (ReservedEbdaBaseAddress <= EbdaBaseAddress); + } + ); + + if (Stack != NULL && StackSize != 0) { + // + // Copy low memory stack to Stack + // + CopyMem (Stack, Stack16, StackSize); + } + + // + // Restore protected mode interrupt state + // + Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259ProtectedMode, NULL, NULL); + ASSERT_EFI_ERROR (Status); + + mThunkContext.RealModeState = NULL; + + // + // End critical section + // + gBS->RestoreTPL (OriginalTpl); + + // + // Restore interrutp of debug timer + // + SaveAndSetDebugTimerInterrupt (InterruptState); + + Regs->E.EDI = ThunkRegSet.E.EDI; + Regs->E.ESI = ThunkRegSet.E.ESI; + Regs->E.EBP = ThunkRegSet.E.EBP; + Regs->E.EBX = ThunkRegSet.E.EBX; + Regs->E.EDX = ThunkRegSet.E.EDX; + Regs->E.ECX = ThunkRegSet.E.ECX; + Regs->E.EAX = ThunkRegSet.E.EAX; + Regs->X.SS = ThunkRegSet.E.SS; + Regs->X.CS = ThunkRegSet.E.CS; + Regs->X.DS = ThunkRegSet.E.DS; + Regs->X.ES = ThunkRegSet.E.ES; + + CopyMem (&(Regs->X.Flags), &(ThunkRegSet.E.EFLAGS.UintN), sizeof (Regs->X.Flags)); + + return (BOOLEAN) (Regs->X.Flags.CF == 1); +} + +/** + Allocate memory < 1 MB and copy the thunker code into low memory. Se up + all the descriptors. + + @param Private Private context for Legacy BIOS + + @retval EFI_SUCCESS Should only pass. + +**/ +EFI_STATUS +LegacyBiosInitializeThunk ( + IN LEGACY_BIOS_INSTANCE *Private + ) +{ + EFI_PHYSICAL_ADDRESS MemoryAddress; + + MemoryAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) Private->IntThunk; + + mThunkContext.RealModeBuffer = (VOID *) (UINTN) (MemoryAddress + ((sizeof (LOW_MEMORY_THUNK) / EFI_PAGE_SIZE) + 1) * EFI_PAGE_SIZE); + mThunkContext.RealModeBufferSize = EFI_PAGE_SIZE; + mThunkContext.ThunkAttributes = THUNK_ATTRIBUTE_BIG_REAL_MODE | THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15; + + AsmPrepareThunk16 (&mThunkContext); + + return EFI_SUCCESS; +} diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/X64/InterruptTable.S b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/X64/InterruptTable.S new file mode 100644 index 0000000000..95a0888863 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/X64/InterruptTable.S @@ -0,0 +1,72 @@ +## @file +# Interrupt Redirection Template +# +# Copyright (c) 2006, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions +# of the BSD License which accompanies this distribution. The +# full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +#text SEGMENT + + +#---------------------------------------------------------------------------- +# Procedure: InterruptRedirectionTemplate: Redirects interrupts 0x68-0x6F +# +# Input: None +# +# Output: None +# +# Prototype: VOID +# InterruptRedirectionTemplate ( +# VOID +# ); +# +# Saves: None +# +# Modified: None +# +# Description: Contains the code that is copied into low memory (below 640K). +# This code reflects interrupts 0x68-0x6f to interrupts 0x08-0x0f. +# This template must be copied into low memory, and the IDT entries +# 0x68-0x6F must be point to the low memory copy of this code. Each +# entry is 4 bytes long, so IDT entries 0x68-0x6F can be easily +# computed. +# +#---------------------------------------------------------------------------- + +ASM_GLOBAL ASM_PFX(InterruptRedirectionTemplate) +ASM_PFX(InterruptRedirectionTemplate): + int $0x08 + .byte 0x0cf # IRET + nop + int $0x09 + .byte 0x0cf # IRET + nop + int $0x0a + .byte 0x0cf # IRET + nop + int $0x0b + .byte 0x0cf # IRET + nop + int $0x0c + .byte 0x0cf # IRET + nop + int $0x0d + .byte 0x0cf # IRET + nop + int $0x0e + .byte 0x0cf # IRET + nop + int $0x0f + .byte 0x0cf # IRET + nop + +#END diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/X64/InterruptTable.asm b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/X64/InterruptTable.asm new file mode 100644 index 0000000000..750423e5b7 --- /dev/null +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/X64/InterruptTable.asm @@ -0,0 +1,71 @@ +;; @file +; Interrupt Redirection Template +; +; Copyright (c) 2006, Intel Corporation. All rights reserved.
+; +; This program and the accompanying materials +; are licensed and made available under the terms and conditions +; of the BSD License which accompanies this distribution. The +; full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +;; + +text SEGMENT + +;---------------------------------------------------------------------------- +; Procedure: InterruptRedirectionTemplate: Redirects interrupts 0x68-0x6F +; +; Input: None +; +; Output: None +; +; Prototype: VOID +; InterruptRedirectionTemplate ( +; VOID +; ); +; +; Saves: None +; +; Modified: None +; +; Description: Contains the code that is copied into low memory (below 640K). +; This code reflects interrupts 0x68-0x6f to interrupts 0x08-0x0f. +; This template must be copied into low memory, and the IDT entries +; 0x68-0x6F must be point to the low memory copy of this code. Each +; entry is 4 bytes long, so IDT entries 0x68-0x6F can be easily +; computed. +; +;---------------------------------------------------------------------------- + +InterruptRedirectionTemplate PROC + int 08h + DB 0cfh ; IRET + nop + int 09h + DB 0cfh ; IRET + nop + int 0ah + DB 0cfh ; IRET + nop + int 0bh + DB 0cfh ; IRET + nop + int 0ch + DB 0cfh ; IRET + nop + int 0dh + DB 0cfh ; IRET + nop + int 0eh + DB 0cfh ; IRET + nop + int 0fh + DB 0cfh ; IRET + nop +InterruptRedirectionTemplate ENDP + +END \ No newline at end of file diff --git a/IntelFrameworkModulePkg/Include/Guid/LegacyBios.h b/IntelFrameworkModulePkg/Include/Guid/LegacyBios.h new file mode 100644 index 0000000000..1fcea9ccfb --- /dev/null +++ b/IntelFrameworkModulePkg/Include/Guid/LegacyBios.h @@ -0,0 +1,36 @@ +/** @file + Defines a Tag GUID used to mark a UEFI legacy BIOS thunk driver based + on legacy BIOS services and legacy option ROM. This Tag GUID must be installed on + the ImageHandle of any module that follows the EFI Driver Model and uses + the Int86() or FarCall() services of the Legacy Bios Protocol to produce + a standard UEFI I/O Protocol. + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _LEGACY_BIOS_H_ +#define _LEGACY_BIOS_H_ + +/// +/// The Global ID for the Legacy BIOS GUID that must be installed onto the ImageHandle +/// of any module follows the EFI Driver Model and uses the Int86() or FarCall() +/// services of the Legacy BIOS Protocol to produce a standard UEFI I/O Protocol. +/// +#define EFI_LEGACY_BIOS_GUID \ + { \ + 0x2e3044ac, 0x879f, 0x490f, {0x97, 0x60, 0xbb, 0xdf, 0xaf, 0x69, 0x5f, 0x50 } \ + } + +extern EFI_GUID gEfiLegacyBiosGuid; + +#endif diff --git a/IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec b/IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec index 6661eaf905..15434893a3 100644 --- a/IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec +++ b/IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec @@ -56,6 +56,9 @@ ## Include/Guid/AcpiVariable.h gEfiAcpiVariableCompatiblityGuid = { 0xc020489e, 0x6db2, 0x4ef2, { 0x9a, 0xa5, 0xca, 0x6, 0xfc, 0x11, 0xd3, 0x6a }} + ## Include/Guid/LegacyBios.h + gEfiLegacyBiosGuid = { 0x2E3044AC, 0x879F, 0x490F, { 0x97, 0x60, 0xBB, 0xDF, 0xAF, 0x69, 0x5F, 0x50 }} + [Protocols] ## Vga Mini port binding for a VGA controller # Include/Protocol/VgaMiniPort.h @@ -87,6 +90,10 @@ ## This PCD specifies whether Serial device use half hand shake. gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdIsaBusSerialUseHalfHandshake|FALSE|BOOLEAN|0x00010043 + ## Indicates if CSM support is needed for ACPI S3 Save. + # If TRUE, CSM support is enclosed for ACPI S3 Save. + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformCsmSupport|TRUE|BOOLEAN|0x00010044 + [PcdsFixedAtBuild] ## FFS filename to find the default BMP Logo file. gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdLogoFile |{ 0x99, 0x8b, 0xB2, 0x7B, 0xBB, 0x61, 0xD5, 0x11, 0x9A, 0x5D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }|VOID*|0x40000003 @@ -113,8 +120,25 @@ # This PCD should be set as HII type PCD by platform integrator mapped to variable L"HwErrRecSupport" gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdHardwareErrorRecordLevel|0|UINT16|0x40000002 - [PcdsFixedAtBuild, PcdsDynamic, PcdsDynamicEx, PcdsPatchableInModule] ## I/O Base address of floppy device controller. gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdFdcBaseAddress|0x3f0|UINT16|0x30000000 + ## If TRUE, BiosVideo will switch to 80x25 Text VGA Mode when exiting boot service. + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdBiosVideoSetTextVgaModeEnable|FALSE|BOOLEAN|0x30000001 + + ## If TRUE, BiosVideo will check for VESA BIOS Extension service support. + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdBiosVideoCheckVbeEnable|TRUE|BOOLEAN|0x30000002 + + ## If TRUE, BiosVideo will check for VGA service support. + # NOTE: If both PcdBiosVideoCheckVbeEnable and PcdBiosVideoCheckVgaEnable are set to FALSE, + # that means Graphics Output protocol will not be installed, the VGA miniport protocol will be installed instead. + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdBiosVideoCheckVgaEnable|TRUE|BOOLEAN|0x30000003 + + ## If TRUE, memory space for legacy region will be set as cacheable. + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdLegacyBiosCacheLegacyRegion|TRUE|BOOLEAN|0x00000004 + + ## The PCD is used to specify memory size with bytes to reserve EBDA for OPROM. + ## The value should be a multiple of 4KB. + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdEbdaReservedMemorySize|0x8000|UINT32|0x30000005 + diff --git a/IntelFrameworkModulePkg/IntelFrameworkModulePkg.dsc b/IntelFrameworkModulePkg/IntelFrameworkModulePkg.dsc index fd2898e259..f986b4880b 100644 --- a/IntelFrameworkModulePkg/IntelFrameworkModulePkg.dsc +++ b/IntelFrameworkModulePkg/IntelFrameworkModulePkg.dsc @@ -39,11 +39,14 @@ 0|DEFAULT # The entry: 0|DEFAULT is reserved and always required. [LibraryClasses] + CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf + DebugAgentLib|MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf BaseLib|MdePkg/Library/BaseLib/BaseLib.inf SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf @@ -55,6 +58,8 @@ HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf PlatformBdsLib|IntelFrameworkModulePkg/Library/PlatformBdsLibNull/PlatformBdsLibNull.inf CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf + PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf + PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf ReportStatusCodeLib|MdePkg/Library/BaseReportStatusCodeLibNull/BaseReportStatusCodeLibNull.inf @@ -139,6 +144,11 @@ IntelFrameworkModulePkg/Bus/Isa/Ps2MouseAbsolutePointerDxe/Ps2MouseAbsolutePointerDxe.inf IntelFrameworkModulePkg/Bus/Pci/VgaMiniPortDxe/VgaMiniPortDxe.inf + IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/KeyboardDxe.inf + IntelFrameworkModulePkg/Csm/BiosThunk/VideoDxe/VideoDxe.inf + IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BlockIoDxe.inf + IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Snp16Dxe.inf + IntelFrameworkModulePkg/Universal/SectionExtractionDxe/SectionExtractionDxe.inf IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf IntelFrameworkModulePkg/Universal/DataHubStdErrDxe/DataHubStdErrDxe.inf @@ -147,6 +157,9 @@ IntelFrameworkModulePkg/Universal/BdsDxe/BdsDxe.inf IntelFrameworkModulePkg/Universal/LegacyRegionDxe/LegacyRegionDxe.inf IntelFrameworkModulePkg/Universal/StatusCode/DatahubStatusCodeHandlerDxe/DatahubStatusCodeHandlerDxe.inf + +[Components.IA32,Components.X64,Components.IPF] + IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf [Components.IA32] IntelFrameworkModulePkg/Universal/StatusCode/RuntimeDxe/StatusCodeRuntimeDxe.inf -- 2.39.2