From: qhuang8 Date: Tue, 3 Jul 2007 14:09:20 +0000 (+0000) Subject: Add DebugPort & DebugSupport drivers X-Git-Tag: edk2-stable201903~22937 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=c1f23d63363d36947e76df61320bdd2e5e233946 Add DebugPort & DebugSupport drivers git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3018 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index cbcc245460..77290bc3ea 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -297,6 +297,7 @@ PcdPeiPcdDatabaseGetSizeEnabled|gEfiEdkModulePkgTokenSpaceGuid|TRUE PcdPeiPcdDatabaseCallbackOnSetEnabled|gEfiEdkModulePkgTokenSpaceGuid|TRUE PcdPeiPcdDatabaseExEnabled|gEfiEdkModulePkgTokenSpaceGuid|TRUE + PcdNtEmulatorEnable|gEfiEdkModulePkgTokenSpaceGuid|FALSE # PcdStatusCodeUseOEM|gEfiIntelFrameworkModulePkgTokenSpaceGuid|FALSE @@ -362,6 +363,8 @@ ${WORKSPACE}/MdeModulePkg/Universal/WatchDogTimerDxe/WatchDogTimer.inf ${WORKSPACE}/MdeModulePkg/Universal/VariablePei/Variable.inf ${WORKSPACE}/MdeModulePkg/Universal/VariableRuntimeDxe/Variable.inf + ${WORKSPACE}/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.inf + ${WORKSPACE}/MdeModulePkg/Universal/DebugPortDxe/DebugPort.inf ${WORKSPACE}/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.inf ${WORKSPACE}/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.inf $(WORKSPACE)/MdeModulePkg/Universal/PCD/Dxe/Pcd.inf diff --git a/MdeModulePkg/Universal/DebugPortDxe/ComponentName.c b/MdeModulePkg/Universal/DebugPortDxe/ComponentName.c new file mode 100644 index 0000000000..078bdab54d --- /dev/null +++ b/MdeModulePkg/Universal/DebugPortDxe/ComponentName.c @@ -0,0 +1,108 @@ +/*++ + +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. + +Module Name: + ComponentName.c + +Abstract: + Component name protocol member functions for DebugPort... + +--*/ + +#include "DebugPort.h" + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gDebugPortComponentName = { + DebugPortComponentNameGetDriverName, + DebugPortComponentNameGetControllerName, + "eng" +}; + +static EFI_UNICODE_STRING_TABLE mDebugPortDriverNameTable[] = { + { + "eng", + (CHAR16 *) L"DebugPort Driver" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +DebugPortComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that 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. + DriverName - 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. + + Returns: + EFI_SUCCESS - The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gDebugPortComponentName.SupportedLanguages, + mDebugPortDriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +DebugPortComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + + Routine Description: + The debug port driver does not support GetControllerName, so this function + is just stubbed and returns EFI_UNSUPPORTED. + + Arguments: + Per EFI 1.10 driver model + + Returns: + EFI_UNSUPPORTED + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Universal/DebugPortDxe/DebugPort.c b/MdeModulePkg/Universal/DebugPortDxe/DebugPort.c new file mode 100644 index 0000000000..92c9d61c36 --- /dev/null +++ b/MdeModulePkg/Universal/DebugPortDxe/DebugPort.c @@ -0,0 +1,827 @@ +/*++ + +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. + +Module Name: + + DebugPort.c + +Abstract: + + Top level C file for debugport driver. Contains initialization function. + This driver layers on top of SerialIo. + + ALL CODE IN THE SERIALIO STACK MUST BE RE-ENTRANT AND CALLABLE FROM + INTERRUPT CONTEXT. + +Revision History + +--*/ + + +#include "DebugPort.h" + +// +// Misc. functions local to this module.. +// +STATIC +VOID +GetDebugPortVariable ( + DEBUGPORT_DEVICE *DebugPortDevice + ) +/*++ + +Routine Description: + Local worker function to obtain device path information from DebugPort variable. + Records requested settings in DebugPort device structure. + +Arguments: + DEBUGPORT_DEVICE *DebugPortDevice, + +Returns: + + Nothing + +--*/ +{ + UINTN DataSize; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_STATUS Status; + + DataSize = 0; + + Status = gRT->GetVariable ( + (CHAR16 *) EFI_DEBUGPORT_VARIABLE_NAME, + &gEfiDebugPortVariableGuid, + NULL, + &DataSize, + DebugPortDevice->DebugPortVariable + ); + + if (Status == EFI_BUFFER_TOO_SMALL) { + if (gDebugPortDevice->DebugPortVariable != NULL) { + FreePool (gDebugPortDevice->DebugPortVariable); + } + + DebugPortDevice->DebugPortVariable = AllocatePool (DataSize); + if (DebugPortDevice->DebugPortVariable != NULL) { + gRT->GetVariable ( + (CHAR16 *) EFI_DEBUGPORT_VARIABLE_NAME, + &gEfiDebugPortVariableGuid, + NULL, + &DataSize, + DebugPortDevice->DebugPortVariable + ); + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) DebugPortDevice->DebugPortVariable; + while (!EfiIsDevicePathEnd (DevicePath) && !EfiIsUartDevicePath (DevicePath)) { + DevicePath = EfiNextDevicePathNode (DevicePath); + } + + if (EfiIsDevicePathEnd (DevicePath)) { + FreePool (gDebugPortDevice->DebugPortVariable); + DebugPortDevice->DebugPortVariable = NULL; + } else { + CopyMem ( + &DebugPortDevice->BaudRate, + &((UART_DEVICE_PATH *) DevicePath)->BaudRate, + sizeof (((UART_DEVICE_PATH *) DevicePath)->BaudRate) + ); + DebugPortDevice->ReceiveFifoDepth = DEBUGPORT_UART_DEFAULT_FIFO_DEPTH; + DebugPortDevice->Timeout = DEBUGPORT_UART_DEFAULT_TIMEOUT; + CopyMem ( + &DebugPortDevice->Parity, + &((UART_DEVICE_PATH *) DevicePath)->Parity, + sizeof (((UART_DEVICE_PATH *) DevicePath)->Parity) + ); + CopyMem ( + &DebugPortDevice->DataBits, + &((UART_DEVICE_PATH *) DevicePath)->DataBits, + sizeof (((UART_DEVICE_PATH *) DevicePath)->DataBits) + ); + CopyMem ( + &DebugPortDevice->StopBits, + &((UART_DEVICE_PATH *) DevicePath)->StopBits, + sizeof (((UART_DEVICE_PATH *) DevicePath)->StopBits) + ); + } + } + } +} + +// +// Globals +// + +EFI_DRIVER_BINDING_PROTOCOL gDebugPortDriverBinding = { + DebugPortSupported, + DebugPortStart, + DebugPortStop, + DEBUGPORT_DRIVER_VERSION, + NULL, + NULL +}; + +DEBUGPORT_DEVICE *gDebugPortDevice; + +// +// implementation code +// + +EFI_STATUS +EFIAPI +InitializeDebugPortDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + Driver entry point. Reads DebugPort variable to determine what device and settings + to use as the debug port. Binds exclusively to SerialIo. Reverts to defaults \ + if no variable is found. + + Creates debugport and devicepath protocols on new handle. + +Arguments: + ImageHandle, + SystemTable + +Returns: + + EFI_UNSUPPORTED + EFI_OUT_OF_RESOURCES + +--*/ +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &gDebugPortDriverBinding, + ImageHandle, + &gDebugPortComponentName, + NULL, + NULL + ); + ASSERT_EFI_ERROR (Status); + // + // Allocate and Initialize dev structure + // + gDebugPortDevice = AllocateZeroPool (sizeof (DEBUGPORT_DEVICE)); + if (gDebugPortDevice == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Fill in static and default pieces of device structure first. + // + gDebugPortDevice->Signature = DEBUGPORT_DEVICE_SIGNATURE; + + gDebugPortDevice->DebugPortInterface.Reset = DebugPortReset; + gDebugPortDevice->DebugPortInterface.Read = DebugPortRead; + gDebugPortDevice->DebugPortInterface.Write = DebugPortWrite; + gDebugPortDevice->DebugPortInterface.Poll = DebugPortPoll; + + gDebugPortDevice->BaudRate = DEBUGPORT_UART_DEFAULT_BAUDRATE; + gDebugPortDevice->ReceiveFifoDepth = DEBUGPORT_UART_DEFAULT_FIFO_DEPTH; + gDebugPortDevice->Timeout = DEBUGPORT_UART_DEFAULT_TIMEOUT; + gDebugPortDevice->Parity = (EFI_PARITY_TYPE) DEBUGPORT_UART_DEFAULT_PARITY; + gDebugPortDevice->DataBits = DEBUGPORT_UART_DEFAULT_DATA_BITS; + gDebugPortDevice->StopBits = (EFI_STOP_BITS_TYPE) DEBUGPORT_UART_DEFAULT_STOP_BITS; + + return EFI_SUCCESS; +} +// +// DebugPort driver binding member functions... +// +EFI_STATUS +EFIAPI +DebugPortSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +/*++ + +Routine Description: + Checks to see that there's not already a DebugPort interface somewhere. If so, + fail. + + If there's a DEBUGPORT variable, the device path must match exactly. If there's + no DEBUGPORT variable, then device path is not checked and does not matter. + + Checks to see that there's a serial io interface on the controller handle + that can be bound BY_DRIVER | EXCLUSIVE. + + If all these tests succeed, then we return EFI_SUCCESS, else, EFI_UNSUPPORTED + or other error returned by OpenProtocol. + +Arguments: + This + ControllerHandle + RemainingDevicePath + +Returns: + EFI_UNSUPPORTED + EFI_OUT_OF_RESOURCES + EFI_SUCCESS + +--*/ +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *Dp1; + EFI_DEVICE_PATH_PROTOCOL *Dp2; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + EFI_DEBUGPORT_PROTOCOL *DebugPortInterface; + EFI_HANDLE TempHandle; + + // + // Check to see that there's not a debugport protocol already published + // + if (gBS->LocateProtocol (&gEfiDebugPortProtocolGuid, NULL, (VOID **) &DebugPortInterface) != EFI_NOT_FOUND) { + return EFI_UNSUPPORTED; + } + // + // Read DebugPort variable to determine debug port selection and parameters + // + GetDebugPortVariable (gDebugPortDevice); + + if (gDebugPortDevice->DebugPortVariable != NULL) { + // + // There's a DEBUGPORT variable, so do LocateDevicePath and check to see if + // the closest matching handle matches the controller handle, and if it does, + // check to see that the remaining device path has the DebugPort GUIDed messaging + // device path only. Otherwise, it's a mismatch and EFI_UNSUPPORTED is returned. + // + Dp1 = DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL *) gDebugPortDevice->DebugPortVariable); + if (Dp1 == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Dp2 = Dp1; + + Status = gBS->LocateDevicePath ( + &gEfiSerialIoProtocolGuid, + &Dp2, + &TempHandle + ); + + if (Status == EFI_SUCCESS && TempHandle != ControllerHandle) { + Status = EFI_UNSUPPORTED; + } + + if (Status == EFI_SUCCESS && (Dp2->Type != 3 || Dp2->SubType != 10 || *((UINT16 *) Dp2->Length) != 20)) { + Status = EFI_UNSUPPORTED; + } + + if (Status == EFI_SUCCESS && CompareMem (&gEfiDebugPortDevicePathGuid, Dp2 + 1, sizeof (EFI_GUID))) { + Status = EFI_UNSUPPORTED; + } + + FreePool (Dp1); + if (EFI_ERROR (Status)) { + return Status; + } + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +DebugPortStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +/*++ + +Routine Description: + Binds exclusively to serial io on the controller handle. Produces DebugPort + protocol and DevicePath on new handle. + +Arguments: + This + ControllerHandle + RemainingDevicePath + +Returns: + EFI_OUT_OF_RESOURCES + EFI_SUCCESS +--*/ +{ + EFI_STATUS Status; + DEBUGPORT_DEVICE_PATH DebugPortDP; + EFI_DEVICE_PATH_PROTOCOL EndDP; + EFI_DEVICE_PATH_PROTOCOL *Dp1; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &gDebugPortDevice->SerialIoBinding, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gDebugPortDevice->SerialIoDeviceHandle = ControllerHandle; + + // + // Initialize the Serial Io interface... + // + Status = gDebugPortDevice->SerialIoBinding->SetAttributes ( + gDebugPortDevice->SerialIoBinding, + gDebugPortDevice->BaudRate, + gDebugPortDevice->ReceiveFifoDepth, + gDebugPortDevice->Timeout, + gDebugPortDevice->Parity, + gDebugPortDevice->DataBits, + gDebugPortDevice->StopBits + ); + if (EFI_ERROR (Status)) { + gDebugPortDevice->BaudRate = 0; + gDebugPortDevice->Parity = DefaultParity; + gDebugPortDevice->DataBits = 0; + gDebugPortDevice->StopBits = DefaultStopBits; + gDebugPortDevice->ReceiveFifoDepth = 0; + Status = gDebugPortDevice->SerialIoBinding->SetAttributes ( + gDebugPortDevice->SerialIoBinding, + gDebugPortDevice->BaudRate, + gDebugPortDevice->ReceiveFifoDepth, + gDebugPortDevice->Timeout, + gDebugPortDevice->Parity, + gDebugPortDevice->DataBits, + gDebugPortDevice->StopBits + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + return Status; + } + } + + gDebugPortDevice->SerialIoBinding->Reset (gDebugPortDevice->SerialIoBinding); + + // + // Create device path instance for DebugPort + // + DebugPortDP.Header.Type = MESSAGING_DEVICE_PATH; + DebugPortDP.Header.SubType = MSG_VENDOR_DP; + SetDevicePathNodeLength (&(DebugPortDP.Header), sizeof (DebugPortDP)); + CopyMem (&DebugPortDP.Guid, &gEfiDebugPortDevicePathGuid, sizeof (EFI_GUID)); + + Dp1 = DevicePathFromHandle (ControllerHandle); + if (Dp1 == NULL) { + Dp1 = &EndDP; + SetDevicePathEndNode (Dp1); + } + + gDebugPortDevice->DebugPortDevicePath = AppendDevicePathNode (Dp1, (EFI_DEVICE_PATH_PROTOCOL *) &DebugPortDP); + if (gDebugPortDevice->DebugPortDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Publish DebugPort and Device Path protocols + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &gDebugPortDevice->DebugPortDeviceHandle, + &gEfiDevicePathProtocolGuid, + gDebugPortDevice->DebugPortDevicePath, + &gEfiDebugPortProtocolGuid, + &gDebugPortDevice->DebugPortInterface, + NULL + ); + + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + return Status; + } + // + // Connect debugport child to serial io + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &gDebugPortDevice->SerialIoBinding, + This->DriverBindingHandle, + gDebugPortDevice->DebugPortDeviceHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR (Status)) { + DEBUG_CODE_BEGIN (); + UINTN BufferSize; + + BufferSize = 48; + DebugPortWrite ( + &gDebugPortDevice->DebugPortInterface, + 0, + &BufferSize, + "DebugPort driver failed to open child controller\n\n" + ); + DEBUG_CODE_END (); + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + return Status; + } + + DEBUG_CODE_BEGIN (); + UINTN BufferSize; + + BufferSize = 38; + DebugPortWrite ( + &gDebugPortDevice->DebugPortInterface, + 0, + &BufferSize, + "Hello World from the DebugPort driver\n\n" + ); + DEBUG_CODE_END (); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +DebugPortStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +/*++ + +Routine Description: + We're never intending to be stopped via the driver model so this just returns + EFI_UNSUPPORTED + +Arguments: + Per EFI 1.10 driver model + +Returns: + EFI_UNSUPPORTED + EFI_SUCCESS + +--*/ +{ + EFI_STATUS Status; + + if (NumberOfChildren == 0) { + // + // Close the bus driver + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + gDebugPortDevice->SerialIoBinding = NULL; + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + FreePool (gDebugPortDevice->DebugPortDevicePath); + + return EFI_SUCCESS; + } else { + // + // Disconnect SerialIo child handle + // + Status = gBS->CloseProtocol ( + gDebugPortDevice->SerialIoDeviceHandle, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + gDebugPortDevice->DebugPortDeviceHandle + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Unpublish our protocols (DevicePath, DebugPort) + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + gDebugPortDevice->DebugPortDeviceHandle, + &gEfiDevicePathProtocolGuid, + gDebugPortDevice->DebugPortDevicePath, + &gEfiDebugPortProtocolGuid, + &gDebugPortDevice->DebugPortInterface, + NULL + ); + + if (EFI_ERROR (Status)) { + gBS->OpenProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &gDebugPortDevice->SerialIoBinding, + This->DriverBindingHandle, + gDebugPortDevice->DebugPortDeviceHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + gDebugPortDevice->DebugPortDeviceHandle = NULL; + } + } + + return Status; +} +// +// Debugport protocol member functions +// +EFI_STATUS +EFIAPI +DebugPortReset ( + IN EFI_DEBUGPORT_PROTOCOL *This + ) +/*++ + +Routine Description: + DebugPort protocol member function. Calls SerialIo:GetControl to flush buffer. + We cannot call SerialIo:SetAttributes because it uses pool services, which use + locks, which affect TPL, so it's not interrupt context safe or re-entrant. + SerialIo:Reset() calls SetAttributes, so it can't be used either. + + The port itself should be fine since it was set up during initialization. + +Arguments: + This + +Returns: + + EFI_SUCCESS + +--*/ +{ + UINTN BufferSize; + UINTN BitBucket; + + while (This->Poll (This) == EFI_SUCCESS) { + BufferSize = 1; + This->Read (This, 0, &BufferSize, &BitBucket); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +DebugPortRead ( + IN EFI_DEBUGPORT_PROTOCOL *This, + IN UINT32 Timeout, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +/*++ + +Routine Description: + DebugPort protocol member function. Calls SerialIo:Read() after setting + if it's different than the last SerialIo access. + +Arguments: + IN EFI_DEBUGPORT_PROTOCOL *This + IN UINT32 Timeout, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + +Returns: + + EFI_STATUS + +--*/ +{ + DEBUGPORT_DEVICE *DebugPortDevice; + UINTN LocalBufferSize; + EFI_STATUS Status; + UINT8 *BufferPtr; + + DebugPortDevice = DEBUGPORT_DEVICE_FROM_THIS (This); + BufferPtr = Buffer; + LocalBufferSize = *BufferSize; + do { + Status = DebugPortDevice->SerialIoBinding->Read ( + DebugPortDevice->SerialIoBinding, + &LocalBufferSize, + BufferPtr + ); + if (Status == EFI_TIMEOUT) { + if (Timeout > DEBUGPORT_UART_DEFAULT_TIMEOUT) { + Timeout -= DEBUGPORT_UART_DEFAULT_TIMEOUT; + } else { + Timeout = 0; + } + } else if (EFI_ERROR (Status)) { + break; + } + + BufferPtr += LocalBufferSize; + LocalBufferSize = *BufferSize - (BufferPtr - (UINT8 *) Buffer); + } while (LocalBufferSize != 0 && Timeout > 0); + + *BufferSize = (UINTN) (BufferPtr - (UINT8 *) Buffer); + + return Status; +} + +EFI_STATUS +EFIAPI +DebugPortWrite ( + IN EFI_DEBUGPORT_PROTOCOL *This, + IN UINT32 Timeout, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +/*++ + +Routine Description: + DebugPort protocol member function. Calls SerialIo:Write() Writes 8 bytes at + a time and does a GetControl between 8 byte writes to help insure reads are + interspersed This is poor-man's flow control.. + +Arguments: + This - Pointer to DebugPort protocol + Timeout - Timeout value + BufferSize - On input, the size of Buffer. + On output, the amount of data actually written. + Buffer - Pointer to buffer to write + +Returns: + EFI_SUCCESS - The data was written. + EFI_DEVICE_ERROR - The device reported an error. + EFI_TIMEOUT - The data write was stopped due to a timeout. + +--*/ +{ + DEBUGPORT_DEVICE *DebugPortDevice; + UINTN Position; + UINTN WriteSize; + EFI_STATUS Status; + UINT32 SerialControl; + + Status = EFI_SUCCESS; + DebugPortDevice = DEBUGPORT_DEVICE_FROM_THIS (This); + + WriteSize = 8; + for (Position = 0; Position < *BufferSize && !EFI_ERROR (Status); Position += WriteSize) { + DebugPortDevice->SerialIoBinding->GetControl ( + DebugPortDevice->SerialIoBinding, + &SerialControl + ); + if (*BufferSize - Position < 8) { + WriteSize = *BufferSize - Position; + } + + Status = DebugPortDevice->SerialIoBinding->Write ( + DebugPortDevice->SerialIoBinding, + &WriteSize, + &((UINT8 *) Buffer)[Position] + ); + } + + *BufferSize = Position; + return Status; +} + +EFI_STATUS +EFIAPI +DebugPortPoll ( + IN EFI_DEBUGPORT_PROTOCOL *This + ) +/*++ + +Routine Description: + DebugPort protocol member function. Calls SerialIo:Write() after setting + if it's different than the last SerialIo access. + +Arguments: + IN EFI_DEBUGPORT_PROTOCOL *This + +Returns: + EFI_SUCCESS - At least 1 character is ready to be read from the DebugPort interface + EFI_NOT_READY - There are no characters ready to read from the DebugPort interface + EFI_DEVICE_ERROR - A hardware failure occured... (from SerialIo) + +--*/ +{ + EFI_STATUS Status; + UINT32 SerialControl; + DEBUGPORT_DEVICE *DebugPortDevice; + + DebugPortDevice = DEBUGPORT_DEVICE_FROM_THIS (This); + + Status = DebugPortDevice->SerialIoBinding->GetControl ( + DebugPortDevice->SerialIoBinding, + &SerialControl + ); + + if (!EFI_ERROR (Status)) { + if (SerialControl & EFI_SERIAL_INPUT_BUFFER_EMPTY) { + Status = EFI_NOT_READY; + } else { + Status = EFI_SUCCESS; + } + } + + return Status; +} + +EFI_STATUS +EFIAPI +ImageUnloadHandler ( + EFI_HANDLE ImageHandle + ) +/*++ + +Routine Description: + Unload function that is registered in the LoadImage protocol. It un-installs + protocols produced and deallocates pool used by the driver. Called by the core + when unloading the driver. + +Arguments: + EFI_HANDLE ImageHandle + +Returns: + + EFI_SUCCESS + +--*/ +{ + EFI_STATUS Status; + + if (gDebugPortDevice->SerialIoBinding != NULL) { + return EFI_ABORTED; + } + + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gDebugPortDevice->DriverBindingInterface, + &gEfiComponentNameProtocolGuid, + &gDebugPortDevice->ComponentNameInterface, + NULL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Clean up allocations + // + if (gDebugPortDevice->DebugPortVariable != NULL) { + FreePool (gDebugPortDevice->DebugPortVariable); + } + + FreePool (gDebugPortDevice); + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/DebugPortDxe/DebugPort.h b/MdeModulePkg/Universal/DebugPortDxe/DebugPort.h new file mode 100644 index 0000000000..58a1629cd5 --- /dev/null +++ b/MdeModulePkg/Universal/DebugPortDxe/DebugPort.h @@ -0,0 +1,201 @@ +/*++ + +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. + +Module Name: + DebugPort.h + +Abstract: + Definitions and prototypes for DebugPort driver + +--*/ + +#ifndef __DEBUGPORT_H__ +#define __DEBUGPORT_H__ + +// +// The package level header files this module uses +// +#include +// +// The protocols, PPI and GUID defintions for this module +// +#include +#include +#include +#include +#include +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Driver Binding Externs +// +extern EFI_DRIVER_BINDING_PROTOCOL gDebugPortDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gDebugPortComponentName; + +// +// local type definitions +// +#define DEBUGPORT_DEVICE_SIGNATURE EFI_SIGNATURE_32 ('D', 'B', 'G', 'P') + +// +// Device structure used by driver +// +typedef struct { + UINT32 Signature; + EFI_HANDLE DriverBindingHandle; + EFI_HANDLE DebugPortDeviceHandle; + VOID *DebugPortVariable; + + EFI_DRIVER_BINDING_PROTOCOL DriverBindingInterface; + EFI_COMPONENT_NAME_PROTOCOL ComponentNameInterface; + EFI_DEVICE_PATH_PROTOCOL *DebugPortDevicePath; + EFI_DEBUGPORT_PROTOCOL DebugPortInterface; + + EFI_HANDLE SerialIoDeviceHandle; + EFI_SERIAL_IO_PROTOCOL *SerialIoBinding; + UINT64 BaudRate; + UINT32 ReceiveFifoDepth; + UINT32 Timeout; + EFI_PARITY_TYPE Parity; + UINT8 DataBits; + EFI_STOP_BITS_TYPE StopBits; +} DEBUGPORT_DEVICE; + +#define DEBUGPORT_DEVICE_FROM_THIS(a) CR (a, DEBUGPORT_DEVICE, DebugPortInterface, DEBUGPORT_DEVICE_SIGNATURE) + +#define EFI_ACPI_PC_COMPORT_HID EISA_PNP_ID (0x0500) +#define EFI_ACPI_16550UART_HID EISA_PNP_ID (0x0501) + +#define DEBUGPORT_UART_DEFAULT_BAUDRATE 115200 +#define DEBUGPORT_UART_DEFAULT_PARITY 0 +#define DEBUGPORT_UART_DEFAULT_FIFO_DEPTH 16 +#define DEBUGPORT_UART_DEFAULT_TIMEOUT 50000 // 5 ms +#define DEBUGPORT_UART_DEFAULT_DATA_BITS 8 +#define DEBUGPORT_UART_DEFAULT_STOP_BITS 1 + +#define DEBUGPORT_DRIVER_VERSION 1 + +#define EfiIsUartDevicePath(dp) (DevicePathType (dp) == MESSAGING_DEVICE_PATH && DevicePathSubType (dp) == MSG_UART_DP) + +// +// globals +// +extern DEBUGPORT_DEVICE *gDebugPortDevice; + +// +// Driver binding interface functions... +// +EFI_STATUS +DebugPortEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +; + +EFI_STATUS +EFIAPI +DebugPortSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +; + +EFI_STATUS +EFIAPI +DebugPortStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +; + +EFI_STATUS +EFIAPI +DebugPortStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +; + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +DebugPortComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +; + +EFI_STATUS +EFIAPI +DebugPortComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +; + +// +// DebugPort member functions +// +EFI_STATUS +EFIAPI +DebugPortReset ( + IN EFI_DEBUGPORT_PROTOCOL *This + ) +; + +EFI_STATUS +EFIAPI +DebugPortRead ( + IN EFI_DEBUGPORT_PROTOCOL *This, + IN UINT32 Timeout, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +; + +EFI_STATUS +EFIAPI +DebugPortWrite ( + IN EFI_DEBUGPORT_PROTOCOL *This, + IN UINT32 Timeout, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +; + +EFI_STATUS +EFIAPI +DebugPortPoll ( + IN EFI_DEBUGPORT_PROTOCOL *This + ) +; + +#endif diff --git a/MdeModulePkg/Universal/DebugPortDxe/DebugPort.inf b/MdeModulePkg/Universal/DebugPortDxe/DebugPort.inf new file mode 100644 index 0000000000..86c8b09b5b --- /dev/null +++ b/MdeModulePkg/Universal/DebugPortDxe/DebugPort.inf @@ -0,0 +1,111 @@ +#/** @file +# Component description file for DebugPort module. +# +# This driver binds exclusively to serial io on the controller handle, +# and initializes serial Io interface, publishs DebugPort and device path Protocol. +# Copyright (c) 2006 - 2007, 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 Section - statements that will be processed to create a Makefile. +# +################################################################################ +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugPort + FILE_GUID = 73E9457A-CEA1-4917-9A9C-9F1F0F0FD322 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = InitializeDebugPortDriver + UNLOAD_IMAGE = ImageUnloadHandler + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gDebugPortDriverBinding +# COMPONENT_NAME = gDebugPortComponentName +# Variable Guid C Name: gEfiDebugPortProtocolGuid Variable Name: L"DEBUGPORT" +# +# + +################################################################################ +# +# Sources Section - list of files that are required for the build to succeed. +# +################################################################################ + +[Sources.common] + ComponentName.c + DebugPort.c + DebugPort.h + + +################################################################################ +# +# Package Dependency Section - list of Package files that are required for +# this module. +# +################################################################################ + +[Packages] + MdePkg/MdePkg.dec + + +################################################################################ +# +# Library Class Section - list of Library Classes that are required for +# this module. +# +################################################################################ + +[LibraryClasses] + DevicePathLib + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + UefiDriverEntryPoint + DebugLib + + +################################################################################ +# +# Protocol C Name Section - list of Protocol and Protocol Notify C Names +# that this module uses or produces. +# +################################################################################ + +[Protocols] + gEfiDriverBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiComponentNameProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiSerialIoProtocolGuid # PROTOCOL TO_START + gEfiDevicePathProtocolGuid # PROTOCOL BY_START + gEfiDebugPortProtocolGuid # PROTOCOL BY_START + + +################################################################################ +# +# Dependency Expression Section - list of Dependency expressions that are required for +# this module. +# +################################################################################ + +[Depex] + TRUE + diff --git a/MdeModulePkg/Universal/DebugPortDxe/DebugPort.msa b/MdeModulePkg/Universal/DebugPortDxe/DebugPort.msa new file mode 100644 index 0000000000..16743bc7b2 --- /dev/null +++ b/MdeModulePkg/Universal/DebugPortDxe/DebugPort.msa @@ -0,0 +1,100 @@ + + + + DebugPort + UEFI_DRIVER + 73E9457A-CEA1-4917-9A9C-9F1F0F0FD322 + 1.0 + Component description file for DebugPort module. + This driver binds exclusively to serial io on the controller handle, + and initializes serial Io interface, publishs DebugPort and device path Protocol. + Copyright (c) 2006 - 2007, 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. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + DebugPort + + + + DebugLib + + + UefiDriverEntryPoint + + + UefiLib + + + BaseMemoryLib + + + MemoryAllocationLib + + + UefiBootServicesTableLib + + + UefiRuntimeServicesTableLib + + + DevicePathLib + + + + DebugPort.h + DebugPort.c + ComponentName.c + DebugPort.dxs + + + + + + + gEfiDebugPortProtocolGuid + + + gEfiDevicePathProtocolGuid + + + gEfiSerialIoProtocolGuid + + + gEfiComponentNameProtocolGuid + + + gEfiDriverBindingProtocolGuid + + + + + 0x0044 0x0045 0x0042 0x0055 0x0047 0x0050 0x004F 0x0052 0x0054 + gEfiDebugPortProtocolGuid + L"DEBUGPORT" variable is specified. gEfiDebugPortVariableGuid is + one virtual guid, which can't be described in this section. + So gEfiDebugPortProtocolGuid name is instead of it. + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + InitializeDebugPortDriver + + + ImageUnloadHandler + + + gDebugPortDriverBinding + gDebugPortComponentName + + + diff --git a/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.c b/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.c new file mode 100644 index 0000000000..a0ec6c5934 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.c @@ -0,0 +1,151 @@ +/*++ + +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. + +Module Name: + + DebugSupport.c + +Abstract: + + Top level C file for debug support driver. Contains initialization function. + +Revision History + +--*/ + +// +// private header files +// +#include "plDebugSupport.h" + +// +// This is a global that is the actual interface +// +EFI_DEBUG_SUPPORT_PROTOCOL gDebugSupportProtocolInterface = { + EFI_ISA, + GetMaximumProcessorIndex, + RegisterPeriodicCallback, + RegisterExceptionCallback, + InvalidateInstructionCache +}; + +// +// Driver Entry Point +// +EFI_STATUS +InitializeDebugSupportDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + Driver entry point. Checks to see there's not already a DebugSupport protocol + installed for the selected processor before installing protocol. + +Arguments: + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + +Returns: + + EFI_STATUS + +--*/ +// TODO: ImageHandle - add argument and description to function comment +// TODO: SystemTable - add argument and description to function comment +{ + EFI_LOADED_IMAGE_PROTOCOL *LoadedImageProtocolPtr; + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_HANDLE *HandlePtr; + UINTN NumHandles; + EFI_DEBUG_SUPPORT_PROTOCOL *DebugSupportProtocolPtr; + + // + // Install Protocol Interface... + // + // First check to see that the debug support protocol for this processor + // type is not already installed + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiDebugSupportProtocolGuid, + NULL, + &NumHandles, + &HandlePtr + ); + + if (Status != EFI_NOT_FOUND) { + do { + NumHandles--; + Status = gBS->OpenProtocol ( + HandlePtr[NumHandles], + &gEfiDebugSupportProtocolGuid, + (VOID **) &DebugSupportProtocolPtr, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (Status == EFI_SUCCESS && DebugSupportProtocolPtr->Isa == EFI_ISA) { + FreePool (HandlePtr); + Status = EFI_ALREADY_STARTED; + goto ErrExit; + } + } while (NumHandles > 0); + FreePool (HandlePtr); + } + + // + // Get our image information and install platform specific unload handler + // + Status = gBS->OpenProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **) &LoadedImageProtocolPtr, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT (!EFI_ERROR (Status)); + if (Status != EFI_SUCCESS) { + goto ErrExit; + } + + LoadedImageProtocolPtr->Unload = plUnloadDebugSupportDriver; + + // + // Call hook for platform specific initialization + // + Status = plInitializeDebugSupportDriver (); + ASSERT (!EFI_ERROR (Status)); + if (Status != EFI_SUCCESS) { + goto ErrExit; + } + + // + // Install DebugSupport protocol to new handle + // + Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &Handle, + &gEfiDebugSupportProtocolGuid, + EFI_NATIVE_INTERFACE, + &gDebugSupportProtocolInterface + ); + ASSERT (!EFI_ERROR (Status)); + if (Status != EFI_SUCCESS) { + goto ErrExit; + } + +ErrExit: + return Status; +} diff --git a/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.inf b/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.inf new file mode 100644 index 0000000000..005539bed0 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.inf @@ -0,0 +1,133 @@ +#/** @file +# Component description file for DebugSupport module. +# +# This driver installs DebugSupport protocol for the selected processor. +# Copyright (c) 2006 - 2007, 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 Section - statements that will be processed to create a Makefile. +# +################################################################################ +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugSupport + FILE_GUID = 911D584C-35F7-4955-BEF9-B452769DDC3A + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = InitializeDebugSupportDriver + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +################################################################################ +# +# Sources Section - list of files that are required for the build to succeed. +# +################################################################################ + +[Sources.common] + DebugSupport.c + +[Sources.Ia32] + Ia32/plDebugSupport.c + Ia32/plDebugSupport.h + Ia32/AsmFuncs.S + Ia32/AsmFuncs.asm + +[Sources.X64] + x64/plDebugSupport.c + x64/plDebugSupport.h + x64/AsmFuncs.S + x64/AsmFuncs.asm + +[Sources.IPF] + ipf/plDebugSupport.h + ipf/plDebugSupport.c + ipf/Ds64Macros.i + ipf/common.i + ipf/AsmFuncs.s + + +################################################################################ +# +# Package Dependency Section - list of Package files that are required for +# this module. +# +################################################################################ + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +################################################################################ +# +# Library Class Section - list of Library Classes that are required for +# this module. +# +################################################################################ + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiDriverEntryPoint + DebugLib + +[LibraryClasses.IA32] + PcdLib + BaseLib + +[LibraryClasses.X64] + BaseLib + + +################################################################################ +# +# Protocol C Name Section - list of Protocol and Protocol Notify C Names +# that this module uses or produces. +# +################################################################################ + +[Protocols] + gEfiLoadedImageProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiDebugSupportProtocolGuid # PROTOCOL SOMETIMES_PRODUCED + + +################################################################################ +# +# Pcd FEATURE_FLAG - list of PCDs that this module is coded for. +# +################################################################################ + +[PcdsFeatureFlag.IA32] + PcdNtEmulatorEnable|gEfiEdkModulePkgTokenSpaceGuid + + +################################################################################ +# +# Dependency Expression Section - list of Dependency expressions that are required for +# this module. +# +################################################################################ + +[Depex] + TRUE + diff --git a/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.msa b/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.msa new file mode 100644 index 0000000000..979705363b --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/DebugSupport.msa @@ -0,0 +1,90 @@ + + + + DebugSupport + DXE_DRIVER + 911D584C-35F7-4955-BEF9-B452769DDC3A + 1.0 + Component description file for DebugSupport module. + This driver installs DebugSupport protocol for the selected processor. + Copyright (c) 2006 - 2007, 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. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF + false + DebugSupport + + + + DebugLib + + + UefiDriverEntryPoint + + + BaseMemoryLib + + + MemoryAllocationLib + + + UefiBootServicesTableLib + + + BaseLib + + + PcdLib + + + + DebugSupport.c + DebugSupport.dxs + Ia32/AsmFuncs.asm + Ia32/AsmFuncs.S + Ia32/plDebugSupport.h + Ia32/plDebugSupport.c + ipf/AsmFuncs.s + ipf/common.i + ipf/Ds64Macros.i + ipf/plDebugSupport.c + ipf/plDebugSupport.h + x64/AsmFuncs.asm + x64/AsmFuncs.S + x64/plDebugSupport.h + x64/plDebugSupport.c + + + + + + + + gEfiDebugSupportProtocolGuid + + + gEfiLoadedImageProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + InitializeDebugSupportDriver + + + + + PcdNtEmulatorEnable + gEfiEdkModulePkgTokenSpaceGuid + If this PCD is set as TRUE, NT emulator will be endabled. + + + diff --git a/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.S b/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.S new file mode 100644 index 0000000000..0dddb3b550 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.S @@ -0,0 +1,270 @@ +#****************************************************************************** +#* +#* 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. +#* +#****************************************************************************** + +.globl ASM_PFX(OrigVector) +.globl ASM_PFX(InterruptEntryStub) +.globl ASM_PFX(StubSize) +.globl ASM_PFX(CommonIdtEntry) +.globl ASM_PFX(FxStorSupport) + +ASM_PFX(AppEsp): .long 0x11111111 # ? +ASM_PFX(DebugEsp): .long 0x22222222 # ? +ASM_PFX(ExtraPush): .long 0x33333333 # ? +ASM_PFX(ExceptData): .long 0x44444444 # ? +ASM_PFX(Eflags): .long 0x55555555 # ? +ASM_PFX(OrigVector): .long 0x66666666 # ? +ASM_PFX(StubSize): .long ASM_PFX(InterruptEntryStubEnd) - ASM_PFX(InterruptEntryStub) + +.globl ASM_PFX(FxStorSupport) +ASM_PFX(FxStorSupport): + push %ebx + mov $0x1,%eax + cpuid + mov %edx,%eax + and $0x1000000,%eax + shr $0x18,%eax + pop %ebx + ret + +.globl ASM_PFX(GetIdtr) +ASM_PFX(GetIdtr): + push %ebp + mov %esp,%ebp + add $0xfffffff8,%esp + sidtl 0xfffffffa(%ebp) + mov 0xfffffffc(%ebp),%eax + leave + ret + +.globl ASM_PFX(WriteInterruptFlag) +ASM_PFX(WriteInterruptFlag): + push %ebp + mov %esp,%ebp + pushf + pop %eax + and $0x200,%eax + shr $0x9,%eax + mov 0x8(%ebp),%ecx + or %cl,%cl + jne ASM_PFX(WriteInterruptFlag+0x17) + cli + jmp ASM_PFX(WriteInterruptFlag+0x18) + sti + leave + ret + +.globl ASM_PFX(Vect2Desc) +ASM_PFX(Vect2Desc): + push %ebp + mov %esp,%ebp + mov 0xc(%ebp),%eax + mov 0x8(%ebp),%ecx + mov %ax,(%ecx) + movw $0x20,0x2(%ecx) + movw $0x8e00,0x4(%ecx) + shr $0x10,%eax + mov %ax,0x6(%ecx) + leave + ret + +.globl ASM_PFX(InterruptEntryStub) +ASM_PFX(InterruptEntryStub): + mov %esp,0x0 + mov $0x0,%esp + push $0x0 + jmp ASM_PFX(CommonIdtEntry) +.globl ASM_PFX(InterruptEntryStubEnd) +ASM_PFX(InterruptEntryStubEnd): + +.globl ASM_PFX(CommonIdtEntry) +ASM_PFX(CommonIdtEntry): + pusha + pushf + pop %eax + mov %eax,0x0 + cmpl $0x8,0x0 + jne ASM_PFX(CommonIdtEntry+0x20) + movl $0x1,0x0 + jmp ASM_PFX(CommonIdtEntry+0xa8) + cmpl $0xa,0x0 + jne ASM_PFX(CommonIdtEntry+0x35) + movl $0x1,0x0 + jmp ASM_PFX(CommonIdtEntry+0xa8) + cmpl $0xb,0x0 + jne ASM_PFX(CommonIdtEntry+0x4a) + movl $0x1,0x0 + jmp ASM_PFX(CommonIdtEntry+0xa8) + cmpl $0xc,0x0 + jne ASM_PFX(CommonIdtEntry+0x5f) + movl $0x1,0x0 + jmp ASM_PFX(CommonIdtEntry+0xa8) + cmpl $0xd,0x0 + jne ASM_PFX(CommonIdtEntry+0x74) + movl $0x1,0x0 + jmp ASM_PFX(CommonIdtEntry+0xa8) + cmpl $0xe,0x0 + jne ASM_PFX(CommonIdtEntry+0x89) + movl $0x1,0x0 + jmp ASM_PFX(CommonIdtEntry+0xa8) + cmpl $0x11,0x0 + jne ASM_PFX(CommonIdtEntry+0x9e) + movl $0x1,0x0 + jmp ASM_PFX(CommonIdtEntry+0xa8) + movl $0x0,0x0 + cmpl $0x1,0x0 + jne ASM_PFX(CommonIdtEntry+0xc8) + mov 0x0,%eax + mov (%eax),%ebx + mov %ebx,0x0 + add $0x4,%eax + mov %eax,0x0 + jmp ASM_PFX(CommonIdtEntry+0xd2) + movl $0x0,0x0 + mov 0xc(%esp),%eax + mov %eax,0x0 + mov 0x0,%eax + add $0xc,%eax + mov %eax,0xc(%esp) + mov %ss,%eax + push %eax + mov 0x0,%eax + movzwl 0x4(%eax),%eax + push %eax + mov %ds,%eax + push %eax + mov %es,%eax + push %eax + mov %fs,%eax + push %eax + mov %gs,%eax + push %eax + mov 0x0,%eax + pushl (%eax) + push $0x0 + push $0x0 + sidtl (%esp) + push $0x0 + push $0x0 + sgdtl (%esp) + xor %eax,%eax + str %eax + push %eax + sldt %eax + push %eax + mov 0x0,%eax + pushl 0x8(%eax) + mov %cr4,%eax + or $0x208,%eax + mov %eax,%cr4 + push %eax + mov %cr3,%eax + push %eax + mov %cr2,%eax + push %eax + push $0x0 + mov %cr0,%eax + push %eax + mov %db7,%eax + push %eax + xor %eax,%eax + mov %eax,%db7 + mov %db6,%eax + push %eax + xor %eax,%eax + mov %eax,%db6 + mov %db3,%eax + push %eax + mov %db2,%eax + push %eax + mov %db1,%eax + push %eax + mov %db0,%eax + push %eax + sub $0x200,%esp + mov %esp,%edi + fxsave (%edi) + mov 0x0,%eax + push %eax + mov %esp,%eax + push %eax + mov 0x0,%eax + push %eax + call ASM_PFX(CommonIdtEntry+0x184) + add $0x8,%esp + add $0x4,%esp + mov %esp,%esi + fxrstor (%esi) + add $0x200,%esp + pop %eax + mov %eax,%db0 + pop %eax + mov %eax,%db1 + pop %eax + mov %eax,%db2 + pop %eax + mov %eax,%db3 + add $0x4,%esp + pop %eax + mov %eax,%db7 + pop %eax + mov %eax,%cr0 + add $0x4,%esp + pop %eax + mov %eax,%cr2 + pop %eax + mov %eax,%cr3 + pop %eax + mov %eax,%cr4 + mov 0x0,%eax + popl 0x8(%eax) + add $0x18,%esp + popl (%eax) + pop %gs + pop %fs + pop %es + pop %ds + popl 0x4(%eax) + pop %ss + mov 0xc(%esp),%ebx + mov 0x0,%eax + add $0xc,%eax + cmp %eax,%ebx + je ASM_PFX(CommonIdtEntry+0x202) + mov 0x0,%eax + mov (%eax),%ecx + mov %ecx,(%ebx) + mov 0x4(%eax),%ecx + mov %ecx,0x4(%ebx) + mov 0x8(%eax),%ecx + mov %ecx,0x8(%ebx) + mov %ebx,%eax + mov %eax,0x0 + mov 0x0,%eax + mov %eax,0xc(%esp) + cmpl $0x68,0x0 + jne PhonyIretd+0xd + mov 0x0,%eax + mov 0x8(%eax),%ebx + and $0xfffffcff,%ebx + push %ebx + push %cs + push $0x0 + iret + +PhonyIretd: + popa + mov 0x0,%esp + jmp *0x0 + popa + mov 0x0,%esp + iret diff --git a/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.asm b/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.asm new file mode 100644 index 0000000000..28a43cfe63 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/Ia32/AsmFuncs.asm @@ -0,0 +1,548 @@ +;****************************************************************************** +;* +;* 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. +;* +;****************************************************************************** + +.586p +.MODEL FLAT, C + +EXCPT32_DIVIDE_ERROR EQU 0 +EXCPT32_DEBUG EQU 1 +EXCPT32_NMI EQU 2 +EXCPT32_BREAKPOINT EQU 3 +EXCPT32_OVERFLOW EQU 4 +EXCPT32_BOUND EQU 5 +EXCPT32_INVALID_OPCODE EQU 6 +EXCPT32_DOUBLE_FAULT EQU 8 +EXCPT32_INVALID_TSS EQU 10 +EXCPT32_SEG_NOT_PRESENT EQU 11 +EXCPT32_STACK_FAULT EQU 12 +EXCPT32_GP_FAULT EQU 13 +EXCPT32_PAGE_FAULT EQU 14 +EXCPT32_FP_ERROR EQU 16 +EXCPT32_ALIGNMENT_CHECK EQU 17 +EXCPT32_MACHINE_CHECK EQU 18 +EXCPT32_SIMD EQU 19 + +FXSTOR_FLAG EQU 01000000h ; bit cpuid 24 of feature flags + +;; The FXSTOR and FXRSTOR commands are used for saving and restoring the x87, +;; MMX, SSE, SSE2, etc registers. The initialization of the debugsupport driver +;; MUST check the CPUID feature flags to see that these instructions are available +;; and fail to init if they are not. + +;; fxstor [edi] +FXSTOR_EDI MACRO + db 0fh, 0aeh, 00000111y ; mod = 00, reg/op = 000, r/m = 111 = [edi] +ENDM + +;; fxrstor [esi] +FXRSTOR_ESI MACRO + db 0fh, 0aeh, 00001110y ; mod = 00, reg/op = 001, r/m = 110 = [esi] +ENDM +.DATA + +public OrigVector, InterruptEntryStub, StubSize, CommonIdtEntry, FxStorSupport + +StubSize dd InterruptEntryStubEnd - InterruptEntryStub +AppEsp dd 11111111h ; ? +DebugEsp dd 22222222h ; ? +ExtraPush dd 33333333h ; ? +ExceptData dd 44444444h ; ? +Eflags dd 55555555h ; ? +OrigVector dd 66666666h ; ? + +;; The declarations below define the memory region that will be used for the debug stack. +;; The context record will be built by pushing register values onto this stack. +;; It is imparitive that alignment be carefully managed, since the FXSTOR and +;; FXRSTOR instructions will GP fault if their memory operand is not 16 byte aligned. +;; +;; The stub will switch stacks from the application stack to the debuger stack +;; and pushes the exception number. +;; +;; Then we building the context record on the stack. Since the stack grows down, +;; we push the fields of the context record from the back to the front. There +;; are 132 bytes of stack used prior allocating the 512 bytes of stack to be +;; used as the memory buffer for the fxstor instruction. Therefore address of +;; the buffer used for the FXSTOR instruction is &Eax - 132 - 512, which +;; must be 16 byte aligned. +;; +;; We carefully locate the stack to make this happen. +;; +;; For reference, the context structure looks like this: +;; struct { +;; UINT32 ExceptionData; +;; FX_SAVE_STATE_IA32 FxSaveState; // 512 bytes, must be 16 byte aligned +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; +;; UINT32 EFlags; +;; UINT32 Ldtr, Tr; +;; UINT32 Gdtr[2], Idtr[2]; +;; UINT32 Eip; +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; +;; } SYSTEM_CONTEXT_IA32; // 32 bit system context record + + +align 16 +DebugStackEnd db "DbgStkEnd >>>>>>" ;; 16 byte long string - must be 16 bytes to preserve alignment + dd 1ffdh dup (000000000h) ;; 32K should be enough stack + ;; This allocation is coocked to insure + ;; that the the buffer for the FXSTORE instruction + ;; will be 16 byte aligned also. + ;; +ExceptionNumber dd ? ;; first entry will be the vector number pushed by the stub + +DebugStackBegin db "<<<< DbgStkBegin" ;; initial debug ESP == DebugStackBegin, set in stub + +.CODE + +externdef InterruptDistrubutionHub:near + +;------------------------------------------------------------------------------ +; BOOLEAN +; FxStorSupport ( +; void +; ) +; +; Abstract: Returns TRUE if FxStor instructions are supported +; +FxStorSupport PROC C PUBLIC + +; +; cpuid corrupts ebx which must be preserved per the C calling convention +; + push ebx + mov eax, 1 + cpuid + mov eax, edx + and eax, FXSTOR_FLAG + shr eax, 24 + pop ebx + ret +FxStorSupport ENDP + + +;------------------------------------------------------------------------------ +; DESCRIPTOR * +; GetIdtr ( +; void +; ) +; +; Abstract: Returns physical address of IDTR +; +GetIdtr PROC C PUBLIC + LOCAL IdtrBuf:FWORD + + sidt IdtrBuf + mov eax, DWORD PTR IdtrBuf + 2 + ret +GetIdtr ENDP + + +;------------------------------------------------------------------------------ +; BOOLEAN +; WriteInterruptFlag ( +; BOOLEAN NewState +; ) +; +; Abstract: Programs interrupt flag to the requested state and returns previous +; state. +; +WriteInterruptFlag PROC C PUBLIC State:DWORD + + pushfd + pop eax + and eax, 200h + shr eax, 9 + mov ecx, State + .IF cl == 0 + cli + .ELSE + sti + .ENDIF + ret + +WriteInterruptFlag ENDP + + + +;------------------------------------------------------------------------------ +; void +; Vect2Desc ( +; DESCRIPTOR * DestDesc, +; void (*Vector) (void) +; ) +; +; Abstract: Encodes an IDT descriptor with the given physical address +; +Vect2Desc PROC C PUBLIC DestPtr:DWORD, Vector:DWORD + + mov eax, Vector + mov ecx, DestPtr + mov word ptr [ecx], ax ; write bits 15..0 of offset + mov dx, cs + mov word ptr [ecx+2], dx ; SYS_CODE_SEL from GDT + mov word ptr [ecx+4], 0e00h OR 8000h ; type = 386 interrupt gate, present + shr eax, 16 + mov word ptr [ecx+6], ax ; write bits 31..16 of offset + + ret + +Vect2Desc ENDP + + + +;------------------------------------------------------------------------------ +; InterruptEntryStub +; +; Abstract: This code is not a function, but is a small piece of code that is +; copied and fixed up once for each IDT entry that is hooked. +; +InterruptEntryStub:: + mov AppEsp, esp ; save stack top + mov esp, offset DebugStackBegin ; switch to debugger stack + push 0 ; push vector number - will be modified before installed + db 0e9h ; jump rel32 + dd 0 ; fixed up to relative address of CommonIdtEntry +InterruptEntryStubEnd: + + + +;------------------------------------------------------------------------------ +; CommonIdtEntry +; +; Abstract: This code is not a function, but is the common part for all IDT +; vectors. +; +CommonIdtEntry:: +;; +;; At this point, the stub has saved the current application stack esp into AppEsp +;; and switched stacks to the debug stack, where it pushed the vector number +;; +;; The application stack looks like this: +;; +;; ... +;; (last application stack entry) +;; eflags from interrupted task +;; CS from interrupted task +;; EIP from interrupted task +;; Error code <-------------------- Only present for some exeption types +;; +;; + + +;; The stub switched us to the debug stack and pushed the interrupt number. +;; +;; Next, construct the context record. It will be build on the debug stack by +;; pushing the registers in the correct order so as to create the context structure +;; on the debug stack. The context record must be built from the end back to the +;; beginning because the stack grows down... +; +;; For reference, the context record looks like this: +;; +;; typedef +;; struct { +;; UINT32 ExceptionData; +;; FX_SAVE_STATE_IA32 FxSaveState; +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; UINT32 Cr0, Cr2, Cr3, Cr4; +;; UINT32 EFlags; +;; UINT32 Ldtr, Tr; +;; UINT32 Gdtr[2], Idtr[2]; +;; UINT32 Eip; +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; +;; } SYSTEM_CONTEXT_IA32; // 32 bit system context record + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + pushad + +;; Save interrupt state eflags register... + pushfd + pop eax + mov dword ptr Eflags, eax + +;; We need to determine if any extra data was pushed by the exception, and if so, save it +;; To do this, we check the exception number pushed by the stub, and cache the +;; result in a variable since we'll need this again. + .IF ExceptionNumber == EXCPT32_DOUBLE_FAULT + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_INVALID_TSS + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_SEG_NOT_PRESENT + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_STACK_FAULT + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_GP_FAULT + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_PAGE_FAULT + mov ExtraPush, 1 + .ELSEIF ExceptionNumber == EXCPT32_ALIGNMENT_CHECK + mov ExtraPush, 1 + .ELSE + mov ExtraPush, 0 + .ENDIF + +;; If there's some extra data, save it also, and modify the saved AppEsp to effectively +;; pop this value off the application's stack. + .IF ExtraPush == 1 + mov eax, AppEsp + mov ebx, [eax] + mov ExceptData, ebx + add eax, 4 + mov AppEsp, eax + .ELSE + mov ExceptData, 0 + .ENDIF + +;; The "pushad" above pushed the debug stack esp. Since what we're actually doing +;; is building the context record on the debug stack, we need to save the pushed +;; debug ESP, and replace it with the application's last stack entry... + mov eax, [esp + 12] + mov DebugEsp, eax + mov eax, AppEsp + add eax, 12 + ; application stack has eflags, cs, & eip, so + ; last actual application stack entry is + ; 12 bytes into the application stack. + mov [esp + 12], eax + +;; continue building context record +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + mov eax, ss + push eax + + ; CS from application is one entry back in application stack + mov eax, AppEsp + movzx eax, word ptr [eax + 4] + push eax + + mov eax, ds + push eax + mov eax, es + push eax + mov eax, fs + push eax + mov eax, gs + push eax + +;; UINT32 Eip; + ; Eip from application is on top of application stack + mov eax, AppEsp + push dword ptr [eax] + +;; UINT32 Gdtr[2], Idtr[2]; + push 0 + push 0 + sidt fword ptr [esp] + push 0 + push 0 + sgdt fword ptr [esp] + +;; UINT32 Ldtr, Tr; + xor eax, eax + str ax + push eax + sldt ax + push eax + +;; UINT32 EFlags; +;; Eflags from application is two entries back in application stack + mov eax, AppEsp + push dword ptr [eax + 8] + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; +;; insure FXSAVE/FXRSTOR is enabled in CR4... +;; ... while we're at it, make sure DE is also enabled... + mov eax, cr4 + or eax, 208h + mov cr4, eax + push eax + mov eax, cr3 + push eax + mov eax, cr2 + push eax + push 0 + mov eax, cr0 + push eax + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov eax, dr7 + push eax +;; clear Dr7 while executing debugger itself + xor eax, eax + mov dr7, eax + + mov eax, dr6 + push eax +;; insure all status bits in dr6 are clear... + xor eax, eax + mov dr6, eax + + mov eax, dr3 + push eax + mov eax, dr2 + push eax + mov eax, dr1 + push eax + mov eax, dr0 + push eax + +;; FX_SAVE_STATE_IA32 FxSaveState; + sub esp, 512 + mov edi, esp + ; IMPORTANT!! The debug stack has been carefully constructed to + ; insure that esp and edi are 16 byte aligned when we get here. + ; They MUST be. If they are not, a GP fault will occur. + FXSTOR_EDI + +;; UINT32 ExceptionData; + mov eax, ExceptData + push eax + +; call to C code which will in turn call registered handler +; pass in the vector number + mov eax, esp + push eax + mov eax, ExceptionNumber + push eax + call InterruptDistrubutionHub + add esp, 8 + +; restore context... +;; UINT32 ExceptionData; + add esp, 4 + +;; FX_SAVE_STATE_IA32 FxSaveState; + mov esi, esp + FXRSTOR_ESI + add esp, 512 + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + pop eax + mov dr0, eax + pop eax + mov dr1, eax + pop eax + mov dr2, eax + pop eax + mov dr3, eax +;; skip restore of dr6. We cleared dr6 during the context save. + add esp, 4 + pop eax + mov dr7, eax + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + pop eax + mov cr0, eax + add esp, 4 + pop eax + mov cr2, eax + pop eax + mov cr3, eax + pop eax + mov cr4, eax + +;; UINT32 EFlags; + mov eax, AppEsp + pop dword ptr [eax + 8] + +;; UINT32 Ldtr, Tr; +;; UINT32 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add esp, 24 + +;; UINT32 Eip; + pop dword ptr [eax] + +;; UINT32 SegGs, SegFs, SegEs, SegDs, SegCs, SegSs; +;; NOTE - modified segment registers could hang the debugger... We +;; could attempt to insulate ourselves against this possibility, +;; but that poses risks as well. +;; + + pop gs + pop fs + pop es + pop ds + pop [eax + 4] + pop ss + +;; The next stuff to restore is the general purpose registers that were pushed +;; using the "pushad" instruction. +;; +;; The value of ESP as stored in the context record is the application ESP +;; including the 3 entries on the application stack caused by the exception +;; itself. It may have been modified by the debug agent, so we need to +;; determine if we need to relocate the application stack. + + mov ebx, [esp + 12] ; move the potentially modified AppEsp into ebx + mov eax, AppEsp + add eax, 12 + cmp ebx, eax + je NoAppStackMove + + mov eax, AppEsp + mov ecx, [eax] ; EIP + mov [ebx], ecx + + mov ecx, [eax + 4] ; CS + mov [ebx + 4], ecx + + mov ecx, [eax + 8] ; EFLAGS + mov [ebx + 8], ecx + + mov eax, ebx ; modify the saved AppEsp to the new AppEsp + mov AppEsp, eax +NoAppStackMove: + mov eax, DebugEsp ; restore the DebugEsp on the debug stack + ; so our "popad" will not cause a stack switch + mov [esp + 12], eax + + cmp ExceptionNumber, 068h + jne NoChain + +Chain: + +;; Restore eflags so when we chain, the flags will be exactly as if we were never here. +;; We gin up the stack to do an iretd so we can get ALL the flags. + mov eax, AppEsp + mov ebx, [eax + 8] + and ebx, NOT 300h ; special handling for IF and TF + push ebx + push cs + push PhonyIretd + iretd +PhonyIretd: + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + popad + +;; Switch back to application stack + mov esp, AppEsp + +;; Jump to original handler + jmp OrigVector + +NoChain: +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + popad + +;; Switch back to application stack + mov esp, AppEsp + +;; We're outa here... + iretd +END + + + diff --git a/MdeModulePkg/Universal/DebugSupportDxe/Ia32/plDebugSupport.c b/MdeModulePkg/Universal/DebugSupportDxe/Ia32/plDebugSupport.c new file mode 100644 index 0000000000..1eaa5ec591 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/Ia32/plDebugSupport.c @@ -0,0 +1,449 @@ +/**@file + IA32 specific debug support functions + +Copyright (c) 2006 - 2007, 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. + +**/ + +// +// private header files +// +#include "plDebugSupport.h" + +// +// This the global main table to keep track of the interrupts +// +IDT_ENTRY *IdtEntryTable = NULL; +DESCRIPTOR NullDesc = 0; + +STATIC +EFI_STATUS +CreateEntryStub ( + IN EFI_EXCEPTION_TYPE ExceptionType, + OUT VOID **Stub + ) +/*++ + +Routine Description: Allocate pool for a new IDT entry stub. Copy the generic + stub into the new buffer and fixup the vector number and jump target address. + +Arguments: + ExceptionType - This is the exception type that the new stub will be created + for. + Stub - On successful exit, *Stub contains the newly allocated entry stub. +Returns: + Typically EFI_SUCCESS + other possibilities are passed through from AllocatePool + +--*/ +{ + UINT8 *StubCopy; + + StubCopy = *Stub; + + // + // Fixup the stub code for this vector + // + + // The stub code looks like this: + // + // 00000000 89 25 00000004 R mov AppEsp, esp ; save stack top + // 00000006 BC 00008014 R mov esp, offset DbgStkBot ; switch to debugger stack + // 0000000B 6A 00 push 0 ; push vector number - will be modified before installed + // 0000000D E9 db 0e9h ; jump rel32 + // 0000000E 00000000 dd 0 ; fixed up to relative address of CommonIdtEntry + // + + // + // poke in the exception type so the second push pushes the exception type + // + StubCopy[0x0c] = (UINT8) ExceptionType; + + // + // fixup the jump target to point to the common entry + // + *(UINT32 *) &StubCopy[0x0e] = (UINT32) CommonIdtEntry - (UINT32) &StubCopy[StubSize]; + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +HookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN VOID (*NewCallback) () + ) +/*++ + +Routine Description: + Creates a nes entry stub. Then saves the current IDT entry and replaces it + with an interrupt gate for the new entry point. The IdtEntryTable is updated + with the new registered function. + + This code executes in boot services context. The stub entry executes in interrupt + context. + +Arguments: + ExceptionType - specifies which vector to hook. + NewCallback - a pointer to the new function to be registered. + +Returns: + EFI_SUCCESS + Other possibilities are passed through by CreateEntryStub + +--*/ +{ + BOOLEAN OldIntFlagState; + EFI_STATUS Status; + + Status = CreateEntryStub (ExceptionType, (VOID **) &IdtEntryTable[ExceptionType].StubEntry); + if (Status == EFI_SUCCESS) { + OldIntFlagState = WriteInterruptFlag (0); + ReadIdt (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc)); + + ((UINT16 *) &IdtEntryTable[ExceptionType].OrigVector)[0] = ((UINT16 *) &IdtEntryTable[ExceptionType].OrigDesc)[0]; + ((UINT16 *) &IdtEntryTable[ExceptionType].OrigVector)[1] = ((UINT16 *) &IdtEntryTable[ExceptionType].OrigDesc)[3]; + + Vect2Desc (&IdtEntryTable[ExceptionType].NewDesc, IdtEntryTable[ExceptionType].StubEntry); + IdtEntryTable[ExceptionType].RegisteredCallback = NewCallback; + WriteIdt (ExceptionType, &(IdtEntryTable[ExceptionType].NewDesc)); + WriteInterruptFlag (OldIntFlagState); + } + + return Status; +} + +STATIC +EFI_STATUS +UnhookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType + ) +/*++ + +Routine Description: + Undoes HookEntry. This code executes in boot services context. + +Arguments: + ExceptionType - specifies which entry to unhook + +Returns: + EFI_SUCCESS + +--*/ +{ + BOOLEAN OldIntFlagState; + + OldIntFlagState = WriteInterruptFlag (0); + WriteIdt (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc)); + WriteInterruptFlag (OldIntFlagState); + + return EFI_SUCCESS; +} + +EFI_STATUS +ManageIdtEntryTable ( + VOID (*NewCallback)(), + EFI_EXCEPTION_TYPE ExceptionType + ) +/*++ + +Routine Description: + This is the main worker function that manages the state of the interrupt + handlers. It both installs and uninstalls interrupt handlers based on the + value of NewCallback. If NewCallback is NULL, then uninstall is indicated. + If NewCallback is non-NULL, then install is indicated. + +Arguments: + NewCallback - If non-NULL, NewCallback specifies the new handler to register. + If NULL, specifies that the previously registered handler should + be uninstalled. + ExceptionType - Indicates which entry to manage + +Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has + no handler registered for it + EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered. + + Other possible return values are passed through from UnHookEntry and HookEntry. + +--*/ +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (FeaturePcdGet (PcdNtEmulatorEnable)) { + if (CompareDescriptor (&IdtEntryTable[ExceptionType].NewDesc, &NullDesc)) { + // + // we've already installed to this vector + // + if (NewCallback != NULL) { + // + // if the input handler is non-null, error + // + Status = EFI_ALREADY_STARTED; + } else { + Status = UnhookEntry (ExceptionType); + } + } else { + // + // no user handler installed on this vector + // + if (NewCallback == NULL) { + // + // if the input handler is null, error + // + Status = EFI_INVALID_PARAMETER; + } else { + Status = HookEntry (ExceptionType, NewCallback); + } + } + } + + return Status; +} + +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ) +/*++ + +Routine Description: This is a DebugSupport protocol member function. + +Arguments: + This - The DebugSupport instance + MaxProcessorIndex - The maximuim supported processor index + +Returns: + Always returns EFI_SUCCESS with *MaxProcessorIndex set to 0 + +--*/ +{ + *MaxProcessorIndex = 0; + return (EFI_SUCCESS); +} + +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ) +/*++ + +Routine Description: This is a DebugSupport protocol member function. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + PeriodicCallback - Callback function + +Returns: + + EFI_SUCCESS + EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has + no handler registered for it + EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered. + + Other possible return values are passed through from UnHookEntry and HookEntry. + +--*/ +{ + return ManageIdtEntryTable (PeriodicCallback, SYSTEM_TIMER_VECTOR); +} + +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK NewCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ) +/*++ + +Routine Description: + This is a DebugSupport protocol member function. + + This code executes in boot services context. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + NewCallback - Callback function + ExceptionType - Which exception to hook + +Returns: + + EFI_SUCCESS + EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has + no handler registered for it + EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered. + + Other possible return values are passed through from UnHookEntry and HookEntry. + +--*/ +{ + return ManageIdtEntryTable (NewCallback, ExceptionType); +} + +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINT64 Length + ) +/*++ + +Routine Description: + This is a DebugSupport protocol member function. + Calls assembly routine to flush cache. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + Start - Physical base of the memory range to be invalidated + Length - mininum number of bytes in instruction cache to invalidate + +Returns: + + EFI_SUCCESS - always return success + +--*/ +{ + AsmWbinvd (); + return EFI_SUCCESS; +} + +EFI_STATUS +plInitializeDebugSupportDriver ( + VOID + ) +/*++ + +Routine Description: + Initializes driver's handler registration database. + + This code executes in boot services context. + +Arguments: + None + +Returns: + EFI_SUCCESS + EFI_UNSUPPORTED - if IA32 processor does not support FXSTOR/FXRSTOR instructions, + the context save will fail, so these processor's are not supported. + EFI_OUT_OF_RESOURCES - not resource to finish initialization + +--*/ +{ + EFI_EXCEPTION_TYPE ExceptionType; + + if (!FxStorSupport ()) { + return EFI_UNSUPPORTED; + } + + IdtEntryTable = AllocateZeroPool (sizeof (IDT_ENTRY) * NUM_IDT_ENTRIES); + if (IdtEntryTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { + IdtEntryTable[ExceptionType].StubEntry = (DEBUG_PROC) (UINTN) AllocatePool (StubSize); + if (IdtEntryTable[ExceptionType].StubEntry == NULL) { + goto ErrorCleanup; + } + + CopyMem ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry, InterruptEntryStub, StubSize); + } + return EFI_SUCCESS; + +ErrorCleanup: + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { + if (IdtEntryTable[ExceptionType].StubEntry != NULL) { + FreePool ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry); + } + } + FreePool (IdtEntryTable); + + return EFI_OUT_OF_RESOURCES; +} + +EFI_STATUS +EFIAPI +plUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ) +/*++ + +Routine Description: + This is the callback that is written to the LoadedImage protocol instance + on the image handle. It uninstalls all registered handlers and frees all entry + stub memory. + + This code executes in boot services context. + +Arguments: + ImageHandle - The image handle of the unload handler + +Returns: + + EFI_SUCCESS - always return success + +--*/ +{ + EFI_EXCEPTION_TYPE ExceptionType; + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { + ManageIdtEntryTable (NULL, ExceptionType); + } + + FreePool (IdtEntryTable); + return EFI_SUCCESS; +} + +VOID +InterruptDistrubutionHub ( + EFI_EXCEPTION_TYPE ExceptionType, + EFI_SYSTEM_CONTEXT_IA32 *ContextRecord + ) +/*++ + +Routine Description: Common piece of code that invokes the registered handlers. + + This code executes in exception context so no efi calls are allowed. + +Arguments: + ExceptionType - exception type + ContextRecord - system context + +Returns: + + None + +--*/ +{ + if (IdtEntryTable[ExceptionType].RegisteredCallback != NULL) { + if (ExceptionType != SYSTEM_TIMER_VECTOR) { + IdtEntryTable[ExceptionType].RegisteredCallback (ExceptionType, ContextRecord); + } else { + OrigVector = IdtEntryTable[ExceptionType].OrigVector; + IdtEntryTable[ExceptionType].RegisteredCallback (ContextRecord); + } + } +} diff --git a/MdeModulePkg/Universal/DebugSupportDxe/Ia32/plDebugSupport.h b/MdeModulePkg/Universal/DebugSupportDxe/Ia32/plDebugSupport.h new file mode 100644 index 0000000000..98b4df007c --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/Ia32/plDebugSupport.h @@ -0,0 +1,339 @@ +/**@file + IA32 specific debug support macros, typedefs and prototypes. + +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. + +**/ + +#ifndef _PLDEBUG_SUPPORT_H +#define _PLDEBUG_SUPPORT_H + +// +// The package level header files this module uses +// +#include +// +// The protocols, PPI and GUID defintions for this module +// +#include +#include +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include +#include + +#define NUM_IDT_ENTRIES 0x78 +#define SYSTEM_TIMER_VECTOR 0x68 +#define VECTOR_ENTRY_PAGES 1 +#define CopyDescriptor(Dest, Src) CopyMem ((Dest), (Src), sizeof (DESCRIPTOR)) +#define ZeroDescriptor(Dest) CopyDescriptor ((Dest), &NullDesc) +#define ReadIdt(Vector, Dest) CopyDescriptor ((Dest), &((GetIdtr ())[(Vector)])) +#define WriteIdt(Vector, Src) CopyDescriptor (&((GetIdtr ())[(Vector)]), (Src)) +#define CompareDescriptor(Desc1, Desc2) CompareMem ((Desc1), (Desc2), sizeof (DESCRIPTOR)) +#define EFI_ISA IsaIa32 +#define FF_FXSR (1 << 24) + +typedef UINT64 DESCRIPTOR; + +typedef +VOID +(*DEBUG_PROC) ( + VOID + ) +; + +typedef struct { + DESCRIPTOR OrigDesc; + DEBUG_PROC OrigVector; + DESCRIPTOR NewDesc; + DEBUG_PROC StubEntry; + VOID (*RegisteredCallback) (); +} IDT_ENTRY; + +extern EFI_SYSTEM_CONTEXT SystemContext; +extern UINT8 InterruptEntryStub[]; +extern UINT32 StubSize; +extern VOID (*OrigVector) (VOID); + +VOID +CommonIdtEntry ( + VOID + ) +/*++ + +Routine Description: + + Generic IDT entry + +Arguments: + + None + +Returns: + + None + +--*/ +; + + +BOOLEAN +FxStorSupport ( + VOID + ) +/*++ + +Routine Description: + + Check whether FXSTOR is supported + +Arguments: + + None + +Returns: + + TRUE - supported + FALSE - not supported + +--*/ +; + +DESCRIPTOR * +GetIdtr ( + VOID + ) +/*++ + +Routine Description: + + Return the physical address of IDTR + +Arguments: + + None + +Returns: + + The physical address of IDTR + +--*/ +; + +VOID +Vect2Desc ( + DESCRIPTOR * DestDesc, + VOID (*Vector) (VOID) + ) +/*++ + +Routine Description: + + Encodes an IDT descriptor with the given physical address + +Arguments: + + DestDesc - The IDT descriptor address + Vector - The interrupt vector entry + +Returns: + + None + +--*/ +; + +BOOLEAN +WriteInterruptFlag ( + BOOLEAN NewState + ) +/*++ + +Routine Description: + + Programs interrupt flag to the requested state and returns previous + state. + +Arguments: + + NewState - New interrupt status + +Returns: + + Old interrupt status + +--*/ +; + +EFI_STATUS +plInitializeDebugSupportDriver ( + VOID + ) +/*++ + +Routine Description: + Initializes driver's handler registration database. + + This code executes in boot services context. + +Arguments: + None + +Returns: + EFI_SUCCESS + EFI_UNSUPPORTED - if IA32 processor does not support FXSTOR/FXRSTOR instructions, + the context save will fail, so these processor's are not supported. + EFI_OUT_OF_RESOURCES - not resource to finish initialization + +--*/ +; + +EFI_STATUS +EFIAPI +plUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ) +/*++ + +Routine Description: + This is the callback that is written to the LoadedImage protocol instance + on the image handle. It uninstalls all registered handlers and frees all entry + stub memory. + + This code executes in boot services context. + +Arguments: + ImageHandle - The image handle of the unload handler + +Returns: + + EFI_SUCCESS - always return success + +--*/ +; + +// +// DebugSupport protocol member functions +// +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ) +/*++ + +Routine Description: This is a DebugSupport protocol member function. + +Arguments: + This - The DebugSupport instance + MaxProcessorIndex - The maximuim supported processor index + +Returns: + Always returns EFI_SUCCESS with *MaxProcessorIndex set to 0 + +--*/ +; + +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ) +/*++ + +Routine Description: This is a DebugSupport protocol member function. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + PeriodicCallback - Callback function + +Returns: + + EFI_SUCCESS + EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has + no handler registered for it + EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered. + + Other possible return values are passed through from UnHookEntry and HookEntry. + +--*/ +; + +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK NewCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ) +/*++ + +Routine Description: + This is a DebugSupport protocol member function. + + This code executes in boot services context. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + NewCallback - Callback function + ExceptionType - Which exception to hook + +Returns: + + EFI_SUCCESS + EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has + no handler registered for it + EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered. + + Other possible return values are passed through from UnHookEntry and HookEntry. + +--*/ +; + +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINT64 Length + ) +/*++ + +Routine Description: + This is a DebugSupport protocol member function. + Calls assembly routine to flush cache. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + Start - Physical base of the memory range to be invalidated + Length - mininum number of bytes in instruction cache to invalidate + +Returns: + + EFI_SUCCESS - always return success + +--*/ +; + +#endif diff --git a/MdeModulePkg/Universal/DebugSupportDxe/ipf/AsmFuncs.s b/MdeModulePkg/Universal/DebugSupportDxe/ipf/AsmFuncs.s new file mode 100644 index 0000000000..866e5db9a8 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/ipf/AsmFuncs.s @@ -0,0 +1,1389 @@ +//++ +// 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. +// +// Module Name: +// +// AsmFuncs.s +// +// Abstract: +// +// Low level IPF routines used by the debug support driver +// +// Revision History: +// +//-- + + +#include "common.i" +#include "Ds64Macros.i" + +.globl PatchSaveBuffer +.globl IpfContextBuf +.globl CommonHandler +.globl ExternalInterruptCount + + +///////////////////////////////////////////// +// +// Name: +// InstructionCacheFlush +// +// Description: +// Flushes instruction cache for specified number of bytes +// + .globl InstructionCacheFlush + .proc InstructionCacheFlush + .align 32 +InstructionCacheFlush:: + { .mii + alloc r3=2, 0, 0, 0 + cmp4.leu p0,p6=32, r33;; + (p6) mov r33=32;; + } + { .mii + nop.m 0 + zxt4 r29=r33;; + dep.z r30=r29, 0, 5;; + } + { .mii + cmp4.eq p0,p7=r0, r30 + shr.u r28=r29, 5;; + (p7) adds r28=1, r28;; + } + { .mii + nop.m 0 + shl r27=r28, 5;; + zxt4 r26=r27;; + } + { .mfb + add r31=r26, r32 + nop.f 0 + nop.b 0 + } +LoopBack: // $L143: + { .mii + fc r32 + adds r32=32, r32;; + cmp.ltu p14,p15=r32, r31 + } + { .mfb + nop.m 0 + nop.f 0 + //(p14) br.cond.dptk.few $L143#;; + (p14) br.cond.dptk.few LoopBack;; + } + { .mmi + sync.i;; + srlz.i + nop.i 0;; + } + { .mfb + nop.m 0 + nop.f 0 + br.ret.sptk.few b0;; + } + .endp InstructionCacheFlush + + +///////////////////////////////////////////// +// +// Name: +// ChainHandler +// +// Description: +// Chains an interrupt handler +// +// The purpose of this function is to enable chaining of the external interrupt. +// Since there's no clean SAL abstraction for doing this, we must do it +// surreptitiously. +// +// The reserved IVT entry at offset 0x3400 is coopted for use by this handler. +// According to Itanium architecture, it is reserved. Strictly speaking, this is +// not safe, as we're cheating and violating the Itanium architecture. However, +// as long as we're the only ones cheating, we should be OK. Without hooks in +// the SAL to enable IVT management, there aren't many good options. +// +// The strategy is to replace the first bundle of the external interrupt handler +// with our own that will branch into a piece of code we've supplied and located +// in the reserved IVT entry. Only the first bundle of the external interrupt +// IVT entry is modified. +// +// The original bundle is moved and relocated to space +// allocated within the reserved IVT entry. The next bundle following is +// is generated to go a hard coded branch back to the second bundle of the +// external interrupt IVT entry just in case the first bundle had no branch. +// +// Our new code will execute our handler, and then fall through to the +// original bundle after restoring all context appropriately. +// +// The following is a representation of what the IVT memory map looks like with +// our chained handler installed: +// +// +// +// +// +// This IVT entry is Failsafe bundle +// reserved by the +// Itanium architecture Original bundle 0 +// and is used for +// for locating our +// handler and the +// original bundle Patch code... +// zero of the ext +// interrupt handler +// +// RSVD (3400) Unused +// +// +// +// +// +// +// +// +// +// +// +// +// EXT_INT (3000) Bundle 0 Bundle zero - This one is +// modified, all other bundles +// in the EXT_INT entry are +// untouched. +// +// +// Arguments: +// +// Returns: +// +// Notes: +// +// + .globl ChainHandler + .proc ChainHandler +ChainHandler: + + NESTED_SETUP( 0,2+3,3,0 ) + + mov r8=1 // r8 = success + mov r2=cr.iva;; +// +// NOTE: There's a potential hazard here in that we're simply stealing a bunch of +// bundles (memory) from the IVT and assuming there's no catastrophic side effect. +// +// First, save IVT area we're taking over with the patch so we can restore it later +// + addl out0=PATCH_ENTRY_OFFSET, r2 // out0 = source buffer + movl out1=PatchSaveBuffer // out1 = destination buffer + mov out2=0x40;; // out2 = number of bundles to copy... save entire IDT entry + br.call.sptk.few b0 = CopyBundles + +// Next, copy the patch code into the IVT + movl out0=PatchCode // out0 = source buffer of patch code + addl out1=PATCH_OFFSET, r2 // out1 = destination buffer - in IVT + mov out2=NUM_PATCH_BUNDLES;; // out2 = number of bundles to copy + br.call.sptk.few b0 = CopyBundles + + +// copy original bundle 0 from the external interrupt handler to the +// appropriate place in the reserved IVT interrupt slot + addl out0=EXT_INT_ENTRY_OFFSET, r2 // out0 = source buffer + addl out1=RELOCATED_EXT_INT, r2 // out1 = destination buffer - in reserved IVT + mov out2=1;; // out2 = copy 1 bundle + br.call.sptk.few b0 = CopyBundles + +// Now relocate it there because it very likely had a branch instruction that +// that must now be fixed up. + addl out0=RELOCATED_EXT_INT, r2 // out0 = new runtime address of bundle - in reserved IVT + addl out1=EXT_INT_ENTRY_OFFSET, r2;;// out1 = IP address of previous location + mov out2=out0;; // out2 = IP address of new location + br.call.sptk.few b0 = RelocateBundle + +// Now copy into the failsafe branch into the next bundle just in case +// the original ext int bundle 0 bundle did not contain a branch instruction + movl out0=FailsafeBranch // out0 = source buffer + addl out1=FAILSAFE_BRANCH_OFFSET, r2 // out1 = destination buffer - in reserved IVT + mov out2=1;; // out2 = copy 1 bundle + br.call.sptk.few b0 = CopyBundles + +// Last, copy in our replacement for the external interrupt IVT entry bundle 0 + movl out0=PatchCodeNewBun0 // out0 = source buffer - our replacement bundle 0 + addl out1=EXT_INT_ENTRY_OFFSET, r2 // out1 = destination buffer - bundle 0 of External interrupt entry + mov out2=1;; // out2 = copy 1 bundle + br.call.sptk.few b0 = CopyBundles + +ChainHandlerDone: + NESTED_RETURN + + .endp ChainHandler + + +///////////////////////////////////////////// +// +// Name: +// UnchainHandler +// +// Description: +// Unchains an interrupt handler +// +// Arguments: +// +// Returns: +// +// Notes: +// +// + .globl UnchainHandler + .proc UnchainHandler + +UnchainHandler: + + NESTED_SETUP( 0,2+3,3,0 ) + + mov r8=1 // r8 = success + mov r2=cr.iva;; // r2 = interrupt vector address + +// First copy original Ext Int bundle 0 back to it's proper home... + addl out0=RELOCATED_EXT_INT, r2 // out0 = source - in reserved IVT + addl out1=EXT_INT_ENTRY_OFFSET, r2 // out1 = destination buffer - first bundle of Ext Int entry + mov out2=1;; // out2 = copy 1 bundle + br.call.sptk.few b0 = CopyBundles + +// Now, relocate it again... + addl out0=EXT_INT_ENTRY_OFFSET, r2 // out1 = New runtime address + addl out1=RELOCATED_EXT_INT, r2;; // out0 = IP address of previous location + mov out2=out0;; // out2 = IP address of new location + br.call.sptk.few b0 = RelocateBundle + +// Last, restore the patch area + movl out0=PatchSaveBuffer // out0 = source buffer + addl out1=PATCH_ENTRY_OFFSET, r2 // out1 = destination buffer + mov out2=0x40;; // out2 = number of bundles to copy... save entire IDT entry + br.call.sptk.few b0 = CopyBundles + +UnchainHandlerDone: + NESTED_RETURN + + .endp UnchainHandler + + +///////////////////////////////////////////// +// +// Name: +// CopyBundles +// +// Description: +// Copies instruction bundles - flushes icache as necessary +// +// Arguments: +// in0 - Bundle source +// in1 - Bundle destination +// in2 - Bundle count +// +// Returns: +// +// Notes: +// This procedure is a leaf routine +// + .proc CopyBundles + +CopyBundles: + + NESTED_SETUP(3,2+1,0,0) + + shl in2=in2, 1;; // in2 = count of 8 byte blocks to copy + +CopyBundlesLoop: + + cmp.eq p14, p15 = 0, in2;; // Check if done +(p14) br.sptk.few CopyBundlesDone;; + + ld8 loc2=[in0], 0x8;; // loc2 = source bytes + st8 [in1]=loc2;; // [in1] = destination bytes + fc in1;; // Flush instruction cache + sync.i;; // Ensure local and remote data/inst caches in sync + srlz.i;; // Ensure sync has been observed + add in1=0x8, in1;; // in1 = next destination + add in2=-1, in2;; // in2 = decrement 8 bytes blocks to copy + br.sptk.few CopyBundlesLoop;; + +CopyBundlesDone: + NESTED_RETURN + + .endp CopyBundles + + +///////////////////////////////////////////// +// +// Name: +// RelocateBundle +// +// Description: +// Relocates an instruction bundle by updating any ip-relative branch instructions. +// +// Arguments: +// in0 - Runtime address of bundle +// in1 - IP address of previous location of bundle +// in2 - IP address of new location of bundle +// +// Returns: +// in0 - 1 if successful or 0 if unsuccessful +// +// Notes: +// This routine examines all slots in the given bundle that are destined for the +// branch execution unit. If any of these slots contain an IP-relative branch +// namely instructions B1, B2, B3, or B6, the slot is fixed-up with a new relative +// address. Errors can occur if a branch cannot be reached. +// + .proc RelocateBundle + +RelocateBundle: + + NESTED_SETUP(3,2+4,3,0) + + mov loc2=SLOT0 // loc2 = slot index + mov loc5=in0;; // loc5 = runtime address of bundle + mov in0=1;; // in0 = success + +RelocateBundleNextSlot: + + cmp.ge p14, p15 = SLOT2, loc2;; // Check if maximum slot +(p15) br.sptk.few RelocateBundleDone + + mov out0=loc5;; // out0 = runtime address of bundle + br.call.sptk.few b0 = GetTemplate + mov loc3=out0;; // loc3 = instruction template + mov out0=loc5 // out0 = runtime address of bundle + mov out1=loc2;; // out1 = instruction slot number + br.call.sptk.few b0 = GetSlot + mov loc4=out0;; // loc4 = instruction encoding + mov out0=loc4 // out0 = instuction encoding + mov out1=loc2 // out1 = instruction slot number + mov out2=loc3;; // out2 = instruction template + br.call.sptk.few b0 = IsSlotBranch + cmp.eq p14, p15 = 1, out0;; // Check if branch slot +(p15) add loc2=1,loc2 // Increment slot +(p15) br.sptk.few RelocateBundleNextSlot + mov out0=loc4 // out0 = instuction encoding + mov out1=in1 // out1 = IP address of previous location + mov out2=in2;; // out2 = IP address of new location + br.call.sptk.few b0 = RelocateSlot + cmp.eq p14, p15 = 1, out1;; // Check if relocated slot +(p15) mov in0=0 // in0 = failure +(p15) br.sptk.few RelocateBundleDone + mov out2=out0;; // out2 = instruction encoding + mov out0=loc5 // out0 = runtime address of bundle + mov out1=loc2;; // out1 = instruction slot number + br.call.sptk.few b0 = SetSlot + add loc2=1,loc2;; // Increment slot + br.sptk.few RelocateBundleNextSlot + +RelocateBundleDone: + NESTED_RETURN + + .endp RelocateBundle + + +///////////////////////////////////////////// +// +// Name: +// RelocateSlot +// +// Description: +// Relocates an instruction bundle by updating any ip-relative branch instructions. +// +// Arguments: +// in0 - Instruction encoding (41-bits, right justified) +// in1 - IP address of previous location of bundle +// in2 - IP address of new location of bundle +// +// Returns: +// in0 - Instruction encoding (41-bits, right justified) +// in1 - 1 if successful otherwise 0 +// +// Notes: +// This procedure is a leaf routine +// + .proc RelocateSlot + +RelocateSlot: + NESTED_SETUP(3,2+5,0,0) + extr.u loc2=in0, 37, 4;; // loc2 = instruction opcode + cmp.eq p14, p15 = 4, loc2;; // IP-relative branch (B1) or + // IP-relative counted branch (B2) +(p15) cmp.eq p14, p15 = 5, loc2;; // IP-relative call (B3) +(p15) cmp.eq p14, p15 = 7, loc2;; // IP-relative predict (B6) +(p15) mov in1=1 // Instruction did not need to be reencoded +(p15) br.sptk.few RelocateSlotDone + tbit.nz p14, p15 = in0, 36;; // put relative offset sign bit in p14 + extr.u loc2=in0, 13, 20;; // loc2 = relative offset in instruction +(p14) movl loc3=0xfffffffffff00000;; // extend sign +(p14) or loc2=loc2, loc3;; + shl loc2=loc2,4;; // convert to byte offset instead of bundle offset + add loc3=loc2, in1;; // loc3 = physical address of branch target +(p14) sub loc2=r0,loc2;; // flip sign in loc2 if offset is negative + sub loc4=loc3,in2;; // loc4 = relative offset from new ip to branch target + cmp.lt p15, p14 = 0, loc4;; // get new sign bit +(p14) sub loc5=r0,loc4 // get absolute value of offset +(p15) mov loc5=loc4;; + movl loc6=0x0FFFFFF;; // maximum offset in bytes for ip-rel branch + cmp.gt p14, p15 = loc5, loc6;; // check to see we're not out of range for an ip-relative branch +(p14) br.sptk.few RelocateSlotError + cmp.lt p15, p14 = 0, loc4;; // store sign in p14 again +(p14) dep in0=1,in0,36,1 // store sign bit in instruction +(p15) dep in0=0,in0,36,1 + shr loc4=loc4, 4;; // convert back to bundle offset + dep in0=loc4,in0,13,16;; // put first 16 bits of new offset into instruction + shr loc4=loc4,16;; + dep in0=loc4,in0,13+16,4 // put last 4 bits of new offset into instruction + mov in1=1;; // in1 = success + br.sptk.few RelocateSlotDone;; + +RelocateSlotError: + mov in1=0;; // in1 = failure + +RelocateSlotDone: + NESTED_RETURN + + .endp RelocateSlot + + +///////////////////////////////////////////// +// +// Name: +// IsSlotBranch +// +// Description: +// Determines if the given instruction is a branch instruction. +// +// Arguments: +// in0 - Instruction encoding (41-bits, right justified) +// in1 - Instruction slot number +// in2 - Bundle template +// +// Returns: +// in0 - 1 if branch or 0 if not branch +// +// Notes: +// This procedure is a leaf routine +// +// IsSlotBranch recognizes all branch instructions by looking at the provided template. +// The instruction encoding is only passed to this routine for future expansion. +// + .proc IsSlotBranch + +IsSlotBranch: + + NESTED_SETUP (3,2+0,0,0) + + mov in0=1;; // in0 = 1 which destroys the instruction + andcm in2=in2,in0;; // in2 = even template to reduce compares + mov in0=0;; // in0 = not a branch + cmp.eq p14, p15 = 0x16, in2;; // Template 0x16 is BBB +(p14) br.sptk.few IsSlotBranchTrue + cmp.eq p14, p15 = SLOT0, in1;; // Slot 0 has no other possiblities +(p14) br.sptk.few IsSlotBranchDone + cmp.eq p14, p15 = 0x12, in2;; // Template 0x12 is MBB +(p14) br.sptk.few IsSlotBranchTrue + cmp.eq p14, p15 = SLOT1, in1;; // Slot 1 has no other possiblities +(p14) br.sptk.few IsSlotBranchDone + cmp.eq p14, p15 = 0x10, in2;; // Template 0x10 is MIB +(p14) br.sptk.few IsSlotBranchTrue + cmp.eq p14, p15 = 0x18, in2;; // Template 0x18 is MMB +(p14) br.sptk.few IsSlotBranchTrue + cmp.eq p14, p15 = 0x1C, in2;; // Template 0x1C is MFB +(p14) br.sptk.few IsSlotBranchTrue + br.sptk.few IsSlotBranchDone + +IsSlotBranchTrue: + mov in0=1;; // in0 = branch + +IsSlotBranchDone: + NESTED_RETURN + + .endp IsSlotBranch + + +///////////////////////////////////////////// +// +// Name: +// GetTemplate +// +// Description: +// Retrieves the instruction template for an instruction bundle +// +// Arguments: +// in0 - Runtime address of bundle +// +// Returns: +// in0 - Instruction template (5-bits, right-justified) +// +// Notes: +// This procedure is a leaf routine +// + .proc GetTemplate + +GetTemplate: + + NESTED_SETUP (1,2+2,0,0) + + ld8 loc2=[in0], 0x8 // loc2 = first 8 bytes of branch bundle + movl loc3=MASK_0_4;; // loc3 = template mask + and loc2=loc2,loc3;; // loc2 = template, right justified + mov in0=loc2;; // in0 = template, right justified + + NESTED_RETURN + + .endp GetTemplate + + +///////////////////////////////////////////// +// +// Name: +// GetSlot +// +// Description: +// Gets the instruction encoding for an instruction slot and bundle +// +// Arguments: +// in0 - Runtime address of bundle +// in1 - Instruction slot (either 0, 1, or 2) +// +// Returns: +// in0 - Instruction encoding (41-bits, right justified) +// +// Notes: +// This procedure is a leaf routine +// +// Slot0 - [in0 + 0x8] Bits 45-5 +// Slot1 - [in0 + 0x8] Bits 63-46 and [in0] Bits 22-0 +// Slot2 - [in0] Bits 63-23 +// + .proc GetSlot + +GetSlot: + NESTED_SETUP (2,2+3,0,0) + + ld8 loc2=[in0], 0x8;; // loc2 = first 8 bytes of branch bundle + ld8 loc3=[in0];; // loc3 = second 8 bytes of branch bundle + cmp.eq p14, p15 = 2, in1;; // check if slot 2 specified + (p14) br.cond.sptk.few GetSlot2;; // get slot 2 + cmp.eq p14, p15 = 1, in1;; // check if slot 1 specified + (p14) br.cond.sptk.few GetSlot1;; // get slot 1 + +GetSlot0: + extr.u in0=loc2, 5, 45 // in0 = extracted slot 0 + br.sptk.few GetSlotDone;; + +GetSlot1: + extr.u in0=loc2, 46, 18 // in0 = bits 63-46 of loc2 right-justified + extr.u loc4=loc3, 0, 23;; // loc4 = bits 22-0 of loc3 right-justified + dep in0=loc4, in0, 18, 15;; + shr.u loc4=loc4,15;; + dep in0=loc4, in0, 33, 8;; // in0 = extracted slot 1 + br.sptk.few GetSlotDone;; + +GetSlot2: + extr.u in0=loc3, 23, 41;; // in0 = extracted slot 2 + +GetSlotDone: + NESTED_RETURN + + .endp GetSlot + + +///////////////////////////////////////////// +// +// Name: +// SetSlot +// +// Description: +// Sets the instruction encoding for an instruction slot and bundle +// +// Arguments: +// in0 - Runtime address of bundle +// in1 - Instruction slot (either 0, 1, or 2) +// in2 - Instruction encoding (41-bits, right justified) +// +// Returns: +// +// Notes: +// This procedure is a leaf routine +// + .proc SetSlot + +SetSlot: + NESTED_SETUP (3,2+3,0,0) + + ld8 loc2=[in0], 0x8;; // loc2 = first 8 bytes of bundle + ld8 loc3=[in0];; // loc3 = second 8 bytes of bundle + cmp.eq p14, p15 = 2, in1;; // check if slot 2 specified + (p14) br.cond.sptk.few SetSlot2;; // set slot 2 + cmp.eq p14, p15 = 1, in1;; // check if slot 1 specified + (p14) br.cond.sptk.few SetSlot1;; // set slot 1 + +SetSlot0: + dep loc2=0, loc2, 5, 41;; // remove old instruction from slot 0 + shl loc4=in2, 5;; // loc4 = new instruction ready to be inserted + or loc2=loc2, loc4;; // loc2 = updated first 8 bytes of bundle + add loc4=0x8,in0;; // loc4 = address to store first 8 bytes of bundle + st8 [loc4]=loc2 // [loc4] = updated bundle + br.sptk.few SetSlotDone;; + ;; + +SetSlot1: + dep loc2=0, loc2, 46, 18 // remove old instruction from slot 1 + dep loc3=0, loc3, 0, 23;; + shl loc4=in2, 46;; // loc4 = partial instruction ready to be inserted + or loc2=loc2, loc4;; // loc2 = updated first 8 bytes of bundle + add loc4=0x8,in0;; // loc4 = address to store first 8 bytes of bundle + st8 [loc4]=loc2;; // [loc4] = updated bundle + shr.u loc4=in2, 18;; // loc4 = partial instruction ready to be inserted + or loc3=loc3, loc4;; // loc3 = updated second 8 bytes of bundle + st8 [in0]=loc3;; // [in0] = updated bundle + br.sptk.few SetSlotDone;; + +SetSlot2: + dep loc3=0, loc3, 23, 41;; // remove old instruction from slot 2 + shl loc4=in2, 23;; // loc4 = instruction ready to be inserted + or loc3=loc3, loc4;; // loc3 = updated second 8 bytes of bundle + st8 [in0]=loc3;; // [in0] = updated bundle + +SetSlotDone: + + NESTED_RETURN + .endp SetSlot + + +///////////////////////////////////////////// +// +// Name: +// GetIva +// +// Description: +// C callable function to obtain the current value of IVA +// +// Returns: +// Current value if IVA + + .globl GetIva + .proc GetIva +GetIva: + mov r8=cr2;; + br.ret.sptk.many b0 + + .endp GetIva + + +///////////////////////////////////////////// +// +// Name: +// ProgramInterruptFlags +// +// Description: +// C callable function to enable/disable interrupts +// +// Returns: +// Previous state of psr.ic +// + .globl ProgramInterruptFlags + .proc ProgramInterruptFlags +ProgramInterruptFlags: + alloc loc0=1,2,0,0;; + mov loc0=psr + mov loc1=0x6000;; + and r8=loc0, loc1 // obtain current psr.ic and psr.i state + and in0=in0, loc1 // insure no extra bits set in input + andcm loc0=loc0,loc1;; // clear original psr.i and psr.ic + or loc0=loc0,in0;; // OR in new psr.ic value + mov psr.l=loc0;; // write new psr + srlz.d + br.ret.sptk.many b0 // return + + .endp ProgramInterruptFlags + + +///////////////////////////////////////////// +// +// Name: +// SpillContext +// +// Description: +// Saves system context to context record. +// +// Arguments: +// in0 = 512 byte aligned context record address +// in1 = original B0 +// in2 = original ar.bsp +// in3 = original ar.bspstore +// in4 = original ar.rnat +// in5 = original ar.pfs +// +// Notes: +// loc0 - scratch +// loc1 - scratch +// loc2 - temporary application unat storage +// loc3 - temporary exception handler unat storage + + .proc SpillContext + +SpillContext: + alloc loc0=6,4,0,0;; // alloc 6 input, 4 locals, 0 outs + mov loc2=ar.unat;; // save application context unat (spilled later) + mov ar.unat=r0;; // set UNAT=0 + st8.spill [in0]=r0,8;; + st8.spill [in0]=r1,8;; // save R1 - R31 + st8.spill [in0]=r2,8;; + st8.spill [in0]=r3,8;; + st8.spill [in0]=r4,8;; + st8.spill [in0]=r5,8;; + st8.spill [in0]=r6,8;; + st8.spill [in0]=r7,8;; + st8.spill [in0]=r8,8;; + st8.spill [in0]=r9,8;; + st8.spill [in0]=r10,8;; + st8.spill [in0]=r11,8;; + st8.spill [in0]=r12,8;; + st8.spill [in0]=r13,8;; + st8.spill [in0]=r14,8;; + st8.spill [in0]=r15,8;; + st8.spill [in0]=r16,8;; + st8.spill [in0]=r17,8;; + st8.spill [in0]=r18,8;; + st8.spill [in0]=r19,8;; + st8.spill [in0]=r20,8;; + st8.spill [in0]=r21,8;; + st8.spill [in0]=r22,8;; + st8.spill [in0]=r23,8;; + st8.spill [in0]=r24,8;; + st8.spill [in0]=r25,8;; + st8.spill [in0]=r26,8;; + st8.spill [in0]=r27,8;; + st8.spill [in0]=r28,8;; + st8.spill [in0]=r29,8;; + st8.spill [in0]=r30,8;; + st8.spill [in0]=r31,8;; + mov loc3=ar.unat;; // save debugger context unat (spilled later) + stf.spill [in0]=f2,16;; // save f2 - f31 + stf.spill [in0]=f3,16;; + stf.spill [in0]=f4,16;; + stf.spill [in0]=f5,16;; + stf.spill [in0]=f6,16;; + stf.spill [in0]=f7,16;; + stf.spill [in0]=f8,16;; + stf.spill [in0]=f9,16;; + stf.spill [in0]=f10,16;; + stf.spill [in0]=f11,16;; + stf.spill [in0]=f12,16;; + stf.spill [in0]=f13,16;; + stf.spill [in0]=f14,16;; + stf.spill [in0]=f15,16;; + stf.spill [in0]=f16,16;; + stf.spill [in0]=f17,16;; + stf.spill [in0]=f18,16;; + stf.spill [in0]=f19,16;; + stf.spill [in0]=f20,16;; + stf.spill [in0]=f21,16;; + stf.spill [in0]=f22,16;; + stf.spill [in0]=f23,16;; + stf.spill [in0]=f24,16;; + stf.spill [in0]=f25,16;; + stf.spill [in0]=f26,16;; + stf.spill [in0]=f27,16;; + stf.spill [in0]=f28,16;; + stf.spill [in0]=f29,16;; + stf.spill [in0]=f30,16;; + stf.spill [in0]=f31,16;; + mov loc0=pr;; // save predicates + st8.spill [in0]=loc0,8;; + st8.spill [in0]=in1,8;; // save b0 - b7... in1 already equals saved b0 + mov loc0=b1;; + st8.spill [in0]=loc0,8;; + mov loc0=b2;; + st8.spill [in0]=loc0,8;; + mov loc0=b3;; + st8.spill [in0]=loc0,8;; + mov loc0=b4;; + st8.spill [in0]=loc0,8;; + mov loc0=b5;; + st8.spill [in0]=loc0,8;; + mov loc0=b6;; + st8.spill [in0]=loc0,8;; + mov loc0=b7;; + st8.spill [in0]=loc0,8;; + mov loc0=ar.rsc;; // save ar.rsc + st8.spill [in0]=loc0,8;; + st8.spill [in0]=in2,8;; // save ar.bsp (in2) + st8.spill [in0]=in3,8;; // save ar.bspstore (in3) + st8.spill [in0]=in4,8;; // save ar.rnat (in4) + mov loc0=ar.fcr;; // save ar.fcr (ar21 - IA32 floating-point control register) + st8.spill [in0]=loc0,8;; + mov loc0=ar.eflag;; // save ar.eflag (ar24) + st8.spill [in0]=loc0,8;; + mov loc0=ar.csd;; // save ar.csd (ar25 - ia32 CS descriptor) + st8.spill [in0]=loc0,8;; + mov loc0=ar.ssd;; // save ar.ssd (ar26 - ia32 ss descriptor) + st8.spill [in0]=loc0,8;; + mov loc0=ar.cflg;; // save ar.cflg (ar27 - ia32 cr0 and cr4) + st8.spill [in0]=loc0,8;; + mov loc0=ar.fsr;; // save ar.fsr (ar28 - ia32 floating-point status register) + st8.spill [in0]=loc0,8;; + mov loc0=ar.fir;; // save ar.fir (ar29 - ia32 floating-point instruction register) + st8.spill [in0]=loc0,8;; + mov loc0=ar.fdr;; // save ar.fdr (ar30 - ia32 floating-point data register) + st8.spill [in0]=loc0,8;; + mov loc0=ar.ccv;; // save ar.ccv + st8.spill [in0]=loc0,8;; + st8.spill [in0]=loc2,8;; // save ar.unat (saved to loc2 earlier) + mov loc0=ar.fpsr;; // save floating point status register + st8.spill [in0]=loc0,8;; + st8.spill [in0]=in5,8;; // save ar.pfs + mov loc0=ar.lc;; // save ar.lc + st8.spill [in0]=loc0,8;; + mov loc0=ar.ec;; // save ar.ec + st8.spill [in0]=loc0,8;; + + // save control registers + mov loc0=cr.dcr;; // save dcr + st8.spill [in0]=loc0,8;; + mov loc0=cr.itm;; // save itm + st8.spill [in0]=loc0,8;; + mov loc0=cr.iva;; // save iva + st8.spill [in0]=loc0,8;; + mov loc0=cr.pta;; // save pta + st8.spill [in0]=loc0,8;; + mov loc0=cr.ipsr;; // save ipsr + st8.spill [in0]=loc0,8;; + mov loc0=cr.isr;; // save isr + st8.spill [in0]=loc0,8;; + mov loc0=cr.iip;; // save iip + st8.spill [in0]=loc0,8;; + mov loc0=cr.ifa;; // save ifa + st8.spill [in0]=loc0,8;; + mov loc0=cr.itir;; // save itir + st8.spill [in0]=loc0,8;; + mov loc0=cr.iipa;; // save iipa + st8.spill [in0]=loc0,8;; + mov loc0=cr.ifs;; // save ifs + st8.spill [in0]=loc0,8;; + mov loc0=cr.iim;; // save iim + st8.spill [in0]=loc0,8;; + mov loc0=cr.iha;; // save iha + st8.spill [in0]=loc0,8;; + + // save debug registers + mov loc0=dbr[r0];; // save dbr0 - dbr7 + st8.spill [in0]=loc0,8;; + movl loc1=1;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=2;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=3;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=4;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=5;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=6;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=7;; + mov loc0=dbr[loc1];; + st8.spill [in0]=loc0,8;; + mov loc0=ibr[r0];; // save ibr0 - ibr7 + st8.spill [in0]=loc0,8;; + movl loc1=1;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=2;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=3;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=4;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=5;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=6;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + movl loc1=7;; + mov loc0=ibr[loc1];; + st8.spill [in0]=loc0,8;; + st8.spill [in0]=loc3;; + + br.ret.sptk.few b0 + + .endp SpillContext + + +///////////////////////////////////////////// +// +// Name: +// FillContext +// +// Description: +// Restores register context from context record. +// +// Arguments: +// in0 = address of last element 512 byte aligned context record address +// in1 = modified B0 +// in2 = modified ar.bsp +// in3 = modified ar.bspstore +// in4 = modified ar.rnat +// in5 = modified ar.pfs +// +// Notes: +// loc0 - scratch +// loc1 - scratch +// loc2 - temporary application unat storage +// loc3 - temporary exception handler unat storage + + .proc FillContext +FillContext: + alloc loc0=6,4,0,0;; // alloc 6 inputs, 4 locals, 0 outs + ld8.fill loc3=[in0],-8;; // int_nat (nat bits for R1-31) + movl loc1=7;; // ibr7 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=6;; // ibr6 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=5;; // ibr5 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=4;; // ibr4 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=3;; // ibr3 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=2;; // ibr2 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + movl loc1=1;; // ibr1 + ld8.fill loc0=[in0],-8;; + mov ibr[loc1]=loc0;; + ld8.fill loc0=[in0],-8;; // ibr0 + mov ibr[r0]=loc0;; + movl loc1=7;; // dbr7 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=6;; // dbr6 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=5;; // dbr5 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=4;; // dbr4 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=3;; // dbr3 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=2;; // dbr2 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + movl loc1=1;; // dbr1 + ld8.fill loc0=[in0],-8;; + mov dbr[loc1]=loc0;; + ld8.fill loc0=[in0],-8;; // dbr0 + mov dbr[r0]=loc0;; + ld8.fill loc0=[in0],-8;; // iha + mov cr.iha=loc0;; + ld8.fill loc0=[in0],-8;; // iim + mov cr.iim=loc0;; + ld8.fill loc0=[in0],-8;; // ifs + mov cr.ifs=loc0;; + ld8.fill loc0=[in0],-8;; // iipa + mov cr.iipa=loc0;; + ld8.fill loc0=[in0],-8;; // itir + mov cr.itir=loc0;; + ld8.fill loc0=[in0],-8;; // ifa + mov cr.ifa=loc0;; + ld8.fill loc0=[in0],-8;; // iip + mov cr.iip=loc0;; + ld8.fill loc0=[in0],-8;; // isr + mov cr.isr=loc0;; + ld8.fill loc0=[in0],-8;; // ipsr + mov cr.ipsr=loc0;; + ld8.fill loc0=[in0],-8;; // pta + mov cr.pta=loc0;; + ld8.fill loc0=[in0],-8;; // iva + mov cr.iva=loc0;; + ld8.fill loc0=[in0],-8;; // itm + mov cr.itm=loc0;; + ld8.fill loc0=[in0],-8;; // dcr + mov cr.dcr=loc0;; + ld8.fill loc0=[in0],-8;; // ec + mov ar.ec=loc0;; + ld8.fill loc0=[in0],-8;; // lc + mov ar.lc=loc0;; + ld8.fill in5=[in0],-8;; // ar.pfs + ld8.fill loc0=[in0],-8;; // ar.fpsr + mov ar.fpsr=loc0;; + ld8.fill loc2=[in0],-8;; // ar.unat - restored later... + ld8.fill loc0=[in0],-8;; // ar.ccv + mov ar.ccv=loc0;; + ld8.fill loc0=[in0],-8;; // ar.fdr + mov ar.fdr=loc0;; + ld8.fill loc0=[in0],-8;; // ar.fir + mov ar.fir=loc0;; + ld8.fill loc0=[in0],-8;; // ar.fsr + mov ar.fsr=loc0;; + ld8.fill loc0=[in0],-8;; // ar.cflg + mov ar.cflg=loc0;; + ld8.fill loc0=[in0],-8;; // ar.ssd + mov ar.ssd=loc0;; + ld8.fill loc0=[in0],-8;; // ar.csd + mov ar.csd=loc0;; + ld8.fill loc0=[in0],-8;; // ar.eflag + mov ar.eflag=loc0;; + ld8.fill loc0=[in0],-8;; // ar.fcr + mov ar.fcr=loc0;; + ld8.fill in4=[in0],-8;; // ar.rnat + ld8.fill in3=[in0],-8;; // bspstore + ld8.fill in2=[in0],-8;; // bsp + ld8.fill loc0=[in0],-8;; // ar.rsc + mov ar.rsc=loc0;; + ld8.fill loc0=[in0],-8;; // B7 - B0 + mov b7=loc0;; + ld8.fill loc0=[in0],-8;; + mov b6=loc0;; + ld8.fill loc0=[in0],-8;; + mov b5=loc0;; + ld8.fill loc0=[in0],-8;; + mov b4=loc0;; + ld8.fill loc0=[in0],-8;; + mov b3=loc0;; + ld8.fill loc0=[in0],-8;; + mov b2=loc0;; + ld8.fill loc0=[in0],-8;; + mov b1=loc0;; + ld8.fill in1=[in0],-8;; // b0 is temporarily stored in in1 + ld8.fill loc0=[in0],-16;; // predicates + mov pr=loc0;; + ldf.fill f31=[in0],-16;; + ldf.fill f30=[in0],-16;; + ldf.fill f29=[in0],-16;; + ldf.fill f28=[in0],-16;; + ldf.fill f27=[in0],-16;; + ldf.fill f26=[in0],-16;; + ldf.fill f25=[in0],-16;; + ldf.fill f24=[in0],-16;; + ldf.fill f23=[in0],-16;; + ldf.fill f22=[in0],-16;; + ldf.fill f21=[in0],-16;; + ldf.fill f20=[in0],-16;; + ldf.fill f19=[in0],-16;; + ldf.fill f18=[in0],-16;; + ldf.fill f17=[in0],-16;; + ldf.fill f16=[in0],-16;; + ldf.fill f15=[in0],-16;; + ldf.fill f14=[in0],-16;; + ldf.fill f13=[in0],-16;; + ldf.fill f12=[in0],-16;; + ldf.fill f11=[in0],-16;; + ldf.fill f10=[in0],-16;; + ldf.fill f9=[in0],-16;; + ldf.fill f8=[in0],-16;; + ldf.fill f7=[in0],-16;; + ldf.fill f6=[in0],-16;; + ldf.fill f5=[in0],-16;; + ldf.fill f4=[in0],-16;; + ldf.fill f3=[in0],-16;; + ldf.fill f2=[in0],-8;; + mov ar.unat=loc3;; // restore unat (int_nat) before fill of general registers + ld8.fill r31=[in0],-8;; + ld8.fill r30=[in0],-8;; + ld8.fill r29=[in0],-8;; + ld8.fill r28=[in0],-8;; + ld8.fill r27=[in0],-8;; + ld8.fill r26=[in0],-8;; + ld8.fill r25=[in0],-8;; + ld8.fill r24=[in0],-8;; + ld8.fill r23=[in0],-8;; + ld8.fill r22=[in0],-8;; + ld8.fill r21=[in0],-8;; + ld8.fill r20=[in0],-8;; + ld8.fill r19=[in0],-8;; + ld8.fill r18=[in0],-8;; + ld8.fill r17=[in0],-8;; + ld8.fill r16=[in0],-8;; + ld8.fill r15=[in0],-8;; + ld8.fill r14=[in0],-8;; + ld8.fill r13=[in0],-8;; + ld8.fill r12=[in0],-8;; + ld8.fill r11=[in0],-8;; + ld8.fill r10=[in0],-8;; + ld8.fill r9=[in0],-8;; + ld8.fill r8=[in0],-8;; + ld8.fill r7=[in0],-8;; + ld8.fill r6=[in0],-8;; + ld8.fill r5=[in0],-8;; + ld8.fill r4=[in0],-8;; + ld8.fill r3=[in0],-8;; + ld8.fill r2=[in0],-8;; + ld8.fill r1=[in0],-8;; + mov ar.unat=loc2;; // restore application context unat + + br.ret.sptk.many b0 + + .endp FillContext + + +///////////////////////////////////////////// +// +// Name: +// HookHandler +// +// Description: +// Common branch target from hooked IVT entries. Runs in interrupt context. +// Responsible for saving and restoring context and calling common C +// handler. Banked registers running on bank 0 at entry. +// +// Arguments: +// All arguments are passed in banked registers: +// B0_REG = Original B0 +// SCRATCH_REG1 = IVT entry index +// +// Returns: +// Returns via rfi +// +// Notes: +// loc0 - scratch +// loc1 - scratch +// loc2 - vector number / mask +// loc3 - 16 byte aligned context record address +// loc4 - temporary storage of last address in context record + +HookHandler: + flushrs;; // Synch RSE with backing store + mov SCRATCH_REG2=ar.bsp // save interrupted context bsp + mov SCRATCH_REG3=ar.bspstore // save interrupted context bspstore + mov SCRATCH_REG4=ar.rnat // save interrupted context rnat + mov SCRATCH_REG6=cr.ifs;; // save IFS in case we need to chain... + cover;; // creates new frame, moves old + // CFM to IFS. + alloc SCRATCH_REG5=0,5,6,0 // alloc 5 locals, 6 outs + ;; + // save banked registers to locals + mov out1=B0_REG // out1 = Original B0 + mov out2=SCRATCH_REG2 // out2 = original ar.bsp + mov out3=SCRATCH_REG3 // out3 = original ar.bspstore + mov out4=SCRATCH_REG4 // out4 = original ar.rnat + mov out5=SCRATCH_REG5 // out5 = original ar.pfs + mov loc2=SCRATCH_REG1;; // loc2 = vector number + chain flag + bsw.1;; // switch banked registers to bank 1 + srlz.d // explicit serialize required + // now fill in context record structure + movl loc3=IpfContextBuf // Insure context record is aligned + add loc0=-0x200,r0;; // mask the lower 9 bits (align on 512 byte boundary) + and loc3=loc3,loc0;; + add loc3=0x200,loc3;; // move to next 512 byte boundary + // loc3 now contains the 512 byte aligned context record + // spill register context into context record + mov out0=loc3;; // Context record base in out0 + // original B0 in out1 already + // original ar.bsp in out2 already + // original ar.bspstore in out3 already + br.call.sptk.few b0=SpillContext;; // spill context + mov loc4=out0 // save modified address + + // At this point, the context has been saved to the context record and we're + // ready to call the C part of the handler... + + movl loc0=CommonHandler;; // obtain address of plabel + ld8 loc1=[loc0];; // get entry point of CommonHandler + mov b6=loc1;; // put it in a branch register + adds loc1= 8, loc0;; // index to GP in plabel + ld8 r1=[loc1];; // set up gp for C call + mov loc1=0xfffff;; // mask off so only vector bits are present + and out0=loc2,loc1;; // pass vector number (exception type) + mov out1=loc3;; // pass context record address + br.call.sptk.few b0=b6;; // call C handler + + // We've returned from the C call, so restore the context and either rfi + // back to interrupted thread, or chain into the SAL if this was an external interrupt + mov out0=loc4;; // pass address of last element in context record + br.call.sptk.few b0=FillContext;; // Fill context + mov b0=out1 // fill in b0 + mov ar.rnat=out4 + mov ar.pfs=out5 + + // Loadrs is necessary because the debugger may have changed some values in + // the backing store. The processor, however may not be aware that the + // stacked registers need to be reloaded from the backing store. Therefore, + // we explicitly cause the RSE to refresh the stacked register's contents + // from the backing store. + mov loc0=ar.rsc // get RSC value + mov loc1=ar.rsc // save it so we can restore it + movl loc3=0xffffffffc000ffff;; // create mask for clearing RSC.loadrs + and loc0=loc0,loc3;; // create value for RSC with RSC.loadrs==0 + mov ar.rsc=loc0;; // modify RSC + loadrs;; // invalidate register stack + mov ar.rsc=loc1;; // restore original RSC + + bsw.0;; // switch banked registers back to bank 0 + srlz.d;; // explicit serialize required + mov PR_REG=pr // save predicates - to be restored after chaining decision + mov B0_REG=b0 // save b0 - required by chain code + mov loc2=EXCPT_EXTERNAL_INTERRUPT;; + cmp.eq p7,p0=SCRATCH_REG1,loc2;; // check to see if this is the timer tick + (p7) br.cond.dpnt.few DO_CHAIN;; + +NO_CHAIN: + mov pr=PR_REG;; + rfi;; // we're outa here. + +DO_CHAIN: + mov pr=PR_REG + mov SCRATCH_REG1=cr.iva + mov SCRATCH_REG2=PATCH_RETURN_OFFSET;; + add SCRATCH_REG1=SCRATCH_REG1, SCRATCH_REG2;; + mov b0=SCRATCH_REG1;; + br.cond.sptk.few b0;; + +EndHookHandler: + + +///////////////////////////////////////////// +// +// Name: +// HookStub +// +// Description: +// HookStub will be copied from it's loaded location into the IVT when +// an IVT entry is hooked. The IVT entry does an indirect jump via B0 to +// HookHandler, which in turn calls into the default C handler, which calls +// the user-installed C handler. The calls return and HookHandler executes +// an rfi. +// +// Notes: +// Saves B0 to B0_REG +// Saves IVT index to SCRATCH_REG1 (immediate value is fixed up when code is copied +// to the IVT entry. + + .globl HookStub + .proc HookStub +HookStub: + + mov B0_REG=b0 + movl SCRATCH_REG1=HookHandler;; + mov b0=SCRATCH_REG1;; + mov SCRATCH_REG1=0;;// immediate value is fixed up during install of handler to be the vector number + br.cond.sptk.few b0 + + .endp HookStub + + +///////////////////////////////////////////// +// The following code is moved into IVT entry 14 (offset 3400) which is reserved +// in the Itanium architecture. The patch code is located at the end of the +// IVT entry. + +PatchCode: + mov SCRATCH_REG0=psr + mov SCRATCH_REG6=cr.ipsr + mov PR_REG=pr + mov B0_REG=b0;; + + // turn off any virtual translations + movl SCRATCH_REG1 = ~( MASK(PSR_DT,1) | MASK(PSR_RT,1));; + and SCRATCH_REG1 = SCRATCH_REG0, SCRATCH_REG1;; + mov psr.l = SCRATCH_REG1;; + srlz.d + tbit.z p14, p15 = SCRATCH_REG6, PSR_IS;; // Check to see if we were + // interrupted from IA32 + // context. If so, bail out + // and chain to SAL immediately + (p15) br.cond.sptk.few Stub_IVT_Passthru;; + // we only want to take 1 out of 32 external interrupts to minimize the + // impact to system performance. Check our interrupt count and bail + // out if we're not up to 32 + movl SCRATCH_REG1=ExternalInterruptCount;; + ld8 SCRATCH_REG2=[SCRATCH_REG1];; // ExternalInterruptCount + tbit.z p14, p15 = SCRATCH_REG2, 5;; // bit 5 set? + (p14) add SCRATCH_REG2=1, SCRATCH_REG2;; // No? Then increment + // ExternalInterruptCount + // and Chain to SAL + // immediately + (p14) st8 [SCRATCH_REG1]=SCRATCH_REG2;; + (p14) br.cond.sptk.few Stub_IVT_Passthru;; + (p15) mov SCRATCH_REG2=0;; // Yes? Then reset + // ExternalInterruptCount + // and branch to + // HookHandler + (p15) st8 [SCRATCH_REG1]=SCRATCH_REG2;; + mov pr=PR_REG + movl SCRATCH_REG1=HookHandler;; // SCRATCH_REG1 = entrypoint of HookHandler + mov b0=SCRATCH_REG1;; // b0 = entrypoint of HookHandler + mov SCRATCH_REG1=EXCPT_EXTERNAL_INTERRUPT;; + br.sptk.few b0;; // branch to HookHandler + +PatchCodeRet: + // fake-up an rfi to get RSE back to being coherent and insure psr has + // original contents when interrupt occured, then exit to SAL + // at this point: + // cr.ifs has been modified by previous "cover" + // SCRATCH_REG6 has original cr.ifs + + mov SCRATCH_REG5=cr.ipsr + mov SCRATCH_REG4=cr.iip;; + mov cr.ipsr=SCRATCH_REG0 + mov SCRATCH_REG1=ip;; + add SCRATCH_REG1=0x30, SCRATCH_REG1;; + mov cr.iip=SCRATCH_REG1;; + rfi;; // rfi to next instruction + +Stub_RfiTarget: + mov cr.ifs=SCRATCH_REG6 + mov cr.ipsr=SCRATCH_REG5 + mov cr.iip=SCRATCH_REG4;; + +Stub_IVT_Passthru: + mov pr=PR_REG // pr = saved predicate registers + mov b0=B0_REG;; // b0 = saved b0 +EndPatchCode: + + +///////////////////////////////////////////// +// The following bundle is moved into IVT entry 14 (offset 0x3400) which is reserved +// in the Itanium architecture. This bundle will be the last bundle and will +// be located at offset 0x37F0 in the IVT. + +FailsafeBranch: +{ + .mib + nop.m 0 + nop.i 0 + br.sptk.few -(FAILSAFE_BRANCH_OFFSET - EXT_INT_ENTRY_OFFSET - 0x10) +} + + +///////////////////////////////////////////// +// The following bundle is moved into IVT entry 13 (offset 0x3000) which is the +// external interrupt. It branches to the patch code. + +PatchCodeNewBun0: +{ + .mib + nop.m 0 + nop.i 0 + br.cond.sptk.few PATCH_BRANCH +} diff --git a/MdeModulePkg/Universal/DebugSupportDxe/ipf/Ds64Macros.i b/MdeModulePkg/Universal/DebugSupportDxe/ipf/Ds64Macros.i new file mode 100644 index 0000000000..6088ce70fd --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/ipf/Ds64Macros.i @@ -0,0 +1,85 @@ +//++ +// 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. +// +// Module Name: +// +// Ds64Macros.i +// +// Abstract: +// +// This is set of macros used in calculating offsets in the IVT +// +// Revision History: +// +//-- + +#define EXCPT_EXTERNAL_INTERRUPT 12 +#define MASK_0_4 0x000000000000001F // mask bits 0 through 4 +#define SLOT0 0 +#define SLOT1 1 +#define SLOT2 2 + +#define PSR_DT 17 +#define PSR_TB 26 +#define PSR_RT 27 +#define PSR_IS 34 +#define PSR_IT 36 +#define PSR_IC 13 +#define PSR_I 14 +#define PSR_SS 40 +#define PSR_BN 44 +#define PSR_RI_MASK 0x60000000000 + +#define EXCPT_EXTERNAL_INTERRUPT 12 + +#define SCRATCH_REG0 r23 +#define SCRATCH_REG1 r24 +#define SCRATCH_REG2 r25 +#define SCRATCH_REG3 r26 +#define SCRATCH_REG4 r27 +#define SCRATCH_REG5 r28 +#define SCRATCH_REG6 r29 +#define PR_REG r30 +#define B0_REG r31 + + +// EXT_INT_OFFSET is the offset of the external interrupt entry in the IVT +#define EXT_INT_ENTRY_OFFSET 0x03000 + +// PATCH_ENTRY_OFFSET is the offset into the IVT of the entry that is coopted (stolen) +// for use by the handler. The entire entry is restored when the handler is +// unloaded. +#define PATCH_ENTRY_OFFSET 0x03400 + +// PATCH_BUNDLES is the number of bundles actually in the patch +#define NUM_PATCH_BUNDLES ((EndPatchCode - PatchCode) / 0x10) + +// A hard coded branch back into the external interrupt IVT entry's second bundle +// is put here, just in case the original bundle zero did not have a branch +// This is the last bundle in the reserved IVT entry +#define FAILSAFE_BRANCH_OFFSET (PATCH_ENTRY_OFFSET + 0x400 - 0x10) + +// the original external interrupt IVT entry bundle zero is copied and relocated +// here... also in the reserved IVT entry +// This is the second-to-last bundle in the reserved IVT entry +#define RELOCATED_EXT_INT (PATCH_ENTRY_OFFSET + 0x400 - 0x20) + +// The patch is actually stored at the end of IVT:PATCH_ENTRY. The PATCH_OFFSET +// is the offset into IVT where the patch is actually stored. It is carefully +// located so that when we run out of patch code, the next bundle is the +// relocated bundle 0 from the original external interrupt handler +#define PATCH_OFFSET (PATCH_ENTRY_OFFSET + 0x400 - ( EndPatchCode - PatchCode ) - 0x20) + +#define PATCH_RETURN_OFFSET (PATCH_ENTRY_OFFSET + 0x400 - ( EndPatchCode - PatchCodeRet ) - 0x20) + +// PATCH_BRANCH is used only in the new bundle that is placed at the beginning +// of the external interrupt IVT entry. +#define PATCH_BRANCH (PATCH_OFFSET - EXT_INT_ENTRY_OFFSET) + diff --git a/MdeModulePkg/Universal/DebugSupportDxe/ipf/common.i b/MdeModulePkg/Universal/DebugSupportDxe/ipf/common.i new file mode 100644 index 0000000000..a7b571b308 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/ipf/common.i @@ -0,0 +1,34 @@ +//++ +// 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. +// +// Module Name: +// +// Common.i +// +// Abstract: +// +// This is set of useful macros +// +// Revision History: +// +//-- + +#define NESTED_SETUP(i,l,o,r) \ + alloc loc1=ar##.##pfs,i,l,o,r ; \ + mov loc0=b0 ;; + + +#define NESTED_RETURN \ + mov b0=loc0 ; \ + mov ar##.##pfs=loc1 ;; \ + br##.##ret##.##dpnt b0 ;; + +#define MASK(bp,value) (value << bp) + diff --git a/MdeModulePkg/Universal/DebugSupportDxe/ipf/plDebugSupport.c b/MdeModulePkg/Universal/DebugSupportDxe/ipf/plDebugSupport.c new file mode 100644 index 0000000000..33233d68e5 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/ipf/plDebugSupport.c @@ -0,0 +1,586 @@ +/**@file + IPF specific debug support functions + +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. + +**/ + +// +// private header files +// +#include "plDebugSupport.h" + +typedef struct { + UINT64 low; + UINT64 high; +} BUNDLE; + +// +// number of bundles to swap in ivt +// +#define NUM_BUNDLES_IN_STUB 5 +#define NUM_IVT_ENTRIES 64 + +typedef struct { + BUNDLE OrigBundles[NUM_BUNDLES_IN_STUB]; + VOID (*RegisteredCallback) (); +} IVT_ENTRY; + +STATIC +EFI_STATUS +ManageIvtEntryTable ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN BUNDLE NewBundles[4], + IN VOID (*NewCallback) () + ); + +STATIC +VOID +HookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN BUNDLE NewBundles[4], + IN VOID (*NewCallback) () + ); + +STATIC +VOID +UnhookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType + ); + +STATIC +VOID +ChainExternalInterrupt ( + IN VOID (*NewCallback) () + ); + +STATIC +VOID +UnchainExternalInterrupt ( + VOID + ); + +STATIC +VOID +GetHandlerEntryPoint ( + UINTN HandlerIndex, + VOID **EntryPoint + ); + +IVT_ENTRY IvtEntryTable[NUM_IVT_ENTRIES]; + +// +// IPF context record is overallocated by 512 bytes to guarantee a 512 byte alignment exists +// within the buffer and still have a large enough buffer to hold a whole IPF context record. +// +UINT8 IpfContextBuf[sizeof (EFI_SYSTEM_CONTEXT_IPF) + 512]; + +// +// The PatchSaveBuffer is used to store the original bundles from the IVT where it is patched +// with the common handler. +// +UINT8 PatchSaveBuffer[0x400]; +UINTN ExternalInterruptCount; + +EFI_STATUS +plInitializeDebugSupportDriver ( + VOID + ) +/*++ + +Routine Description: + IPF specific DebugSupport driver initialization. Must be public because it's + referenced from DebugSupport.c + +Arguments: + +Returns: + + EFI_SUCCESS + +--*/ +{ + SetMem (IvtEntryTable, sizeof (IvtEntryTable), 0); + ExternalInterruptCount = 0; + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +plUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ) +/*++ + +Routine Description: + Unload handler that is called during UnloadImage() - deallocates pool memory + used by the driver. Must be public because it's referenced from DebugSuport.c + +Arguments: + ImageHandle - Image handle + +Returns: + + EFI_STATUS - anything other than EFI_SUCCESS indicates the callback was not registered. + +--*/ +{ + EFI_EXCEPTION_TYPE ExceptionType; + + for (ExceptionType = 0; ExceptionType < NUM_IVT_ENTRIES; ExceptionType++) { + ManageIvtEntryTable (ExceptionType, NULL, NULL); + } + + return EFI_SUCCESS; +} + +VOID +CommonHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT Context + ) +/*++ + +Routine Description: + C routine that is called for all registered exceptions. This is the main + exception dispatcher. Must be public because it's referenced from AsmFuncs.s. + +Arguments: + ExceptionType - Exception Type + Context - System Context + +Returns: + + Nothing + +--*/ +{ + static BOOLEAN InHandler = FALSE; + + DEBUG_CODE_BEGIN (); + if (InHandler) { + DEBUG ((EFI_D_INFO, "ERROR: Re-entered debugger!\n" + " ExceptionType == %X\n" + " Context == %X\n" + " Context.SystemContextIpf->CrIip == %X\n" + " Context.SystemContextIpf->CrIpsr == %X\n" + " InHandler == %X\n", + ExceptionType, + Context, + Context.SystemContextIpf->CrIip, + Context.SystemContextIpf->CrIpsr, + InHandler)); + } + DEBUG_CODE_END (); + + ASSERT (!InHandler); + InHandler = TRUE; + if (IvtEntryTable[ExceptionType].RegisteredCallback != NULL) { + if (ExceptionType != EXCEPT_IPF_EXTERNAL_INTERRUPT) { + IvtEntryTable[ExceptionType].RegisteredCallback (ExceptionType, Context.SystemContextIpf); + } else { + IvtEntryTable[ExceptionType].RegisteredCallback (Context.SystemContextIpf); + } + } else { + ASSERT (0); + } + + InHandler = FALSE; +} + +STATIC +VOID +GetHandlerEntryPoint ( + UINTN HandlerIndex, + VOID **EntryPoint + ) +/*++ + +Routine Description: + Given an integer number, return the physical address of the entry point in the IFT + +Arguments: + HandlerIndex - Index of the Handler + EntryPoint - IFT Entrypoint + +Returns: + + Nothing + +--*/ +{ + UINT8 *TempPtr; + + // + // get base address of IVT + // + TempPtr = GetIva (); + + if (HandlerIndex < 20) { + // + // first 20 provide 64 bundles per vector + // + TempPtr += 0x400 * HandlerIndex; + } else { + // + // the rest provide 16 bundles per vector + // + TempPtr += 0x5000 + 0x100 * (HandlerIndex - 20); + } + + *EntryPoint = (VOID *) TempPtr; +} + +STATIC +EFI_STATUS +ManageIvtEntryTable ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN BUNDLE NewBundles[NUM_BUNDLES_IN_STUB], + IN VOID (*NewCallback) () + ) +/*++ + +Routine Description: + This is the worker function that installs and removes all handlers + +Arguments: + ExceptionType - Exception Type + NewBundles - New Boundles + NewCallback - New Callback + +Returns: + + EFI_STATUS - any return other than EFI_SUCCESS indicates the request was not + satisfied. + EFI_ALEADY_STARTED - Ivt already hooked. + +--*/ +{ + BUNDLE *B0Ptr; + UINT64 InterruptFlags; + EFI_TPL OldTpl; + + // + // Get address of bundle 0 + // + GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr); + + if (IvtEntryTable[ExceptionType].RegisteredCallback != NULL) { + // + // we've already installed to this vector + // + if (NewCallback != NULL) { + // + // if the input handler is non-null, error + // + return EFI_ALREADY_STARTED; + } else { + // + // else remove the previously installed handler + // + OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + InterruptFlags = ProgramInterruptFlags (DISABLE_INTERRUPTS); + if (ExceptionType == EXCEPT_IPF_EXTERNAL_INTERRUPT) { + UnchainExternalInterrupt (); + } else { + UnhookEntry (ExceptionType); + } + + ProgramInterruptFlags (InterruptFlags); + gBS->RestoreTPL (OldTpl); + // + // re-init IvtEntryTable + // + SetMem (&IvtEntryTable[ExceptionType], sizeof (IVT_ENTRY), 0); + } + } else { + // + // no user handler installed on this vector + // + if (NewCallback != NULL) { + OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + InterruptFlags = ProgramInterruptFlags (DISABLE_INTERRUPTS); + if (ExceptionType == EXCEPT_IPF_EXTERNAL_INTERRUPT) { + ChainExternalInterrupt (NewCallback); + } else { + HookEntry (ExceptionType, NewBundles, NewCallback); + } + + ProgramInterruptFlags (InterruptFlags); + gBS->RestoreTPL (OldTpl); + } + } + + return EFI_SUCCESS; +} + +STATIC +VOID +HookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN BUNDLE NewBundles[4], + IN VOID (*NewCallback) () + ) +/*++ + +Routine Description: + Saves original IVT contents and inserts a few new bundles which are fixed up + to store the ExceptionType and then call the common handler. + +Arguments: + ExceptionType - Exception Type + NewBundles - New Boundles + NewCallback - New Callback + +Returns: + + Nothing + +--*/ +{ + BUNDLE *FixupBundle; + BUNDLE *B0Ptr; + + // + // Get address of bundle 0 + // + GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr); + + // + // copy original bundles from IVT to IvtEntryTable so we can restore them later + // + CopyMem ( + IvtEntryTable[ExceptionType].OrigBundles, + B0Ptr, + sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB + ); + // + // insert new B0 + // + CopyMem (B0Ptr, NewBundles, sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB); + + // + // fixup IVT entry so it stores its index and whether or not to chain... + // + FixupBundle = B0Ptr + 2; + FixupBundle->high |= ExceptionType << 36; + + InstructionCacheFlush (B0Ptr, 5); + IvtEntryTable[ExceptionType].RegisteredCallback = NewCallback; +} + +STATIC +VOID +UnhookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType + ) +/*++ + +Routine Description: + Restores original IVT contents when unregistering a callback function + +Arguments: + ExceptionType - Exception Type + NewBundles - New Boundles + NewCallback - New Callback + +Returns: + + Nothing + +--*/ +{ + BUNDLE *B0Ptr; + + // + // Get address of bundle 0 + // + GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr); + // + // restore original bundles in IVT + // + CopyMem ( + B0Ptr, + IvtEntryTable[ExceptionType].OrigBundles, + sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB + ); + InstructionCacheFlush (B0Ptr, 5); +} + +STATIC +VOID +ChainExternalInterrupt ( + IN VOID (*NewCallback) () + ) +/*++ + +Routine Description: + Sets up cache flush and calls assembly function to chain external interrupt. + Records new callback in IvtEntryTable. + +Arguments: + NewCallback - New Callback + +Returns: + + Nothing + +--*/ +{ + VOID *Start; + + Start = (VOID *) ((UINT8 *) GetIva () + 0x400 * EXCEPT_IPF_EXTERNAL_INTERRUPT + 0x400); + IvtEntryTable[EXCEPT_IPF_EXTERNAL_INTERRUPT].RegisteredCallback = NewCallback; + ChainHandler (); + InstructionCacheFlush (Start, 0x400); +} + +STATIC +VOID +UnchainExternalInterrupt ( + VOID + ) +/*++ + +Routine Description: + Sets up cache flush and calls assembly function to restore external interrupt. + Removes registered callback from IvtEntryTable. + +Arguments: + Nothing + +Returns: + + Nothing + +--*/ +{ + VOID *Start; + + Start = (VOID *) ((UINT8 *) GetIva () + 0x400 * EXCEPT_IPF_EXTERNAL_INTERRUPT + 0x400); + UnchainHandler (); + InstructionCacheFlush (Start, 0x400); + IvtEntryTable[EXCEPT_IPF_EXTERNAL_INTERRUPT].RegisteredCallback = NULL; +} + +// +// The rest of the functions in this file are all member functions for the +// DebugSupport protocol +// + +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ) +/*++ + +Routine Description: This is a DebugSupport protocol member function. Hard + coded to support only 1 processor for now. + +Arguments: + This - The DebugSupport instance + MaxProcessorIndex - The maximuim supported processor index + +Returns: + Always returns EFI_SUCCESS with *MaxProcessorIndex set to 0 + +--*/ +{ + *MaxProcessorIndex = 0; + return (EFI_SUCCESS); +} + +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK NewPeriodicCallback + ) +/*++ + +Routine Description: + DebugSupport protocol member function + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + PeriodicCallback - Callback function + +Returns: + + EFI_STATUS - anything other than EFI_SUCCESS indicates the callback was not registered. + +--*/ +{ + return ManageIvtEntryTable (EXCEPT_IPF_EXTERNAL_INTERRUPT, NULL, NewPeriodicCallback); +} + +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK NewCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ) +/*++ + +Routine Description: + DebugSupport protocol member function + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + NewCallback - Callback function + ExceptionType - Which exception to hook + +Returns: + + EFI_STATUS - anything other than EFI_SUCCESS indicates the callback was not registered. + +--*/ +{ + return ManageIvtEntryTable ( + ExceptionType, + (BUNDLE *) ((EFI_PLABEL *) HookStub)->EntryPoint, + NewCallback + ); +} + +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINTN Length + ) +/*++ + +Routine Description: + DebugSupport protocol member function. Calls assembly routine to flush cache. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + Start - Physical base of the memory range to be invalidated + Length - mininum number of bytes in instruction cache to invalidate + +Returns: + EFI_SUCCESS + +--*/ +{ + InstructionCacheFlush (Start, Length); + return (EFI_SUCCESS); +} diff --git a/MdeModulePkg/Universal/DebugSupportDxe/ipf/plDebugSupport.h b/MdeModulePkg/Universal/DebugSupportDxe/ipf/plDebugSupport.h new file mode 100644 index 0000000000..ddd11e6069 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/ipf/plDebugSupport.h @@ -0,0 +1,338 @@ +/**@file + IPF specific debugsupport types, macros, and definitions. + +Copyright (c) 2004 - 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. + +**/ + +#ifndef _PLDEBUG_SUPPORT_H +#define _PLDEBUG_SUPPORT_H + +// +// The package level header files this module uses +// +#include +// +// The protocols, PPI and GUID defintions for this module +// +#include +#include +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include +#include + +#define DISABLE_INTERRUPTS 0UL + +// +// The remaining definitions comprise the protocol members. +// +#define EFI_ISA IsaIpf + +// +// processor specific functions that must be public +// +EFI_STATUS +plInitializeDebugSupportDriver ( + VOID + ) +/*++ + +Routine Description: + IPF specific DebugSupport driver initialization. Must be public because it's + referenced from DebugSupport.c + +Arguments: + +Returns: + + EFI_SUCCESS + +--*/ +; + +EFI_STATUS +EFIAPI +plUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ) +/*++ + +Routine Description: + Unload handler that is called during UnloadImage() - deallocates pool memory + used by the driver. Must be public because it's referenced from DebugSuport.c + +Arguments: + ImageHandle - Image handle + +Returns: + + EFI_STATUS - anything other than EFI_SUCCESS indicates the callback was not registered. + +--*/ +; + +// +// Assembly worker functions and data referenced from plDebugSupport.c +// +VOID * +GetIva ( + VOID + ) +/*++ + +Routine Description: + + C callable function to obtain the current value of IVA + +Arguments: + + None + +Returns: + + Current value if IVA + +--*/ +; + +VOID +HookStub ( + VOID + ) +/*++ + +Routine Description: + + HookStub will be copied from it's loaded location into the IVT when + an IVT entry is hooked. + +Arguments: + + None + +Returns: + + None + +--*/ +; + +VOID +ChainHandler ( + VOID + ) +/*++ + +Routine Description: + + Chains an interrupt handler + +Arguments: + + None + +Returns: + + None + +--*/ +; + +VOID +UnchainHandler ( + VOID + ) +/*++ + +Routine Description: + + Unchains an interrupt handler + +Arguments: + + None + +Returns: + + None + +--*/ +; + +UINT64 +ProgramInterruptFlags ( + IN UINT64 NewInterruptState + ) +/*++ + +Routine Description: + + C callable function to enable/disable interrupts + +Arguments: + + NewInterruptState - New Interrupt State + +Returns: + + Previous state of psr.ic + +--*/ +; + +VOID +InstructionCacheFlush ( + IN VOID *StartAddress, + IN UINTN SizeInBytes + ) +/*++ + +Routine Description: + + Flushes instruction cache for specified number of bytes + +Arguments: + + StartAddress - Cache Start Address + SizeInBytes - Cache Size + +Returns: + + None + +--*/ +; + +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ) +/*++ + +Routine Description: This is a DebugSupport protocol member function. Hard + coded to support only 1 processor for now. + +Arguments: + This - The DebugSupport instance + MaxProcessorIndex - The maximuim supported processor index + +Returns: + Always returns EFI_SUCCESS with *MaxProcessorIndex set to 0 + +--*/ +; + +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ) +/*++ + +Routine Description: + DebugSupport protocol member function + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + PeriodicCallback - Callback function + +Returns: + + EFI_STATUS - anything other than EFI_SUCCESS indicates the callback was not registered. + +--*/ +; + +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK NewHandler, + IN EFI_EXCEPTION_TYPE ExceptionType + ) +/*++ + +Routine Description: + DebugSupport protocol member function + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + NewCallback - Callback function + ExceptionType - Which exception to hook + +Returns: + + EFI_STATUS - anything other than EFI_SUCCESS indicates the callback was not registered. + +--*/ +; + +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINTN Length + ) +/*++ + +Routine Description: + DebugSupport protocol member function. Calls assembly routine to flush cache. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + Start - Physical base of the memory range to be invalidated + Length - mininum number of bytes in instruction cache to invalidate + +Returns: + EFI_SUCCESS + +--*/ +; + +VOID +CommonHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT Context + ) +/*++ + +Routine Description: + C routine that is called for all registered exceptions. This is the main + exception dispatcher. Must be public because it's referenced from AsmFuncs.s. + +Arguments: + ExceptionType - Exception Type + Context - System Context + +Returns: + + Nothing + +--*/ +; + +#endif diff --git a/MdeModulePkg/Universal/DebugSupportDxe/x64/AsmFuncs.S b/MdeModulePkg/Universal/DebugSupportDxe/x64/AsmFuncs.S new file mode 100644 index 0000000000..fb08ce0a69 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/x64/AsmFuncs.S @@ -0,0 +1,57 @@ +#****************************************************************************** +#* +#* Copyright (c) 2007, 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. +#* +#****************************************************************************** + +.globl _OrigVector +.globl _InterruptEntryStub +.globl _StubSize +.globl _CommonIdtEntry +.globl _FxStorSupport + +_AppEsp: .long 0x11111111 # ? +_DebugEsp: .long 0x22222222 # ? +_ExtraPush: .long 0x33333333 # ? +_ExceptData: .long 0x44444444 # ? +_Eflags: .long 0x55555555 # ? +_OrigVector: .long 0x66666666 # ? +_StubSize: .long _InterruptEntryStubEnd - _InterruptEntryStub + +.globl _FxStorSupport +_FxStorSupport: + ret + +.globl _GetIdtr +_GetIdtr: + ret + +.globl _WriteInterruptFlag +_WriteInterruptFlag: + ret + +.globl _Vect2Desc +_Vect2Desc: + ret + +.globl _InterruptEntryStub +_InterruptEntryStub: + ret + +.globl _InterruptEntryStubEnd +_InterruptEntryStubEnd: + ret + +.globl _CommonIdtEntry +_CommonIdtEntry: + ret + +PhonyIretd: + iret diff --git a/MdeModulePkg/Universal/DebugSupportDxe/x64/AsmFuncs.asm b/MdeModulePkg/Universal/DebugSupportDxe/x64/AsmFuncs.asm new file mode 100644 index 0000000000..810de909a8 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/x64/AsmFuncs.asm @@ -0,0 +1,654 @@ +;****************************************************************************** +;* +;* 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. +;* +;****************************************************************************** + +EXCPT64_DIVIDE_ERROR EQU 0 +EXCPT64_DEBUG EQU 1 +EXCPT64_NMI EQU 2 +EXCPT64_BREAKPOINT EQU 3 +EXCPT64_OVERFLOW EQU 4 +EXCPT64_BOUND EQU 5 +EXCPT64_INVALID_OPCODE EQU 6 +EXCPT64_DOUBLE_FAULT EQU 8 +EXCPT64_INVALID_TSS EQU 10 +EXCPT64_SEG_NOT_PRESENT EQU 11 +EXCPT64_STACK_FAULT EQU 12 +EXCPT64_GP_FAULT EQU 13 +EXCPT64_PAGE_FAULT EQU 14 +EXCPT64_FP_ERROR EQU 16 +EXCPT64_ALIGNMENT_CHECK EQU 17 +EXCPT64_MACHINE_CHECK EQU 18 +EXCPT64_SIMD EQU 19 + +FXSTOR_FLAG EQU 01000000h ; bit cpuid 24 of feature flags + +;; The FXSTOR and FXRSTOR commands are used for saving and restoring the x87, +;; MMX, SSE, SSE2, etc registers. The initialization of the debugsupport driver +;; MUST check the CPUID feature flags to see that these instructions are available +;; and fail to init if they are not. + +;; fxstor [rdi] +FXSTOR_RDI MACRO + db 0fh, 0aeh, 00000111y ; mod = 00, reg/op = 000, r/m = 111 = [rdi] +ENDM + +;; fxrstor [rsi] +FXRSTOR_RSI MACRO + db 0fh, 0aeh, 00001110y ; mod = 00, reg/op = 001, r/m = 110 = [rsi] +ENDM + +data SEGMENT + +public OrigVector, InterruptEntryStub, StubSize, CommonIdtEntry, FxStorSupport + +StubSize dd InterruptEntryStubEnd - InterruptEntryStub +AppRsp dq 1111111111111111h ; ? +DebugRsp dq 2222222222222222h ; ? +ExtraPush dq 3333333333333333h ; ? +ExceptData dq 4444444444444444h ; ? +Rflags dq 5555555555555555h ; ? +OrigVector dq 6666666666666666h ; ? + +;; The declarations below define the memory region that will be used for the debug stack. +;; The context record will be built by pushing register values onto this stack. +;; It is imparitive that alignment be carefully managed, since the FXSTOR and +;; FXRSTOR instructions will GP fault if their memory operand is not 16 byte aligned. +;; +;; The stub will switch stacks from the application stack to the debuger stack +;; and pushes the exception number. +;; +;; Then we building the context record on the stack. Since the stack grows down, +;; we push the fields of the context record from the back to the front. There +;; are 336 bytes of stack used prior allocating the 512 bytes of stack to be +;; used as the memory buffer for the fxstor instruction. Therefore address of +;; the buffer used for the FXSTOR instruction is &Eax - 336 - 512, which +;; must be 16 byte aligned. +;; +;; We carefully locate the stack to make this happen. +;; +;; For reference, the context structure looks like this: +;; struct { +;; UINT64 ExceptionData; +;; FX_SAVE_STATE_X64 FxSaveState; // 512 bytes, must be 16 byte aligned +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; +;; UINT64 RFlags; +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; UINT64 Rip; +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; +;; } SYSTEM_CONTEXT_X64; // 64 bit system context record + +align 16 +DebugStackEnd db "DbgStkEnd >>>>>>" ;; 16 byte long string - must be 16 bytes to preserve alignment + dd 1ffch dup (000000000h) ;; 32K should be enough stack + ;; This allocation is coocked to insure + ;; that the the buffer for the FXSTORE instruction + ;; will be 16 byte aligned also. + ;; +ExceptionNumber dq ? ;; first entry will be the vector number pushed by the stub + +DebugStackBegin db "<<<< DbgStkBegin" ;; initial debug ESP == DebugStackBegin, set in stub + +data ENDS + +text SEGMENT + +externdef InterruptDistrubutionHub:near + +;------------------------------------------------------------------------------ +; VOID +; EfiWbinvd ( +; VOID +; ) +; +; Abstract: Writeback and invalidate cache +; +EfiWbinvd PROC PUBLIC + wbinvd + ret +EfiWbinvd ENDP + +;------------------------------------------------------------------------------ +; BOOLEAN +; FxStorSupport ( +; void +; ) +; +; Abstract: Returns TRUE if FxStor instructions are supported +; +FxStorSupport PROC PUBLIC + +; +; cpuid corrupts rbx which must be preserved per the C calling convention +; + push rbx + mov rax, 1 + cpuid + mov eax, edx + and rax, FXSTOR_FLAG + shr rax, 24 + pop rbx + ret +FxStorSupport ENDP + + +;------------------------------------------------------------------------------ +; DESCRIPTOR * +; GetIdtr ( +; void +; ) +; +; Abstract: Returns physical address of IDTR +; +GetIdtr PROC PUBLIC + push rbp + mov rbp, rsp + + sidt QWORD PTR [rbp - 0ah] + mov rax, QWORD PTR [rbp - 8h] + + mov rsp, rbp + pop rbp + ret +GetIdtr ENDP + + +;------------------------------------------------------------------------------ +; BOOLEAN +; WriteInterruptFlag ( +; BOOLEAN NewState // rcx +; ) +; +; Abstract: Programs interrupt flag to the requested state and returns previous +; state. +; +WriteInterruptFlag PROC PUBLIC + + pushfq + pop rax + and rax, 200h + shr rax, 9 + cmp rcx, 0 + jnz EnableIF + cli + ret +EnableIF: + sti + ret + +WriteInterruptFlag ENDP + + + +;------------------------------------------------------------------------------ +; void +; Vect2Desc ( +; DESCRIPTOR * DestDesc, // rcx +; void (*Vector) (void) // rdx +; ) +; +; Abstract: Encodes an IDT descriptor with the given physical address +; +Vect2Desc PROC PUBLIC + + mov rax, rdx + mov word ptr [rcx], ax ; write bits 15..0 of offset + mov dx, cs + mov word ptr [rcx+2], dx ; SYS_CODE_SEL from GDT + mov word ptr [rcx+4], 0e00h OR 8000h ; type = 386 interrupt gate, present + shr rax, 16 + mov word ptr [rcx+6], ax ; write bits 31..16 of offset + shr rax, 16 + mov dword ptr [rcx+8], eax ; write bits 63..32 of offset + + ret + +Vect2Desc ENDP + + + +;------------------------------------------------------------------------------ +; InterruptEntryStub +; +; Abstract: This code is not a function, but is a small piece of code that is +; copied and fixed up once for each IDT entry that is hooked. +; +InterruptEntryStub:: + push 0 ; push vector number - will be modified before installed + db 0e9h ; jump rel32 + dd 0 ; fixed up to relative address of CommonIdtEntry +InterruptEntryStubEnd: + + + +;------------------------------------------------------------------------------ +; CommonIdtEntry +; +; Abstract: This code is not a function, but is the common part for all IDT +; vectors. +; +CommonIdtEntry:: +;; +;; At this point, the stub has saved the current application stack esp into AppRsp +;; and switched stacks to the debug stack, where it pushed the vector number +;; +;; The application stack looks like this: +;; +;; ... +;; (last application stack entry) +;; [16 bytes alignment, do not care it] +;; SS from interrupted task +;; RSP from interrupted task +;; rflags from interrupted task +;; CS from interrupted task +;; RIP from interrupted task +;; Error code <-------------------- Only present for some exeption types +;; +;; Vector Number <----------------- pushed in our IDT Entry +;; + + +;; The stub switched us to the debug stack and pushed the interrupt number. +;; +;; Next, construct the context record. It will be build on the debug stack by +;; pushing the registers in the correct order so as to create the context structure +;; on the debug stack. The context record must be built from the end back to the +;; beginning because the stack grows down... +; +;; For reference, the context record looks like this: +;; +;; typedef +;; struct { +;; UINT64 ExceptionData; +;; FX_SAVE_STATE_X64 FxSaveState; +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; UINT64 Cr0, Cr2, Cr3, Cr4, Cr8; +;; UINT64 RFlags; +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; UINT64 Rip; +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; +;; } SYSTEM_CONTEXT_X64; // 64 bit system context record + +;; NOTE: we save rsp here to prevent compiler put rip reference cause error AppRsp + push rax + mov rax, qword ptr [rsp][8] ; save vector number + mov ExceptionNumber, rax ; save vector number + pop rax + add rsp, 8 ; pop vector number + mov AppRsp, rsp ; save stack top + mov rsp, offset DebugStackBegin ; switch to debugger stack + sub rsp, 8 ; leave space for vector number + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rax + push rcx + push rdx + push rbx + push rsp + push rbp + push rsi + push rdi + +;; Save interrupt state rflags register... + pushfq + pop rax + mov qword ptr Rflags, rax + +;; We need to determine if any extra data was pushed by the exception, and if so, save it +;; To do this, we check the exception number pushed by the stub, and cache the +;; result in a variable since we'll need this again. + cmp ExceptionNumber, EXCPT64_DOUBLE_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_INVALID_TSS + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_SEG_NOT_PRESENT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_STACK_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_GP_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_PAGE_FAULT + jz ExtraPushOne + cmp ExceptionNumber, EXCPT64_ALIGNMENT_CHECK + jz ExtraPushOne + mov ExtraPush, 0 + mov ExceptData, 0 + jmp ExtraPushDone +ExtraPushOne: + mov ExtraPush, 1 + +;; If there's some extra data, save it also, and modify the saved AppRsp to effectively +;; pop this value off the application's stack. + mov rax, AppRsp + mov rbx, [rax] + mov ExceptData, rbx + add rax, 8 + mov AppRsp, rax + +ExtraPushDone: + +;; The "push" above pushed the debug stack rsp. Since what we're actually doing +;; is building the context record on the debug stack, we need to save the pushed +;; debug RSP, and replace it with the application's last stack entry... + mov rax, [rsp + 24] + mov DebugRsp, rax + mov rax, AppRsp + add rax, 40 + ; application stack has ss, rsp, rflags, cs, & rip, so + ; last actual application stack entry is + ; 40 bytes into the application stack. + mov [rsp + 24], rax + +;; continue building context record +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + mov rax, ss + push rax + + ; CS from application is one entry back in application stack + mov rax, AppRsp + movzx rax, word ptr [rax + 8] + push rax + + mov rax, ds + push rax + mov rax, es + push rax + mov rax, fs + push rax + mov rax, gs + push rax + +;; UINT64 Rip; + ; Rip from application is on top of application stack + mov rax, AppRsp + push qword ptr [rax] + +;; UINT64 Gdtr[2], Idtr[2]; + push 0 + push 0 + sidt fword ptr [rsp] + push 0 + push 0 + sgdt fword ptr [rsp] + +;; UINT64 Ldtr, Tr; + xor rax, rax + str ax + push rax + sldt ax + push rax + +;; UINT64 RFlags; +;; Rflags from application is two entries back in application stack + mov rax, AppRsp + push qword ptr [rax + 16] + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; +;; insure FXSAVE/FXRSTOR is enabled in CR4... +;; ... while we're at it, make sure DE is also enabled... + mov rax, cr8 + push rax + mov rax, cr4 + or rax, 208h + mov cr4, rax + push rax + mov rax, cr3 + push rax + mov rax, cr2 + push rax + push 0 + mov rax, cr0 + push rax + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov rax, dr7 + push rax +;; clear Dr7 while executing debugger itself + xor rax, rax + mov dr7, rax + + mov rax, dr6 + push rax +;; insure all status bits in dr6 are clear... + xor rax, rax + mov dr6, rax + + mov rax, dr3 + push rax + mov rax, dr2 + push rax + mov rax, dr1 + push rax + mov rax, dr0 + push rax + +;; FX_SAVE_STATE_X64 FxSaveState; + sub rsp, 512 + mov rdi, rsp + ; IMPORTANT!! The debug stack has been carefully constructed to + ; insure that rsp and rdi are 16 byte aligned when we get here. + ; They MUST be. If they are not, a GP fault will occur. + FXSTOR_RDI + +;; UINT64 ExceptionData; + mov rax, ExceptData + push rax + +; call to C code which will in turn call registered handler +; pass in the vector number + mov rdx, rsp + mov rcx, ExceptionNumber + sub rsp, 40 + call InterruptDistrubutionHub + add rsp, 40 + +; restore context... +;; UINT64 ExceptionData; + add rsp, 8 + +;; FX_SAVE_STATE_X64 FxSaveState; + mov rsi, rsp + FXRSTOR_RSI + add rsp, 512 + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + pop rax + mov dr0, rax + pop rax + mov dr1, rax + pop rax + mov dr2, rax + pop rax + mov dr3, rax +;; skip restore of dr6. We cleared dr6 during the context save. + add rsp, 8 + pop rax + mov dr7, rax + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + pop rax + mov cr0, rax + add rsp, 8 + pop rax + mov cr2, rax + pop rax + mov cr3, rax + pop rax + mov cr4, rax + pop rax + mov cr8, rax + +;; UINT64 RFlags; + mov rax, AppRsp + pop qword ptr [rax + 16] + +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add rsp, 48 + +;; UINT64 Rip; + pop qword ptr [rax] + +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; +;; NOTE - modified segment registers could hang the debugger... We +;; could attempt to insulate ourselves against this possibility, +;; but that poses risks as well. +;; + + pop rax + ; mov gs, rax + pop rax + ; mov fs, rax + pop rax + mov es, rax + pop rax + mov ds, rax + mov rax, AppRsp + pop qword ptr [rax + 8] + pop rax + mov ss, rax + +;; The next stuff to restore is the general purpose registers that were pushed +;; using the "push" instruction. +;; +;; The value of RSP as stored in the context record is the application RSP +;; including the 5 entries on the application stack caused by the exception +;; itself. It may have been modified by the debug agent, so we need to +;; determine if we need to relocate the application stack. + + mov rbx, [rsp + 24] ; move the potentially modified AppRsp into rbx + mov rax, AppRsp + add rax, 40 + cmp rbx, rax + je NoAppStackMove + + mov rax, AppRsp + mov rcx, [rax] ; RIP + mov [rbx], rcx + + mov rcx, [rax + 8] ; CS + mov [rbx + 8], rcx + + mov rcx, [rax + 16] ; RFLAGS + mov [rbx + 16], rcx + + mov rcx, [rax + 24] ; RSP + mov [rbx + 24], rcx + + mov rcx, [rax + 32] ; SS + mov [rbx + 32], rcx + + mov rax, rbx ; modify the saved AppRsp to the new AppRsp + mov AppRsp, rax +NoAppStackMove: + mov rax, DebugRsp ; restore the DebugRsp on the debug stack + ; so our "pop" will not cause a stack switch + mov [rsp + 24], rax + + cmp ExceptionNumber, 068h + jne NoChain + +Chain: + +;; Restore rflags so when we chain, the flags will be exactly as if we were never here. +;; We gin up the stack to do an iretq so we can get ALL the flags. + mov rax, AppRsp + mov rbx, [rax + 40] + push rbx + mov rax, ss + push rax + mov rax, rsp + add rax, 16 + push rax + mov rax, AppRsp + mov rbx, [rax + 16] + and rbx, NOT 300h ; special handling for IF and TF + push rbx + mov rax, cs + push rax + mov rax, offset PhonyIretq + push rax + iretq +PhonyIretq: + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pop rdi + pop rsi + pop rbp + pop rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + +;; Switch back to application stack + mov rsp, AppRsp + +;; Jump to original handler + jmp OrigVector + +NoChain: +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pop rdi + pop rsi + pop rbp + pop rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + +;; Switch back to application stack + mov rsp, AppRsp + +;; We're outa here... + iretq +text ENDS + +END + + + diff --git a/MdeModulePkg/Universal/DebugSupportDxe/x64/plDebugSupport.c b/MdeModulePkg/Universal/DebugSupportDxe/x64/plDebugSupport.c new file mode 100644 index 0000000000..8b907ee6e1 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/x64/plDebugSupport.c @@ -0,0 +1,446 @@ +/**@file + X64 specific debug support functions + +Copyright (c) 2006 - 2007, 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. + +**/ + +// +// private header files +// +#include "plDebugSupport.h" + +// +// This the global main table to keep track of the interrupts +// +IDT_ENTRY *IdtEntryTable = NULL; +DESCRIPTOR NullDesc = {0, 0}; + +STATIC +EFI_STATUS +CreateEntryStub ( + IN EFI_EXCEPTION_TYPE ExceptionType, + OUT VOID **Stub + ) +/*++ + +Routine Description: Allocate pool for a new IDT entry stub. Copy the generic + stub into the new buffer and fixup the vector number and jump target address. + +Arguments: + ExceptionType - This is the exception type that the new stub will be created + for. + Stub - On successful exit, *Stub contains the newly allocated entry stub. +Returns: + Typically EFI_SUCCESS + other possibilities are passed through from AllocatePool + +--*/ +{ + UINT8 *StubCopy; + + StubCopy = *Stub; + + // + // Fixup the stub code for this vector + // + + // The stub code looks like this: + // + // 00000000 6A 00 push 0 ; push vector number - will be modified before installed + // 00000002 E9 db 0e9h ; jump rel32 + // 00000003 00000000 dd 0 ; fixed up to relative address of CommonIdtEntry + // + + // + // poke in the exception type so the second push pushes the exception type + // + StubCopy[0x1] = (UINT8) ExceptionType; + + // + // fixup the jump target to point to the common entry + // + *(UINT32 *) &StubCopy[0x3] = (UINT32)((UINTN) CommonIdtEntry - (UINTN) &StubCopy[StubSize]); + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +HookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN VOID (*NewCallback) () + ) +/*++ + +Routine Description: + Creates a nes entry stub. Then saves the current IDT entry and replaces it + with an interrupt gate for the new entry point. The IdtEntryTable is updated + with the new registered function. + + This code executes in boot services context. The stub entry executes in interrupt + context. + +Arguments: + ExceptionType - specifies which vector to hook. + NewCallback - a pointer to the new function to be registered. + +Returns: + EFI_SUCCESS + Other possibilities are passed through by CreateEntryStub + +--*/ +{ + BOOLEAN OldIntFlagState; + EFI_STATUS Status; + + Status = CreateEntryStub (ExceptionType, (VOID **) &IdtEntryTable[ExceptionType].StubEntry); + if (Status == EFI_SUCCESS) { + OldIntFlagState = WriteInterruptFlag (0); + ReadIdt (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc)); + + ((UINT16 *) &IdtEntryTable[ExceptionType].OrigVector)[0] = ((UINT16 *) &IdtEntryTable[ExceptionType].OrigDesc.Low)[0]; + ((UINT16 *) &IdtEntryTable[ExceptionType].OrigVector)[1] = ((UINT16 *) &IdtEntryTable[ExceptionType].OrigDesc.Low)[3]; + ((UINT32 *) &IdtEntryTable[ExceptionType].OrigVector)[1] = ((UINT32 *) &IdtEntryTable[ExceptionType].OrigDesc.High)[0]; + + Vect2Desc (&IdtEntryTable[ExceptionType].NewDesc, IdtEntryTable[ExceptionType].StubEntry); + IdtEntryTable[ExceptionType].RegisteredCallback = NewCallback; + WriteIdt (ExceptionType, &(IdtEntryTable[ExceptionType].NewDesc)); + WriteInterruptFlag (OldIntFlagState); + } + + return Status; +} + +STATIC +EFI_STATUS +UnhookEntry ( + IN EFI_EXCEPTION_TYPE ExceptionType + ) +/*++ + +Routine Description: + Undoes HookEntry. This code executes in boot services context. + +Arguments: + ExceptionType - specifies which entry to unhook + +Returns: + EFI_SUCCESS + +--*/ +{ + BOOLEAN OldIntFlagState; + + OldIntFlagState = WriteInterruptFlag (0); + WriteIdt (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc)); + WriteInterruptFlag (OldIntFlagState); + + return EFI_SUCCESS; +} + +EFI_STATUS +ManageIdtEntryTable ( + VOID (*NewCallback)(), + EFI_EXCEPTION_TYPE ExceptionType + ) +/*++ + +Routine Description: + This is the main worker function that manages the state of the interrupt + handlers. It both installs and uninstalls interrupt handlers based on the + value of NewCallback. If NewCallback is NULL, then uninstall is indicated. + If NewCallback is non-NULL, then install is indicated. + +Arguments: + NewCallback - If non-NULL, NewCallback specifies the new handler to register. + If NULL, specifies that the previously registered handler should + be uninstalled. + ExceptionType - Indicates which entry to manage + +Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has + no handler registered for it + EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered. + + Other possible return values are passed through from UnHookEntry and HookEntry. + +--*/ +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (CompareDescriptor (&IdtEntryTable[ExceptionType].NewDesc, &NullDesc)) { + // + // we've already installed to this vector + // + if (NewCallback != NULL) { + // + // if the input handler is non-null, error + // + Status = EFI_ALREADY_STARTED; + } else { + Status = UnhookEntry (ExceptionType); + } + } else { + // + // no user handler installed on this vector + // + if (NewCallback == NULL) { + // + // if the input handler is null, error + // + Status = EFI_INVALID_PARAMETER; + } else { + Status = HookEntry (ExceptionType, NewCallback); + } + } + + return Status; +} + +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ) +/*++ + +Routine Description: This is a DebugSupport protocol member function. + +Arguments: + This - The DebugSupport instance + MaxProcessorIndex - The maximuim supported processor index + +Returns: + Always returns EFI_SUCCESS with *MaxProcessorIndex set to 0 + +--*/ +{ + *MaxProcessorIndex = 0; + return (EFI_SUCCESS); +} + +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ) +/*++ + +Routine Description: This is a DebugSupport protocol member function. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + PeriodicCallback - Callback function + +Returns: + + EFI_SUCCESS + EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has + no handler registered for it + EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered. + + Other possible return values are passed through from UnHookEntry and HookEntry. + +--*/ +{ + return ManageIdtEntryTable (PeriodicCallback, SYSTEM_TIMER_VECTOR); +} + +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK NewCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ) +/*++ + +Routine Description: + This is a DebugSupport protocol member function. + + This code executes in boot services context. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + NewCallback - Callback function + ExceptionType - Which exception to hook + +Returns: + + EFI_SUCCESS + EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has + no handler registered for it + EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered. + + Other possible return values are passed through from UnHookEntry and HookEntry. + +--*/ +{ + return ManageIdtEntryTable (NewCallback, ExceptionType); +} + +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINT64 Length + ) +/*++ + +Routine Description: + This is a DebugSupport protocol member function. + Calls assembly routine to flush cache. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + Start - Physical base of the memory range to be invalidated + Length - mininum number of bytes in instruction cache to invalidate + +Returns: + + EFI_SUCCESS - always return success + +--*/ +{ + AsmWbinvd (); + return EFI_SUCCESS; +} + +EFI_STATUS +plInitializeDebugSupportDriver ( + VOID + ) +/*++ + +Routine Description: + Initializes driver's handler registration database. + + This code executes in boot services context. + +Arguments: + None + +Returns: + EFI_SUCCESS + EFI_UNSUPPORTED - if X64 processor does not support FXSTOR/FXRSTOR instructions, + the context save will fail, so these processor's are not supported. + EFI_OUT_OF_RESOURCES - not resource to finish initialization + +--*/ +{ + EFI_EXCEPTION_TYPE ExceptionType; + + if (!FxStorSupport ()) { + return EFI_UNSUPPORTED; + } + + IdtEntryTable = AllocateZeroPool (sizeof (IDT_ENTRY) * NUM_IDT_ENTRIES); + if (IdtEntryTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { + IdtEntryTable[ExceptionType].StubEntry = (DEBUG_PROC) (UINTN) AllocatePool (StubSize); + if (IdtEntryTable[ExceptionType].StubEntry == NULL) { + goto ErrorCleanup; + } + + CopyMem ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry, InterruptEntryStub, StubSize); + } + return EFI_SUCCESS; + +ErrorCleanup: + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { + if (IdtEntryTable[ExceptionType].StubEntry != NULL) { + FreePool ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry); + } + } + FreePool (IdtEntryTable); + + return EFI_OUT_OF_RESOURCES; +} + +EFI_STATUS +EFIAPI +plUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ) +/*++ + +Routine Description: + This is the callback that is written to the LoadedImage protocol instance + on the image handle. It uninstalls all registered handlers and frees all entry + stub memory. + + This code executes in boot services context. + +Arguments: + ImageHandle - The image handle of the unload handler + +Returns: + + EFI_SUCCESS - always return success + +--*/ +{ + EFI_EXCEPTION_TYPE ExceptionType; + + for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { + ManageIdtEntryTable (NULL, ExceptionType); + } + + FreePool (IdtEntryTable); + return EFI_SUCCESS; +} + +VOID +InterruptDistrubutionHub ( + EFI_EXCEPTION_TYPE ExceptionType, + EFI_SYSTEM_CONTEXT_IA32 *ContextRecord + ) +/*++ + +Routine Description: Common piece of code that invokes the registered handlers. + + This code executes in exception context so no efi calls are allowed. + +Arguments: + ExceptionType - exception type + ContextRecord - system context + +Returns: + + None + +--*/ +{ + if (IdtEntryTable[ExceptionType].RegisteredCallback != NULL) { + if (ExceptionType != SYSTEM_TIMER_VECTOR) { + IdtEntryTable[ExceptionType].RegisteredCallback (ExceptionType, ContextRecord); + } else { + OrigVector = IdtEntryTable[ExceptionType].OrigVector; + IdtEntryTable[ExceptionType].RegisteredCallback (ContextRecord); + } + } +} diff --git a/MdeModulePkg/Universal/DebugSupportDxe/x64/plDebugSupport.h b/MdeModulePkg/Universal/DebugSupportDxe/x64/plDebugSupport.h new file mode 100644 index 0000000000..0f4e2778e6 --- /dev/null +++ b/MdeModulePkg/Universal/DebugSupportDxe/x64/plDebugSupport.h @@ -0,0 +1,342 @@ +/**@file + X64 specific debug support macros, typedefs and prototypes. + +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. + +**/ + +#ifndef _PLDEBUG_SUPPORT_H +#define _PLDEBUG_SUPPORT_H + +// +// The package level header files this module uses +// +#include +// +// The protocols, PPI and GUID defintions for this module +// +#include +#include +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include + +#include +#define NUM_IDT_ENTRIES 0x78 +#define SYSTEM_TIMER_VECTOR 0x68 +#define VECTOR_ENTRY_PAGES 1 +#define CopyDescriptor(Dest, Src) CopyMem ((Dest), (Src), sizeof (DESCRIPTOR)) +#define ZeroDescriptor(Dest) CopyDescriptor ((Dest), &NullDesc) +#define ReadIdt(Vector, Dest) CopyDescriptor ((Dest), &((GetIdtr ())[(Vector)])) +#define WriteIdt(Vector, Src) CopyDescriptor (&((GetIdtr ())[(Vector)]), (Src)) +#define CompareDescriptor(Desc1, Desc2) CompareMem ((Desc1), (Desc2), sizeof (DESCRIPTOR)) +#define EFI_ISA IsaX64 +#define FF_FXSR (1 << 24) + +typedef struct { + UINT64 Low; + UINT64 High; +} DESCRIPTOR; + +typedef +VOID +(*DEBUG_PROC) ( + VOID + ) +; + +typedef struct { + DESCRIPTOR OrigDesc; + DEBUG_PROC OrigVector; + DESCRIPTOR NewDesc; + DEBUG_PROC StubEntry; + VOID (*RegisteredCallback) (); +} IDT_ENTRY; + +extern EFI_SYSTEM_CONTEXT SystemContext; +extern UINT8 InterruptEntryStub[]; +extern UINT32 StubSize; +extern VOID (*OrigVector) (VOID); + +VOID +CommonIdtEntry ( + VOID + ) +/*++ + +Routine Description: + + Generic IDT entry + +Arguments: + + None + +Returns: + + None + +--*/ +; + + +BOOLEAN +FxStorSupport ( + VOID + ) +/*++ + +Routine Description: + + Check whether FXSTOR is supported + +Arguments: + + None + +Returns: + + TRUE - supported + FALSE - not supported + +--*/ +; + +DESCRIPTOR * +GetIdtr ( + VOID + ) +/*++ + +Routine Description: + + Return the physical address of IDTR + +Arguments: + + None + +Returns: + + The physical address of IDTR + +--*/ +; + +VOID +Vect2Desc ( + DESCRIPTOR * DestDesc, + VOID (*Vector) (VOID) + ) +/*++ + +Routine Description: + + Encodes an IDT descriptor with the given physical address + +Arguments: + + DestDesc - The IDT descriptor address + Vector - The interrupt vector entry + +Returns: + + None + +--*/ +; + +BOOLEAN +WriteInterruptFlag ( + BOOLEAN NewState + ) +/*++ + +Routine Description: + + Programs interrupt flag to the requested state and returns previous + state. + +Arguments: + + NewState - New interrupt status + +Returns: + + Old interrupt status + +--*/ +; + +EFI_STATUS +plInitializeDebugSupportDriver ( + VOID + ) +/*++ + +Routine Description: + Initializes driver's handler registration database. + + This code executes in boot services context. + +Arguments: + None + +Returns: + EFI_SUCCESS + EFI_UNSUPPORTED - if X64 processor does not support FXSTOR/FXRSTOR instructions, + the context save will fail, so these processor's are not supported. + EFI_OUT_OF_RESOURCES - not resource to finish initialization + +--*/ +; + +EFI_STATUS +EFIAPI +plUnloadDebugSupportDriver ( + IN EFI_HANDLE ImageHandle + ) +/*++ + +Routine Description: + This is the callback that is written to the LoadedImage protocol instance + on the image handle. It uninstalls all registered handlers and frees all entry + stub memory. + + This code executes in boot services context. + +Arguments: + ImageHandle - The image handle of the unload handler + +Returns: + + EFI_SUCCESS - always return success + +--*/ +; + +// +// DebugSupport protocol member functions +// +EFI_STATUS +EFIAPI +GetMaximumProcessorIndex ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ) +/*++ + +Routine Description: This is a DebugSupport protocol member function. + +Arguments: + This - The DebugSupport instance + MaxProcessorIndex - The maximuim supported processor index + +Returns: + Always returns EFI_SUCCESS with *MaxProcessorIndex set to 0 + +--*/ +; + +EFI_STATUS +EFIAPI +RegisterPeriodicCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ) +/*++ + +Routine Description: This is a DebugSupport protocol member function. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + PeriodicCallback - Callback function + +Returns: + + EFI_SUCCESS + EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has + no handler registered for it + EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered. + + Other possible return values are passed through from UnHookEntry and HookEntry. + +--*/ +; + +EFI_STATUS +EFIAPI +RegisterExceptionCallback ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK NewCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ) +/*++ + +Routine Description: + This is a DebugSupport protocol member function. + + This code executes in boot services context. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + NewCallback - Callback function + ExceptionType - Which exception to hook + +Returns: + + EFI_SUCCESS + EFI_INVALID_PARAMETER - requested uninstalling a handler from a vector that has + no handler registered for it + EFI_ALREADY_STARTED - requested install to a vector that already has a handler registered. + + Other possible return values are passed through from UnHookEntry and HookEntry. + +--*/ +; + +EFI_STATUS +EFIAPI +InvalidateInstructionCache ( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINT64 Length + ) +/*++ + +Routine Description: + This is a DebugSupport protocol member function. + Calls assembly routine to flush cache. + +Arguments: + This - The DebugSupport instance + ProcessorIndex - Which processor the callback applies to. + Start - Physical base of the memory range to be invalidated + Length - mininum number of bytes in instruction cache to invalidate + +Returns: + + EFI_SUCCESS - always return success + +--*/ +; + +#endif