1/* 2 * Copyright 2004-2008, Axel D��rfler, axeld@pinc-software.de. 3 * Copyright 2013-2014, Fredrik Holmqvist, fredrik.holmqvist@gmail.com. 4 * Copyright 2016, Jessica Hamilton, jessica.l.hamilton@gmail.com. 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9#include "efi_platform.h" 10#include <efi/protocol/serial-io.h> 11#include "serial.h" 12 13#include <boot/platform.h> 14#include <arch/cpu.h> 15#include <arch/generic/debug_uart.h> 16#include <arch/generic/debug_uart_8250.h> 17#include <boot/stage2.h> 18#include <boot/stdio.h> 19 20#include <string.h> 21 22 23static efi_guid sSerialIOProtocolGUID = EFI_SERIAL_IO_PROTOCOL_GUID; 24static const uint32 kSerialBaudRate = 115200; 25 26static efi_serial_io_protocol *sEFISerialIO = NULL; 27static bool sSerialEnabled = false; 28static bool sEFIAvailable = true; 29 30 31DebugUART* gUART = NULL; 32bool gUARTSkipInit = false; 33 34 35static void 36serial_putc(char ch) 37{ 38 if (!sSerialEnabled) 39 return; 40 41 // First we use EFI serial_io output if available 42 if (sEFISerialIO != NULL) { 43 size_t bufSize = 1; 44 sEFISerialIO->Write(sEFISerialIO, &bufSize, &ch); 45 return; 46 } 47 48#ifdef DEBUG 49 // If we don't have EFI serial_io, fallback to EFI stdio 50 if (sEFIAvailable) { 51 char16_t ucsBuffer[2]; 52 ucsBuffer[0] = ch; 53 ucsBuffer[1] = 0; 54 kSystemTable->ConOut->OutputString(kSystemTable->ConOut, ucsBuffer); 55 return; 56 } 57#endif 58 59 // If EFI services are unavailable... try any UART 60 // this can happen when serial_io is unavailable, or EFI 61 // is exiting 62 if (gUART != NULL) { 63 gUART->PutChar(ch); 64 return; 65 } 66} 67 68 69extern "C" void 70serial_puts(const char* string, size_t size) 71{ 72 if (!sSerialEnabled) 73 return; 74 75 while (size-- != 0) { 76 char ch = string[0]; 77 78 if (ch == '\n') { 79 serial_putc('\r'); 80 serial_putc('\n'); 81 } else if (ch != '\r') 82 serial_putc(ch); 83 84 string++; 85 } 86} 87 88 89extern "C" void 90serial_disable(void) 91{ 92 sSerialEnabled = false; 93} 94 95 96extern "C" void 97serial_enable(void) 98{ 99 sSerialEnabled = true; 100 if ((gUART != NULL) && !gUARTSkipInit) 101 gUART->InitPort(kSerialBaudRate); 102} 103 104 105extern "C" void 106serial_init(void) 107{ 108 if (sEFIAvailable) { 109 // Check for EFI Serial 110 efi_status status = kSystemTable->BootServices->LocateProtocol( 111 &sSerialIOProtocolGUID, NULL, (void**)&sEFISerialIO); 112 113 if (status != EFI_SUCCESS) 114 sEFISerialIO = NULL; 115 116 if (sEFISerialIO != NULL) { 117 // Setup serial, 0, 0 = Default Receive FIFO queue and default timeout 118 status = sEFISerialIO->SetAttributes(sEFISerialIO, kSerialBaudRate, 0, 0, NoParity, 8, 119 OneStopBit); 120 121 if (status != EFI_SUCCESS) 122 sEFISerialIO = NULL; 123 124 // serial_io was successful. 125 return; 126 } 127 } 128 129#if defined(__i386__) || defined(__x86_64__) 130 // On x86, we can try to setup COM1 as a gUART too 131 // while this serial port may not physically exist, 132 // the location is fixed on the x86 arch. 133 // TODO: We could also try to pull from acpi? 134 if (gUART == NULL) { 135 gUART = arch_get_uart_8250(0x3f8, 1843200); 136 137 // TODO: convert over to exclusively arch_args.uart? 138 memset(gKernelArgs.platform_args.serial_base_ports, 0, 139 sizeof(uint16) * MAX_SERIAL_PORTS); 140 gKernelArgs.platform_args.serial_base_ports[0] = 0x3f8; 141 } 142#endif 143 144 if (gUART != NULL) 145 gUART->InitEarly(); 146} 147 148 149extern "C" void 150serial_kernel_handoff(void) 151{ 152 // The console was provided by boot services, disable it ASAP 153 stdout = NULL; 154 stderr = NULL; 155 156 // Disconnect from EFI serial_io services is important as we leave the bootloader 157 sEFISerialIO = NULL; 158 sEFIAvailable = false; 159} 160