xref: /haiku/src/system/boot/platform/efi/serial.cpp (revision 17889a8c70dbb3d59c1412f6431968753c767bab)
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