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