403 lines
13 KiB
C++
403 lines
13 KiB
C++
/**
|
|
* PROJECT: ExectOS
|
|
* COPYRIGHT: See COPYING.md in the top level directory
|
|
* FILE: xtldr/debug.cc
|
|
* DESCRIPTION: XT Boot Loader debugging support
|
|
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
|
|
*/
|
|
|
|
#include <xtldr.hh>
|
|
|
|
|
|
/**
|
|
* Enables I/O space access to all serial controllers found on the PCI(E) root bridge.
|
|
*
|
|
* @return This routine returns a status code.
|
|
*
|
|
* @since XT 1.0
|
|
*/
|
|
XTCDECL
|
|
EFI_STATUS
|
|
Debug::ActivateSerialIOController()
|
|
{
|
|
EFI_GUID PciGuid = EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID;
|
|
PEFI_PCI_ROOT_BRIDGE_IO_PROTOCOL PciDev;
|
|
USHORT Bus, Device, Function, Command;
|
|
UINT_PTR Index, PciHandleSize;
|
|
PEFI_HANDLE PciHandle = NULLPTR;
|
|
PCI_COMMON_HEADER PciHeader;
|
|
EFI_STATUS Status;
|
|
ULONGLONG Address;
|
|
|
|
/* Allocate memory for single EFI_HANDLE, what should be enough in most cases */
|
|
PciHandleSize = sizeof(EFI_HANDLE);
|
|
Status = Memory::AllocatePool(PciHandleSize, (PVOID*)&PciHandle);
|
|
if(Status != STATUS_EFI_SUCCESS)
|
|
{
|
|
/* Memory allocation failure */
|
|
return Status;
|
|
}
|
|
|
|
/* Get all instances of PciRootBridgeIo */
|
|
Status = XtLoader::GetEfiSystemTable()->BootServices->LocateHandle(ByProtocol, &PciGuid, NULLPTR,
|
|
&PciHandleSize, PciHandle);
|
|
if(Status == STATUS_EFI_BUFFER_TOO_SMALL)
|
|
{
|
|
/* Reallocate more memory as requested by UEFI */
|
|
Memory::FreePool(PciHandle);
|
|
Status = Memory::AllocatePool(PciHandleSize, (PVOID*)&PciHandle);
|
|
if(Status != STATUS_EFI_SUCCESS)
|
|
{
|
|
/* Memory reallocation failure */
|
|
return Status;
|
|
}
|
|
|
|
/* Second attempt to get instances of PciRootBridgeIo */
|
|
Status = XtLoader::GetEfiSystemTable()->BootServices->LocateHandle(ByProtocol, &PciGuid, NULLPTR,
|
|
&PciHandleSize, PciHandle);
|
|
}
|
|
|
|
/* Make sure successfully obtained PciRootBridgeIo instances */
|
|
if(Status != STATUS_EFI_SUCCESS)
|
|
{
|
|
/* Failed to get PciRootBridgeIo instances */
|
|
return Status;
|
|
}
|
|
|
|
/* Enumerate all devices for each handle, which decides a segment and a bus number range */
|
|
for(Index = 0; Index < (PciHandleSize / sizeof(EFI_HANDLE)); Index++)
|
|
{
|
|
/* Get inferface from the protocol */
|
|
Status = XtLoader::GetEfiSystemTable()->BootServices->HandleProtocol(PciHandle[Index], &PciGuid, (PVOID*)&PciDev);
|
|
if(Status != STATUS_EFI_SUCCESS)
|
|
{
|
|
/* Failed to get interface */
|
|
return Status;
|
|
}
|
|
|
|
/* Enumerate whole PCI bridge */
|
|
for(Bus = 0; Bus <= PCI_MAX_BRIDGE_NUMBER; Bus++)
|
|
{
|
|
/* Enumerate all devices for each bus */
|
|
for(Device = 0; Device < PCI_MAX_DEVICES; Device++)
|
|
{
|
|
/* Enumerate all functions for each devices */
|
|
for(Function = 0; Function < PCI_MAX_FUNCTION; Function++)
|
|
{
|
|
/* Read configuration space */
|
|
Address = ((ULONGLONG)((((UINT_PTR) Bus) << 24) + (((UINT_PTR) Device) << 16) +
|
|
(((UINT_PTR) Function) << 8) + ((UINT_PTR) 0)));
|
|
PciDev->Pci.Read(PciDev, EfiPciIoWidthUint32, Address, sizeof (PciHeader) / sizeof (UINT), &PciHeader);
|
|
|
|
/* Check if device exists */
|
|
if(PciHeader.VendorId == PCI_INVALID_VENDORID)
|
|
{
|
|
/* Skip non-existen device */
|
|
continue;
|
|
}
|
|
|
|
/* Check if device is serial controller or multiport serial controller */
|
|
if(PciHeader.BaseClass == 0x07 && (PciHeader.SubClass == 0x00 || PciHeader.SubClass == 0x02))
|
|
{
|
|
/* Enable I/O space access */
|
|
Address |= 0x4;
|
|
Command = PCI_ENABLE_IO_SPACE;
|
|
Status = PciDev->Pci.Write(PciDev, EfiPciIoWidthUint16, Address, 1, &Command);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return SUCCESS */
|
|
return STATUS_EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* This routine initializes the XTLDR debug console.
|
|
*
|
|
* @return This routine returns a status code.
|
|
*
|
|
* @since XT 1.0
|
|
*/
|
|
XTCDECL
|
|
EFI_STATUS
|
|
Debug::InitializeDebugConsole()
|
|
{
|
|
ULONG PortAddress, PortNumber, BaudRate;
|
|
PWCHAR DebugConfiguration, DebugPort, LastPort;
|
|
EFI_STATUS Status;
|
|
|
|
/* Set default serial port options */
|
|
PortAddress = 0;
|
|
PortNumber = 0;
|
|
BaudRate = 0;
|
|
|
|
/* Get debug configuration */
|
|
Configuration::GetValue(L"DEBUG", &DebugConfiguration);
|
|
|
|
/* Make sure any debug options are provided and debug console is not initialized yet */
|
|
if(DebugConfiguration && EnabledDebugPorts == 0)
|
|
{
|
|
/* Find all debug ports */
|
|
DebugPort = RTL::WideString::TokenizeWideString(DebugConfiguration, L";", &LastPort);
|
|
|
|
/* Iterate over all debug ports */
|
|
while(DebugPort != NULLPTR)
|
|
{
|
|
/* Check what port is set for debugging */
|
|
if(RTL::WideString::CompareWideStringInsensitive(DebugPort, L"COM", 3) == 0)
|
|
{
|
|
/* Read COM port number */
|
|
DebugPort += 3;
|
|
while(*DebugPort >= '0' && *DebugPort <= '9')
|
|
{
|
|
/* Get port number */
|
|
PortNumber *= 10;
|
|
PortNumber += *DebugPort - '0';
|
|
DebugPort++;
|
|
}
|
|
|
|
/* Check if custom COM port address supplied */
|
|
if(PortNumber == 0 && RTL::WideString::CompareWideStringInsensitive(DebugPort, L":0x", 3) == 0)
|
|
{
|
|
/* COM port address provided */
|
|
DebugPort += 3;
|
|
while((*DebugPort >= '0' && *DebugPort <= '9') ||
|
|
(*DebugPort >= 'A' && *DebugPort <= 'F') ||
|
|
(*DebugPort >= 'a' && *DebugPort <= 'f'))
|
|
{
|
|
/* Get port address */
|
|
PortAddress *= 16;
|
|
if(*DebugPort >= '0' && *DebugPort <= '9')
|
|
{
|
|
PortAddress += *DebugPort - '0';
|
|
}
|
|
else if(*DebugPort >= 'A' && *DebugPort <= 'F')
|
|
{
|
|
PortAddress += *DebugPort - 'A' + 10;
|
|
}
|
|
else if(*DebugPort >= 'a' && *DebugPort <= 'f')
|
|
{
|
|
PortAddress += *DebugPort - 'a' + 10;
|
|
}
|
|
DebugPort++;
|
|
}
|
|
}
|
|
|
|
/* Look for additional COM port parameters */
|
|
if(*DebugPort == ',')
|
|
{
|
|
/* Baud rate provided */
|
|
DebugPort++;
|
|
while(*DebugPort >= '0' && *DebugPort <= '9')
|
|
{
|
|
/* Get baud rate */
|
|
BaudRate *= 10;
|
|
BaudRate += *DebugPort - '0';
|
|
DebugPort++;
|
|
}
|
|
}
|
|
|
|
/* Enable debug port */
|
|
EnabledDebugPorts |= XTBL_DEBUGPORT_SERIAL;
|
|
}
|
|
else if(RTL::WideString::CompareWideStringInsensitive(DebugPort, L"SCREEN", 5) == 0)
|
|
{
|
|
/* Enable debug port */
|
|
EnabledDebugPorts |= XTBL_DEBUGPORT_SCREEN;
|
|
}
|
|
else
|
|
{
|
|
/* Unsupported debug port specified */
|
|
Console::Print(L"ERROR: Unsupported debug port ('%S') specified\n", DebugPort);
|
|
EfiUtils::SleepExecution(3000);
|
|
}
|
|
|
|
/* Take next debug port */
|
|
DebugPort = RTL::WideString::TokenizeWideString(NULLPTR, L";", &LastPort);
|
|
}
|
|
|
|
/* Check if serial debug port is enabled */
|
|
if(EnabledDebugPorts & XTBL_DEBUGPORT_SERIAL)
|
|
{
|
|
/* Try to initialize COM port */
|
|
Status = InitializeSerialPort(PortNumber, PortAddress, BaudRate);
|
|
if(Status != STATUS_EFI_SUCCESS)
|
|
{
|
|
/* Remove serial debug port, as COM port initialization failed and return */
|
|
EnabledDebugPorts &= ~XTBL_DEBUGPORT_SERIAL;
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return success */
|
|
return STATUS_EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* This routine initializes the serial debug console.
|
|
*
|
|
* @param PortNumber
|
|
* Supplies a port number.
|
|
*
|
|
* @param PortAddress
|
|
* Supplies an address of the COM port.
|
|
*
|
|
* @param BaudRate
|
|
* Supplies an optional port baud rate.
|
|
*
|
|
* @return This routine returns a status code.
|
|
*
|
|
* @since XT 1.0
|
|
*/
|
|
XTCDECL
|
|
EFI_STATUS
|
|
Debug::InitializeSerialPort(IN ULONG PortNumber,
|
|
IN ULONG PortAddress,
|
|
IN ULONG BaudRate)
|
|
{
|
|
EFI_STATUS EfiStatus;
|
|
XTSTATUS Status;
|
|
|
|
/* Check if custom COM port address supplied */
|
|
if(!PortAddress)
|
|
{
|
|
/* We support only a pre-defined number of ports */
|
|
if(PortNumber > COMPORT_COUNT)
|
|
{
|
|
/* Fail if wrong/unsupported port used */
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check if serial port is set */
|
|
if(PortNumber == 0)
|
|
{
|
|
/* Use COM1 by default */
|
|
PortNumber = 1;
|
|
}
|
|
|
|
/* Set custom port address based on the port number and print debug message */
|
|
PortAddress = ComPortList[PortNumber - 1];
|
|
Console::Print(L"Initializing serial console at port COM%d\n", PortNumber);
|
|
}
|
|
else
|
|
{
|
|
/* Custom port address supplied, print debug message */
|
|
Console::Print(L"Initializing serial console at COM port address: 0x%lX\n", PortAddress);
|
|
}
|
|
|
|
/* Initialize COM port */
|
|
Status = HL::ComPort::InitializeComPort(&SerialPort, (PUCHAR)UlongToPtr(PortAddress), BaudRate);
|
|
|
|
/* Port not found under supplied address */
|
|
if(Status == STATUS_NOT_FOUND && PortAddress)
|
|
{
|
|
/* This might be PCI(E) serial controller, try to activate I/O space access first */
|
|
EfiStatus = ActivateSerialIOController();
|
|
if(EfiStatus == STATUS_EFI_SUCCESS)
|
|
{
|
|
/* Try to reinitialize COM port */
|
|
Console::Print(L"Enabled I/O space access for all PCI(E) serial controllers found\n");
|
|
Status = HL::ComPort::InitializeComPort(&SerialPort, (PUCHAR)UlongToPtr(PortAddress), BaudRate);
|
|
}
|
|
}
|
|
|
|
/* Check COM port initialization status code */
|
|
if(Status != STATUS_SUCCESS)
|
|
{
|
|
/* Serial port initialization failed, mark as not ready */
|
|
return STATUS_EFI_NOT_READY;
|
|
}
|
|
|
|
/* Return success */
|
|
return STATUS_EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* This routine formats the input string and prints it out to the debug ports.
|
|
*
|
|
* @param Format
|
|
* The formatted string that is to be written to the output.
|
|
*
|
|
* @param ...
|
|
* Depending on the format string, this routine might expect a sequence of additional arguments.
|
|
*
|
|
* @return This routine does not return any value.
|
|
*
|
|
* @since XT 1.0
|
|
*/
|
|
XTCDECL
|
|
VOID
|
|
Debug::Print(IN PCWSTR Format,
|
|
IN ...)
|
|
{
|
|
RTL_PRINT_CONTEXT ConsolePrintContext, SerialPrintContext;
|
|
VA_LIST Arguments;
|
|
|
|
/* Check if debugging enabled and if EFI serial port is fully initialized */
|
|
if(DEBUG)
|
|
{
|
|
/* Initialize the print contexts */
|
|
ConsolePrintContext.WriteWideCharacter = Console::PutChar;
|
|
SerialPrintContext.WriteWideCharacter = PutChar;
|
|
|
|
/* Initialise the va_list */
|
|
VA_START(Arguments, Format);
|
|
|
|
/* Check if serial debug port is enabled */
|
|
if((EnabledDebugPorts & XTBL_DEBUGPORT_SERIAL) && (SerialPort.Flags & COMPORT_FLAG_INIT))
|
|
{
|
|
/* Format and print the string to the serial console */
|
|
RTL::WideString::FormatWideString(&SerialPrintContext, (PWCHAR)Format, Arguments);
|
|
}
|
|
|
|
/* Check if screen debug port is enabled and Boot Services are still available */
|
|
if((EnabledDebugPorts & XTBL_DEBUGPORT_SCREEN) && (XtLoader::GetBootServicesStatus() == TRUE))
|
|
{
|
|
/* Format and print the string to the screen */
|
|
RTL::WideString::FormatWideString(&ConsolePrintContext, (PWCHAR)Format, Arguments);
|
|
}
|
|
|
|
/* Clean up the va_list */
|
|
VA_END(Arguments);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes a character to the serial console.
|
|
*
|
|
* @param Character
|
|
* The integer promotion of the character to be written.
|
|
*
|
|
* @return This routine returns a status code.
|
|
*
|
|
* @since XT 1.0
|
|
*/
|
|
XTCDECL
|
|
XTSTATUS
|
|
Debug::PutChar(IN WCHAR Character)
|
|
{
|
|
WCHAR Buffer[2];
|
|
|
|
/* Write character to the serial console */
|
|
Buffer[0] = Character;
|
|
Buffer[1] = 0;
|
|
return HL::ComPort::WriteComPort(&SerialPort, Buffer[0]);
|
|
}
|
|
|
|
/**
|
|
* Determines if the serial port has been successfully initialized and is ready for communication.
|
|
*
|
|
* @return This routine returns TRUE if the serial port is initialized and ready, FALSE otherwise.
|
|
*
|
|
* @since XT 1.0
|
|
*/
|
|
XTCDECL
|
|
BOOLEAN
|
|
Debug::SerialPortReady()
|
|
{
|
|
return (BOOLEAN)(SerialPort.Flags & COMPORT_FLAG_INIT);
|
|
}
|