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