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 (SerialPort.Flags & COMPORT_FLAG_INIT);
 | 
						|
}
 |