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 33 34 static void 35 serial_putc(char ch) 36 { 37 if (!sSerialEnabled) 38 return; 39 40 // First we use EFI serial_io output if available 41 if (sEFISerialIO != NULL) { 42 size_t bufSize = 1; 43 sEFISerialIO->Write(sEFISerialIO, &bufSize, &ch); 44 return; 45 } 46 47 #ifdef DEBUG 48 // If we don't have EFI serial_io, fallback to EFI stdio 49 if (sEFIAvailable) { 50 char16_t ucsBuffer[2]; 51 ucsBuffer[0] = ch; 52 ucsBuffer[1] = 0; 53 kSystemTable->ConOut->OutputString(kSystemTable->ConOut, ucsBuffer); 54 return; 55 } 56 #endif 57 58 // If EFI services are unavailable... try any UART 59 // this can happen when serial_io is unavailable, or EFI 60 // is exiting 61 if (gUART != NULL) { 62 gUART->PutChar(ch); 63 return; 64 } 65 } 66 67 68 extern "C" void 69 serial_puts(const char* string, size_t size) 70 { 71 if (!sSerialEnabled) 72 return; 73 74 while (size-- != 0) { 75 char ch = string[0]; 76 77 if (ch == '\n') { 78 serial_putc('\r'); 79 serial_putc('\n'); 80 } else if (ch != '\r') 81 serial_putc(ch); 82 83 string++; 84 } 85 } 86 87 88 extern "C" void 89 serial_disable(void) 90 { 91 sSerialEnabled = false; 92 } 93 94 95 extern "C" void 96 serial_enable(void) 97 { 98 sSerialEnabled = true; 99 if (gUART != NULL) 100 gUART->InitPort(kSerialBaudRate); 101 } 102 103 104 extern "C" void 105 serial_init(void) 106 { 107 if (sEFIAvailable) { 108 // Check for EFI Serial 109 efi_status status = kSystemTable->BootServices->LocateProtocol( 110 &sSerialIOProtocolGUID, NULL, (void**)&sEFISerialIO); 111 112 if (status != EFI_SUCCESS) 113 sEFISerialIO = NULL; 114 115 if (sEFISerialIO != NULL) { 116 // Setup serial, 0, 0 = Default Receive FIFO queue and default timeout 117 status = sEFISerialIO->SetAttributes(sEFISerialIO, kSerialBaudRate, 0, 0, NoParity, 8, 118 OneStopBit); 119 120 if (status != EFI_SUCCESS) 121 sEFISerialIO = NULL; 122 123 // serial_io was successful. 124 return; 125 } 126 } 127 128 #if defined(__i386__) || defined(__x86_64__) 129 // On x86, we can try to setup COM1 as a gUART too 130 // while this serial port may not physically exist, 131 // the location is fixed on the x86 arch. 132 // TODO: We could also try to pull from acpi? 133 if (gUART == NULL) { 134 gUART = arch_get_uart_8250(0x3f8, 1843200); 135 136 // TODO: convert over to exclusively arch_args.uart? 137 memset(gKernelArgs.platform_args.serial_base_ports, 0, 138 sizeof(uint16) * MAX_SERIAL_PORTS); 139 gKernelArgs.platform_args.serial_base_ports[0] = 0x3f8; 140 } 141 #endif 142 143 if (gUART != NULL) 144 gUART->InitEarly(); 145 } 146 147 148 extern "C" void 149 serial_kernel_handoff(void) 150 { 151 // The console was provided by boot services, disable it ASAP 152 stdout = NULL; 153 stderr = NULL; 154 155 // Disconnect from EFI serial_io services is important as we leave the bootloader 156 sEFISerialIO = NULL; 157 sEFIAvailable = false; 158 } 159