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 "efiser.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 = SERIAL_IO_PROTOCOL; 22 static const uint32 kSerialBaudRate = 115200; 23 24 static SERIAL_IO_INTERFACE *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 UINTN bufSize = 1; 52 sSerial->Write(sSerial, &bufSize, &ch); 53 } else { 54 while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0) 55 asm volatile ("pause;"); 56 57 out8(ch, sSerialBasePort + SERIAL_TRANSMIT_BUFFER); 58 } 59 } 60 61 62 extern "C" void 63 serial_puts(const char* string, size_t size) 64 { 65 if (!sSerialEnabled || (sSerial == NULL && sSerialUsesEFI)) 66 return; 67 68 while (size-- != 0) { 69 char ch = string[0]; 70 71 if (ch == '\n') { 72 serial_putc('\r'); 73 serial_putc('\n'); 74 } else if (ch != '\r') 75 serial_putc(ch); 76 77 string++; 78 } 79 } 80 81 82 extern "C" void 83 serial_disable(void) 84 { 85 sSerialEnabled = false; 86 } 87 88 89 extern "C" void 90 serial_enable(void) 91 { 92 sSerialEnabled = true; 93 } 94 95 96 extern "C" void 97 serial_init(void) 98 { 99 EFI_STATUS status = kSystemTable->BootServices->LocateProtocol( 100 &sSerialIOProtocolGUID, NULL, (void**)&sSerial); 101 102 if (status != EFI_SUCCESS || sSerial == NULL) { 103 sSerial = NULL; 104 return; 105 } 106 107 // Setup serial, 0, 0 = Default Receive FIFO queue and default timeout 108 status = sSerial->SetAttributes(sSerial, kSerialBaudRate, 0, 0, NoParity, 8, 109 OneStopBit); 110 111 if (status != EFI_SUCCESS) { 112 sSerial = NULL; 113 return; 114 } 115 } 116 117 118 extern "C" void 119 serial_switch_to_legacy(void) 120 { 121 sSerial = NULL; 122 sSerialUsesEFI = false; 123 124 memset(gKernelArgs.platform_args.serial_base_ports, 0, 125 sizeof(uint16) * MAX_SERIAL_PORTS); 126 127 gKernelArgs.platform_args.serial_base_ports[0] = sSerialBasePort; 128 129 uint16 divisor = uint16(115200 / kSerialBaudRate); 130 131 out8(0x80, sSerialBasePort + SERIAL_LINE_CONTROL); 132 // set divisor latch access bit 133 out8(divisor & 0xf, sSerialBasePort + SERIAL_DIVISOR_LATCH_LOW); 134 out8(divisor >> 8, sSerialBasePort + SERIAL_DIVISOR_LATCH_HIGH); 135 out8(3, sSerialBasePort + SERIAL_LINE_CONTROL); 136 // 8N1 137 } 138