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