xref: /haiku/src/system/boot/platform/efi/serial.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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 <arch/generic/debug_uart_8250.h>
17 #include <boot/stage2.h>
18 #include <boot/stdio.h>
19 
20 #include <string.h>
21 
22 
23 static efi_guid sSerialIOProtocolGUID = EFI_SERIAL_IO_PROTOCOL_GUID;
24 static const uint32 kSerialBaudRate = 115200;
25 
26 static efi_serial_io_protocol *sEFISerialIO = NULL;
27 static bool sSerialEnabled = false;
28 static bool sEFIAvailable = true;
29 
30 
31 DebugUART* gUART = NULL;
32 bool gUARTSkipInit = false;
33 
34 
35 static void
36 serial_putc(char ch)
37 {
38 	if (!sSerialEnabled)
39 		return;
40 
41 	// First we use EFI serial_io output if available
42 	if (sEFISerialIO != NULL) {
43 		size_t bufSize = 1;
44 		sEFISerialIO->Write(sEFISerialIO, &bufSize, &ch);
45 		return;
46 	}
47 
48 #ifdef DEBUG
49 	// If we don't have EFI serial_io, fallback to EFI stdio
50 	if (sEFIAvailable) {
51 		char16_t ucsBuffer[2];
52 		ucsBuffer[0] = ch;
53 		ucsBuffer[1] = 0;
54 		kSystemTable->ConOut->OutputString(kSystemTable->ConOut, ucsBuffer);
55 		return;
56 	}
57 #endif
58 
59 	// If EFI services are unavailable... try any UART
60 	// this can happen when serial_io is unavailable, or EFI
61 	// is exiting
62 	if (gUART != NULL) {
63 		gUART->PutChar(ch);
64 		return;
65 	}
66 }
67 
68 
69 extern "C" void
70 serial_puts(const char* string, size_t size)
71 {
72 	if (!sSerialEnabled)
73 		return;
74 
75 	while (size-- != 0) {
76 		char ch = string[0];
77 
78 		if (ch == '\n') {
79 			serial_putc('\r');
80 			serial_putc('\n');
81 		} else if (ch != '\r')
82 			serial_putc(ch);
83 
84 		string++;
85 	}
86 }
87 
88 
89 extern "C" void
90 serial_disable(void)
91 {
92 	sSerialEnabled = false;
93 }
94 
95 
96 extern "C" void
97 serial_enable(void)
98 {
99 	sSerialEnabled = true;
100 	if ((gUART != NULL) && !gUARTSkipInit)
101 		gUART->InitPort(kSerialBaudRate);
102 }
103 
104 
105 extern "C" void
106 serial_init(void)
107 {
108 	if (sEFIAvailable) {
109 		// Check for EFI Serial
110 		efi_status status = kSystemTable->BootServices->LocateProtocol(
111 			&sSerialIOProtocolGUID, NULL, (void**)&sEFISerialIO);
112 
113 		if (status != EFI_SUCCESS)
114 			sEFISerialIO = NULL;
115 
116 		if (sEFISerialIO != NULL) {
117 			// Setup serial, 0, 0 = Default Receive FIFO queue and default timeout
118 			status = sEFISerialIO->SetAttributes(sEFISerialIO, kSerialBaudRate, 0, 0, NoParity, 8,
119 				OneStopBit);
120 
121 			if (status != EFI_SUCCESS)
122 				sEFISerialIO = NULL;
123 
124 			// serial_io was successful.
125 			return;
126 		}
127 	}
128 
129 #if defined(__i386__) || defined(__x86_64__)
130 	// On x86, we can try to setup COM1 as a gUART too
131 	// while this serial port may not physically exist,
132 	// the location is fixed on the x86 arch.
133 	// TODO: We could also try to pull from acpi?
134 	if (gUART == NULL) {
135 		gUART = arch_get_uart_8250(0x3f8, 1843200);
136 
137 		// TODO: convert over to exclusively arch_args.uart?
138 		memset(gKernelArgs.platform_args.serial_base_ports, 0,
139 			sizeof(uint16) * MAX_SERIAL_PORTS);
140 		gKernelArgs.platform_args.serial_base_ports[0] = 0x3f8;
141 	}
142 #endif
143 
144 	if (gUART != NULL)
145 		gUART->InitEarly();
146 }
147 
148 
149 extern "C" void
150 serial_kernel_handoff(void)
151 {
152 	// The console was provided by boot services, disable it ASAP
153 	stdout = NULL;
154 	stderr = NULL;
155 
156 	// Disconnect from EFI serial_io services is important as we leave the bootloader
157 	sEFISerialIO = NULL;
158 	sEFIAvailable = false;
159 }
160