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 <boot/stage2.h> 17 #include <boot/stdio.h> 18 19 #include <string.h> 20 21 22 static efi_guid sSerialIOProtocolGUID = EFI_SERIAL_IO_PROTOCOL_GUID; 23 static const uint32 kSerialBaudRate = 115200; 24 25 static efi_serial_io_protocol *sSerial = NULL; 26 static bool sSerialEnabled = false; 27 static bool sSerialUsesEFI = true; 28 29 30 enum serial_register_offsets { 31 SERIAL_TRANSMIT_BUFFER = 0, 32 SERIAL_RECEIVE_BUFFER = 0, 33 SERIAL_DIVISOR_LATCH_LOW = 0, 34 SERIAL_DIVISOR_LATCH_HIGH = 1, 35 SERIAL_FIFO_CONTROL = 2, 36 SERIAL_LINE_CONTROL = 3, 37 SERIAL_MODEM_CONTROL = 4, 38 SERIAL_LINE_STATUS = 5, 39 SERIAL_MODEM_STATUS = 6, 40 }; 41 42 static uint16 sSerialBasePort = 0x3f8; 43 44 45 DebugUART* gUART = NULL; 46 47 48 static void 49 serial_putc(char ch) 50 { 51 if (!sSerialEnabled) 52 return; 53 54 if (sSerialUsesEFI && sSerial != NULL) { 55 size_t bufSize = 1; 56 sSerial->Write(sSerial, &bufSize, &ch); 57 return; 58 } 59 60 #ifdef TRACE_DEBUG 61 if (sSerialUsesEFI) { 62 // To aid in early bring-up on EFI platforms, where the 63 // serial_io protocol isn't working/available. 64 char16_t ucsBuffer[2]; 65 ucsBuffer[0] = ch; 66 ucsBuffer[1] = 0; 67 kSystemTable->ConOut->OutputString(kSystemTable->ConOut, ucsBuffer); 68 return; 69 } 70 #endif 71 72 if (gUART != NULL) { 73 gUART->PutChar(ch); 74 return; 75 } 76 77 #if defined(__i386__) || defined(__x86_64__) 78 while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0) 79 asm volatile ("pause;"); 80 81 out8(ch, sSerialBasePort + SERIAL_TRANSMIT_BUFFER); 82 #endif 83 } 84 85 86 extern "C" void 87 serial_puts(const char* string, size_t size) 88 { 89 if (!sSerialEnabled || (sSerial == NULL && sSerialUsesEFI)) 90 return; 91 92 while (size-- != 0) { 93 char ch = string[0]; 94 95 if (ch == '\n') { 96 serial_putc('\r'); 97 serial_putc('\n'); 98 } else if (ch != '\r') 99 serial_putc(ch); 100 101 string++; 102 } 103 } 104 105 106 extern "C" void 107 serial_disable(void) 108 { 109 sSerialEnabled = false; 110 } 111 112 113 extern "C" void 114 serial_enable(void) 115 { 116 sSerialEnabled = true; 117 } 118 119 120 extern "C" void 121 serial_init(void) 122 { 123 efi_status status = kSystemTable->BootServices->LocateProtocol( 124 &sSerialIOProtocolGUID, NULL, (void**)&sSerial); 125 126 if (status != EFI_SUCCESS || sSerial == NULL) { 127 sSerial = NULL; 128 return; 129 } 130 131 // Setup serial, 0, 0 = Default Receive FIFO queue and default timeout 132 status = sSerial->SetAttributes(sSerial, kSerialBaudRate, 0, 0, NoParity, 8, 133 OneStopBit); 134 135 if (status != EFI_SUCCESS) { 136 sSerial = NULL; 137 return; 138 } 139 } 140 141 142 extern "C" void 143 serial_switch_to_legacy(void) 144 { 145 sSerial = NULL; 146 sSerialUsesEFI = false; 147 148 #if defined(__i386__) || defined(__x86_64__) 149 memset(gKernelArgs.platform_args.serial_base_ports, 0, 150 sizeof(uint16) * MAX_SERIAL_PORTS); 151 152 gKernelArgs.platform_args.serial_base_ports[0] = sSerialBasePort; 153 154 uint16 divisor = uint16(115200 / kSerialBaudRate); 155 156 out8(0x80, sSerialBasePort + SERIAL_LINE_CONTROL); 157 // set divisor latch access bit 158 out8(divisor & 0xf, sSerialBasePort + SERIAL_DIVISOR_LATCH_LOW); 159 out8(divisor >> 8, sSerialBasePort + SERIAL_DIVISOR_LATCH_HIGH); 160 out8(3, sSerialBasePort + SERIAL_LINE_CONTROL); 161 // 8N1 162 #endif 163 } 164