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