xref: /haiku/src/system/boot/platform/efi/serial.cpp (revision 8a5fc3866a981b0b9cb7b6bd7c207c3bfe0ad460)
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 	// Check for EFI Serial
108 	efi_status status = kSystemTable->BootServices->LocateProtocol(
109 		&sSerialIOProtocolGUID, NULL, (void**)&sEFISerialIO);
110 
111 	if (status != EFI_SUCCESS)
112 		sEFISerialIO = NULL;
113 
114 	if (sEFISerialIO != NULL) {
115 		// Setup serial, 0, 0 = Default Receive FIFO queue and default timeout
116 		status = sEFISerialIO->SetAttributes(sEFISerialIO, kSerialBaudRate, 0, 0, NoParity, 8,
117 			OneStopBit);
118 
119 		if (status != EFI_SUCCESS)
120 			sEFISerialIO = NULL;
121 
122 		// serial_io was successful.
123 	}
124 
125 #if defined(__i386__) || defined(__x86_64__)
126 	// On x86, we can try to setup COM1 as a gUART too
127 	// while this serial port may not physically exist,
128 	// the location is fixed on the x86 arch.
129 	// TODO: We could also try to pull from acpi?
130 	if (gUART == NULL) {
131 		gUART = arch_get_uart_8250(0x3f8, 1843200);
132 		gUART->InitEarly();
133 	}
134 #endif
135 }
136 
137 
138 extern "C" void
139 serial_kernel_handoff(void)
140 {
141 	// The console was provided by boot services, disable it ASAP
142 	stdout = NULL;
143 	stderr = NULL;
144 
145 	// Disconnect from EFI serial_io services is important as we leave the bootloader
146 	sEFISerialIO = NULL;
147 	sEFIAvailable = false;
148 
149 #if defined(__i386__) || defined(__x86_64__)
150 	// TODO: convert over to exclusively arch_args.uart?
151 	memset(gKernelArgs.platform_args.serial_base_ports, 0,
152 		sizeof(uint16) * MAX_SERIAL_PORTS);
153 	gKernelArgs.platform_args.serial_base_ports[0] = 0x3f8;
154 #endif
155 }
156