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 23 static efi_guid sSerialIOProtocolGUID = EFI_SERIAL_IO_PROTOCOL_GUID; 24 static const uint32 kSerialBaudRate = 115200; 25 26 static efi_serial_io_protocol *sEFISerialIO = NULL; 27 static bool sSerialEnabled = false; 28 static bool sEFIAvailable = true; 29 30 31 DebugUART* gUART = NULL; 32 bool gUARTSkipInit = false; 33 34 35 static void 36 serial_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 69 extern "C" void 70 serial_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 89 extern "C" void 90 serial_disable(void) 91 { 92 sSerialEnabled = false; 93 } 94 95 96 extern "C" void 97 serial_enable(void) 98 { 99 sSerialEnabled = true; 100 if ((gUART != NULL) && !gUARTSkipInit) 101 gUART->InitPort(kSerialBaudRate); 102 } 103 104 105 extern "C" void 106 serial_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 149 extern "C" void 150 serial_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