/*++ 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: ConsoleOption.c Abstract: handles console redirection from boot manager Revision History --*/ #include "BootMaint.h" EFI_DEVICE_PATH_PROTOCOL * DevicePathInstanceDup ( IN EFI_DEVICE_PATH_PROTOCOL *DevPath ); EFI_STATUS UpdateComAttributeFromVariable ( EFI_DEVICE_PATH_PROTOCOL *DevicePath ); EFI_STATUS ChangeTerminalDevicePath ( EFI_DEVICE_PATH_PROTOCOL *DevicePath, BOOLEAN ChangeTerminal ) { EFI_DEVICE_PATH_PROTOCOL *Node; EFI_DEVICE_PATH_PROTOCOL *Node1; ACPI_HID_DEVICE_PATH *Acpi; UART_DEVICE_PATH *Uart; UART_DEVICE_PATH *Uart1; UINTN Com; UINT32 Match; BM_TERMINAL_CONTEXT *NewTerminalContext; BM_MENU_ENTRY *NewMenuEntry; Match = EISA_PNP_ID (0x0501); Node = DevicePath; Node = NextDevicePathNode (Node); Com = 0; while (!IsDevicePathEnd (Node)) { if ((DevicePathType (Node) == ACPI_DEVICE_PATH) && (DevicePathSubType (Node) == ACPI_DP)) { Acpi = (ACPI_HID_DEVICE_PATH *) Node; if (CompareMem (&Acpi->HID, &Match, sizeof (UINT32)) == 0) { CopyMem (&Com, &Acpi->UID, sizeof (UINT32)); } } NewMenuEntry = BOpt_GetMenuEntry (&TerminalMenu, Com); if (NULL == NewMenuEntry) { return EFI_NOT_FOUND; } NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext; if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) && (DevicePathSubType (Node) == MSG_UART_DP)) { Uart = (UART_DEVICE_PATH *) Node; CopyMem ( &Uart->BaudRate, &NewTerminalContext->BaudRate, sizeof (UINT64) ); CopyMem ( &Uart->DataBits, &NewTerminalContext->DataBits, sizeof (UINT8) ); CopyMem ( &Uart->Parity, &NewTerminalContext->Parity, sizeof (UINT8) ); CopyMem ( &Uart->StopBits, &NewTerminalContext->StopBits, sizeof (UINT8) ); // // Change the device path in the ComPort // if (ChangeTerminal) { Node1 = NewTerminalContext->DevicePath; Node1 = NextDevicePathNode (Node1); while (!IsDevicePathEnd (Node1)) { if ((DevicePathType (Node1) == MESSAGING_DEVICE_PATH) && (DevicePathSubType (Node1) == MSG_UART_DP)) { Uart1 = (UART_DEVICE_PATH *) Node1; CopyMem ( &Uart1->BaudRate, &NewTerminalContext->BaudRate, sizeof (UINT64) ); CopyMem ( &Uart1->DataBits, &NewTerminalContext->DataBits, sizeof (UINT8) ); CopyMem ( &Uart1->Parity, &NewTerminalContext->Parity, sizeof (UINT8) ); CopyMem ( &Uart1->StopBits, &NewTerminalContext->StopBits, sizeof (UINT8) ); break; } // // end if // Node1 = NextDevicePathNode (Node1); } // // end while // break; } } Node = NextDevicePathNode (Node); } return EFI_SUCCESS; } VOID ChangeVariableDevicePath ( EFI_DEVICE_PATH_PROTOCOL *DevicePath ) { EFI_DEVICE_PATH_PROTOCOL *Node; ACPI_HID_DEVICE_PATH *Acpi; UART_DEVICE_PATH *Uart; UINTN Com; UINT32 Match; BM_TERMINAL_CONTEXT *NewTerminalContext; BM_MENU_ENTRY *NewMenuEntry; Match = EISA_PNP_ID (0x0501); Node = DevicePath; Node = NextDevicePathNode (Node); Com = 0; while (!IsDevicePathEnd (Node)) { if ((DevicePathType (Node) == ACPI_DEVICE_PATH) && (DevicePathSubType (Node) == ACPI_DP)) { Acpi = (ACPI_HID_DEVICE_PATH *) Node; if (CompareMem (&Acpi->HID, &Match, sizeof (UINT32)) == 0) { CopyMem (&Com, &Acpi->UID, sizeof (UINT32)); } } if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) && (DevicePathSubType (Node) == MSG_UART_DP)) { NewMenuEntry = BOpt_GetMenuEntry ( &TerminalMenu, Com ); NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext; Uart = (UART_DEVICE_PATH *) Node; CopyMem ( &Uart->BaudRate, &NewTerminalContext->BaudRate, sizeof (UINT64) ); CopyMem ( &Uart->DataBits, &NewTerminalContext->DataBits, sizeof (UINT8) ); CopyMem ( &Uart->Parity, &NewTerminalContext->Parity, sizeof (UINT8) ); CopyMem ( &Uart->StopBits, &NewTerminalContext->StopBits, sizeof (UINT8) ); } Node = NextDevicePathNode (Node); } return ; } BOOLEAN IsTerminalDevicePath ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, OUT TYPE_OF_TERMINAL *Termi, OUT UINTN *Com ); EFI_STATUS LocateSerialIo ( VOID ) /*++ Routine Description: Build a list containing all serial devices Arguments: Returns: --*/ { UINT8 *Ptr; UINTN Index; UINTN Index2; UINTN NoHandles; EFI_HANDLE *Handles; EFI_STATUS Status; ACPI_HID_DEVICE_PATH *Acpi; EFI_DEVICE_PATH_PROTOCOL *DevicePath; UINT32 Match; EFI_SERIAL_IO_PROTOCOL *SerialIo; EFI_DEVICE_PATH_PROTOCOL *OutDevicePath; EFI_DEVICE_PATH_PROTOCOL *InpDevicePath; EFI_DEVICE_PATH_PROTOCOL *ErrDevicePath; BM_MENU_ENTRY *NewMenuEntry; BM_TERMINAL_CONTEXT *NewTerminalContext; EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; VENDOR_DEVICE_PATH Vendor; // // Get all handles that have SerialIo protocol installed // InitializeListHead (&TerminalMenu.Head); TerminalMenu.MenuNumber = 0; Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiSerialIoProtocolGuid, NULL, &NoHandles, &Handles ); if (EFI_ERROR (Status)) { // // No serial ports present // return EFI_UNSUPPORTED; } for (Index = 0; Index < NoHandles; Index++) { // // Check to see whether the handle has DevicePath Protocol installed // gBS->HandleProtocol ( Handles[Index], &gEfiDevicePathProtocolGuid, (VOID**) &DevicePath ); Ptr = (UINT8 *) DevicePath; while (*Ptr != END_DEVICE_PATH_TYPE) { Ptr++; } Ptr = Ptr - sizeof (UART_DEVICE_PATH) - sizeof (ACPI_HID_DEVICE_PATH); Acpi = (ACPI_HID_DEVICE_PATH *) Ptr; Match = EISA_PNP_ID (0x0501); if (CompareMem (&Acpi->HID, &Match, sizeof (UINT32)) == 0) { NewMenuEntry = BOpt_CreateMenuEntry (BM_TERMINAL_CONTEXT_SELECT); if (!NewMenuEntry) { return EFI_OUT_OF_RESOURCES; } NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext; CopyMem (&NewMenuEntry->OptionNumber, &Acpi->UID, sizeof (UINT32)); NewTerminalContext->DevicePath = DevicePathInstanceDup (DevicePath); // // BugBug: I have no choice, calling EfiLibStrFromDatahub will hang the system! // coz' the misc data for each platform is not correct, actually it's the device path stored in // datahub which is not completed, so a searching for end of device path will enter a // dead-loop. // NewMenuEntry->DisplayString = EfiLibStrFromDatahub (DevicePath); if (NULL == NewMenuEntry->DisplayString) { NewMenuEntry->DisplayString = DevicePathToStr (DevicePath); } NewMenuEntry->HelpString = NULL; gBS->HandleProtocol ( Handles[Index], &gEfiSerialIoProtocolGuid, (VOID**) &SerialIo ); CopyMem ( &NewTerminalContext->BaudRate, &SerialIo->Mode->BaudRate, sizeof (UINT64) ); CopyMem ( &NewTerminalContext->DataBits, &SerialIo->Mode->DataBits, sizeof (UINT8) ); CopyMem ( &NewTerminalContext->Parity, &SerialIo->Mode->Parity, sizeof (UINT8) ); CopyMem ( &NewTerminalContext->StopBits, &SerialIo->Mode->StopBits, sizeof (UINT8) ); InsertTailList (&TerminalMenu.Head, &NewMenuEntry->Link); TerminalMenu.MenuNumber++; } } // // Get L"ConOut", L"ConIn" and L"ErrOut" from the Var // OutDevicePath = EfiLibGetVariable (L"ConOut", &gEfiGlobalVariableGuid); InpDevicePath = EfiLibGetVariable (L"ConIn", &gEfiGlobalVariableGuid); ErrDevicePath = EfiLibGetVariable (L"ErrOut", &gEfiGlobalVariableGuid); if (OutDevicePath) { UpdateComAttributeFromVariable (OutDevicePath); } if (InpDevicePath) { UpdateComAttributeFromVariable (InpDevicePath); } if (ErrDevicePath) { UpdateComAttributeFromVariable (ErrDevicePath); } for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) { NewMenuEntry = BOpt_GetMenuEntry (&TerminalMenu, Index); if (NULL == NewMenuEntry) { return EFI_NOT_FOUND; } NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext; NewTerminalContext->TerminalType = 0; NewTerminalContext->IsConIn = FALSE; NewTerminalContext->IsConOut = FALSE; NewTerminalContext->IsStdErr = FALSE; Vendor.Header.Type = MESSAGING_DEVICE_PATH; Vendor.Header.SubType = MSG_VENDOR_DP; for (Index2 = 0; Index2 < 4; Index2++) { CopyMem (&Vendor.Guid, &Guid[Index2], sizeof (EFI_GUID)); SetDevicePathNodeLength (&Vendor.Header, sizeof (VENDOR_DEVICE_PATH)); NewDevicePath = AppendDevicePathNode ( NewTerminalContext->DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &Vendor ); SafeFreePool (NewMenuEntry->HelpString); // // NewMenuEntry->HelpString = DevicePathToStr (NewDevicePath); // NewMenuEntry->DisplayString = NewMenuEntry->HelpString; // NewMenuEntry->HelpString = NULL; if (BdsLibMatchDevicePaths (OutDevicePath, NewDevicePath)) { NewTerminalContext->IsConOut = TRUE; NewTerminalContext->TerminalType = (UINT8) Index2; } if (BdsLibMatchDevicePaths (InpDevicePath, NewDevicePath)) { NewTerminalContext->IsConIn = TRUE; NewTerminalContext->TerminalType = (UINT8) Index2; } if (BdsLibMatchDevicePaths (ErrDevicePath, NewDevicePath)) { NewTerminalContext->IsStdErr = TRUE; NewTerminalContext->TerminalType = (UINT8) Index2; } } } return EFI_SUCCESS; } EFI_STATUS UpdateComAttributeFromVariable ( EFI_DEVICE_PATH_PROTOCOL *DevicePath ) /*++ Routine Description: Update Com Ports attributes from DevicePath Arguments: DevicePath - DevicePath that contains Com ports Returns: --*/ { EFI_DEVICE_PATH_PROTOCOL *Node; EFI_DEVICE_PATH_PROTOCOL *SerialNode; ACPI_HID_DEVICE_PATH *Acpi; UART_DEVICE_PATH *Uart; UART_DEVICE_PATH *Uart1; UINT32 Match; UINTN TerminalNumber; BM_MENU_ENTRY *NewMenuEntry; BM_TERMINAL_CONTEXT *NewTerminalContext; UINTN Index; Match = EISA_PNP_ID (0x0501); Node = DevicePath; Node = NextDevicePathNode (Node); TerminalNumber = 0; for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) { while (!IsDevicePathEnd (Node)) { if ((DevicePathType (Node) == ACPI_DEVICE_PATH) && (DevicePathSubType (Node) == ACPI_DP)) { Acpi = (ACPI_HID_DEVICE_PATH *) Node; if (CompareMem (&Acpi->HID, &Match, sizeof (UINT32)) == 0) { CopyMem (&TerminalNumber, &Acpi->UID, sizeof (UINT32)); } } if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) && (DevicePathSubType (Node) == MSG_UART_DP)) { Uart = (UART_DEVICE_PATH *) Node; NewMenuEntry = BOpt_GetMenuEntry (&TerminalMenu, TerminalNumber); if (NULL == NewMenuEntry) { return EFI_NOT_FOUND; } NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext; CopyMem ( &NewTerminalContext->BaudRate, &Uart->BaudRate, sizeof (UINT64) ); CopyMem ( &NewTerminalContext->DataBits, &Uart->DataBits, sizeof (UINT8) ); CopyMem ( &NewTerminalContext->Parity, &Uart->Parity, sizeof (UINT8) ); CopyMem ( &NewTerminalContext->StopBits, &Uart->StopBits, sizeof (UINT8) ); SerialNode = NewTerminalContext->DevicePath; SerialNode = NextDevicePathNode (SerialNode); while (!IsDevicePathEnd (SerialNode)) { if ((DevicePathType (SerialNode) == MESSAGING_DEVICE_PATH) && (DevicePathSubType (SerialNode) == MSG_UART_DP)) { // // Update following device paths according to // previous acquired uart attributes // Uart1 = (UART_DEVICE_PATH *) SerialNode; CopyMem ( &Uart1->BaudRate, &NewTerminalContext->BaudRate, sizeof (UINT64) ); CopyMem ( &Uart1->DataBits, &NewTerminalContext->DataBits, sizeof (UINT8) ); CopyMem ( &Uart1->Parity, &NewTerminalContext->Parity, sizeof (UINT8) ); CopyMem ( &Uart1->StopBits, &NewTerminalContext->StopBits, sizeof (UINT8) ); break; } SerialNode = NextDevicePathNode (SerialNode); } // // end while // } Node = NextDevicePathNode (Node); } // // end while // } return EFI_SUCCESS; } EFI_DEVICE_PATH_PROTOCOL * DevicePathInstanceDup ( IN EFI_DEVICE_PATH_PROTOCOL *DevPath ) /*++ Routine Description: Function creates a device path data structure that identically matches the device path passed in. Arguments: DevPath - A pointer to a device path data structure. Returns: The new copy of DevPath is created to identically match the input. Otherwise, NULL is returned. --*/ { EFI_DEVICE_PATH_PROTOCOL *NewDevPath; EFI_DEVICE_PATH_PROTOCOL *DevicePathInst; EFI_DEVICE_PATH_PROTOCOL *Temp; UINT8 *Ptr; UINTN Size; // // get the size of an instance from the input // Temp = DevPath; DevicePathInst = GetNextDevicePathInstance (&Temp, &Size); // // Make a copy and set proper end type // NewDevPath = NULL; if (Size) { NewDevPath = AllocateZeroPool (Size); ASSERT (NewDevPath != NULL); } if (NewDevPath) { CopyMem (NewDevPath, DevicePathInst, Size); Ptr = (UINT8 *) NewDevPath; Ptr += Size - sizeof (EFI_DEVICE_PATH_PROTOCOL); Temp = (EFI_DEVICE_PATH_PROTOCOL *) Ptr; SetDevicePathEndNode (Temp); } return NewDevPath; } EFI_STATUS GetConsoleMenu ( IN UINTN ConsoleMenuType ) { EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_DEVICE_PATH_PROTOCOL *AllDevicePath; EFI_DEVICE_PATH_PROTOCOL *MultiDevicePath; EFI_DEVICE_PATH_PROTOCOL *DevicePathInst; UINTN Size; UINTN AllCount; UINTN Index; UINTN Index2; BM_MENU_ENTRY *NewMenuEntry; BM_CONSOLE_CONTEXT *NewConsoleContext; BM_TERMINAL_CONTEXT *NewTerminalContext; TYPE_OF_TERMINAL Terminal; BM_MENU_ENTRY *NewTerminalMenuEntry; UINTN Com; BM_MENU_OPTION *ConsoleMenu; DevicePath = NULL; AllDevicePath = NULL; AllCount = 0; switch (ConsoleMenuType) { case BM_CONSOLE_IN_CONTEXT_SELECT: ConsoleMenu = &ConsoleInpMenu; DevicePath = EfiLibGetVariable ( L"ConIn", &gEfiGlobalVariableGuid ); AllDevicePath = EfiLibGetVariable ( L"ConInDev", &gEfiGlobalVariableGuid ); break; case BM_CONSOLE_OUT_CONTEXT_SELECT: ConsoleMenu = &ConsoleOutMenu; DevicePath = EfiLibGetVariable ( L"ConOut", &gEfiGlobalVariableGuid ); AllDevicePath = EfiLibGetVariable ( L"ConOutDev", &gEfiGlobalVariableGuid ); break; case BM_CONSOLE_ERR_CONTEXT_SELECT: ConsoleMenu = &ConsoleErrMenu; DevicePath = EfiLibGetVariable ( L"ErrOut", &gEfiGlobalVariableGuid ); AllDevicePath = EfiLibGetVariable ( L"ErrOutDev", &gEfiGlobalVariableGuid ); break; default: return EFI_UNSUPPORTED; } if (NULL == AllDevicePath) { return EFI_NOT_FOUND; } InitializeListHead (&ConsoleMenu->Head); AllCount = EfiDevicePathInstanceCount (AllDevicePath); ConsoleMenu->MenuNumber = 0; // // Following is menu building up for Console Out Devices // MultiDevicePath = AllDevicePath; Index2 = 0; for (Index = 0; Index < AllCount; Index++) { DevicePathInst = GetNextDevicePathInstance (&MultiDevicePath, &Size); NewMenuEntry = BOpt_CreateMenuEntry (BM_CONSOLE_CONTEXT_SELECT); if (NULL == NewMenuEntry) { return EFI_OUT_OF_RESOURCES; } NewConsoleContext = (BM_CONSOLE_CONTEXT *) NewMenuEntry->VariableContext; NewMenuEntry->OptionNumber = Index2; NewConsoleContext->DevicePath = DevicePathInstanceDup (DevicePathInst); NewMenuEntry->DisplayString = EfiLibStrFromDatahub (NewConsoleContext->DevicePath); if (NULL == NewMenuEntry->DisplayString) { NewMenuEntry->DisplayString = DevicePathToStr (NewConsoleContext->DevicePath); } NewConsoleContext->IsTerminal = IsTerminalDevicePath ( NewConsoleContext->DevicePath, &Terminal, &Com ); NewConsoleContext->IsActive = BdsLibMatchDevicePaths ( DevicePath, NewConsoleContext->DevicePath ); NewTerminalMenuEntry = NULL; NewTerminalContext = NULL; if (NewConsoleContext->IsTerminal) { BOpt_DestroyMenuEntry (NewMenuEntry); } else { Index2++; ConsoleMenu->MenuNumber++; InsertTailList (&ConsoleMenu->Head, &NewMenuEntry->Link); } } return EFI_SUCCESS; } EFI_STATUS GetAllConsoles ( VOID ) /*++ Routine Description: Build up ConsoleOutMenu, ConsoleInpMenu and ConsoleErrMenu Arguments: Returns: EFI_SUCCESS Others --*/ { GetConsoleMenu (BM_CONSOLE_IN_CONTEXT_SELECT); GetConsoleMenu (BM_CONSOLE_OUT_CONTEXT_SELECT); GetConsoleMenu (BM_CONSOLE_ERR_CONTEXT_SELECT); return EFI_SUCCESS; } EFI_STATUS FreeAllConsoles ( VOID ) /*++ Routine Description: Free ConsoleOutMenu, ConsoleInpMenu and ConsoleErrMenu Arguments: Returns: EFI_SUCCESS Others --*/ { BOpt_FreeMenu (&ConsoleOutMenu); BOpt_FreeMenu (&ConsoleInpMenu); BOpt_FreeMenu (&ConsoleErrMenu); BOpt_FreeMenu (&TerminalMenu); return EFI_SUCCESS; } BOOLEAN IsTerminalDevicePath ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, OUT TYPE_OF_TERMINAL *Termi, OUT UINTN *Com ) /*++ Routine Description: Test whether DevicePath is a valid Terminal Arguments: DevicePath - DevicePath to be checked Termi - If is terminal, give its type Com - If is Com Port, give its type Returns: TRUE - If DevicePath point to a Terminal FALSE --*/ { UINT8 *Ptr; BOOLEAN IsTerminal; VENDOR_DEVICE_PATH *Vendor; ACPI_HID_DEVICE_PATH *Acpi; UINT32 Match; EFI_GUID TempGuid; IsTerminal = FALSE; // // Parse the Device Path, should be change later!!! // Ptr = (UINT8 *) DevicePath; while (*Ptr != END_DEVICE_PATH_TYPE) { Ptr++; } Ptr = Ptr - sizeof (VENDOR_DEVICE_PATH); Vendor = (VENDOR_DEVICE_PATH *) Ptr; // // There are four kinds of Terminal types // check to see whether this devicepath // is one of that type // CopyMem (&TempGuid, &Vendor->Guid, sizeof (EFI_GUID)); if (CompareGuid (&TempGuid, &Guid[0])) { *Termi = PC_ANSI; IsTerminal = TRUE; } else { if (CompareGuid (&TempGuid, &Guid[1])) { *Termi = VT_100; IsTerminal = TRUE; } else { if (CompareGuid (&TempGuid, &Guid[2])) { *Termi = VT_100_PLUS; IsTerminal = TRUE; } else { if (CompareGuid (&TempGuid, &Guid[3])) { *Termi = VT_UTF8; IsTerminal = TRUE; } else { IsTerminal = FALSE; } } } } if (!IsTerminal) { return FALSE; } Ptr = Ptr - sizeof (UART_DEVICE_PATH) - sizeof (ACPI_HID_DEVICE_PATH); Acpi = (ACPI_HID_DEVICE_PATH *) Ptr; Match = EISA_PNP_ID (0x0501); if (CompareMem (&Acpi->HID, &Match, sizeof (UINT32)) == 0) { CopyMem (Com, &Acpi->UID, sizeof (UINT32)); } else { return FALSE; } return TRUE; }