xref: /haiku/src/system/boot/platform/efi/serial.cpp (revision b9cca28d489479408c158731f3581ab417cd04d0)
19e487d8dSFredrik Holmqvist /*
29e487d8dSFredrik Holmqvist  * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
39e487d8dSFredrik Holmqvist  * Copyright 2013-2014, Fredrik Holmqvist, fredrik.holmqvist@gmail.com.
49e487d8dSFredrik Holmqvist  * Copyright 2016, Jessica Hamilton, jessica.l.hamilton@gmail.com.
59e487d8dSFredrik Holmqvist  * Distributed under the terms of the MIT License.
69e487d8dSFredrik Holmqvist  */
79e487d8dSFredrik Holmqvist 
89e487d8dSFredrik Holmqvist 
99e487d8dSFredrik Holmqvist #include "efi_platform.h"
10485b5cf8SAlexander von Gluck IV #include <efi/protocol/serial-io.h>
119e487d8dSFredrik Holmqvist #include "serial.h"
129e487d8dSFredrik Holmqvist 
139e487d8dSFredrik Holmqvist #include <boot/platform.h>
149e487d8dSFredrik Holmqvist #include <arch/cpu.h>
15f1fa58bfSX512 #include <arch/generic/debug_uart.h>
1681a892edSAlexander von Gluck IV #include <arch/generic/debug_uart_8250.h>
179e487d8dSFredrik Holmqvist #include <boot/stage2.h>
189e487d8dSFredrik Holmqvist #include <boot/stdio.h>
199e487d8dSFredrik Holmqvist 
209e487d8dSFredrik Holmqvist #include <string.h>
219e487d8dSFredrik Holmqvist 
229e487d8dSFredrik Holmqvist 
23485b5cf8SAlexander von Gluck IV static efi_guid sSerialIOProtocolGUID = EFI_SERIAL_IO_PROTOCOL_GUID;
249e487d8dSFredrik Holmqvist static const uint32 kSerialBaudRate = 115200;
259e487d8dSFredrik Holmqvist 
2681a892edSAlexander von Gluck IV static efi_serial_io_protocol *sEFISerialIO = NULL;
279e487d8dSFredrik Holmqvist static bool sSerialEnabled = false;
2881a892edSAlexander von Gluck IV static bool sEFIAvailable = true;
299e487d8dSFredrik Holmqvist 
309e487d8dSFredrik Holmqvist 
31f1fa58bfSX512 DebugUART* gUART = NULL;
32*b9cca28dSDavid Karoly bool gUARTSkipInit = false;
33f1fa58bfSX512 
34f1fa58bfSX512 
359e487d8dSFredrik Holmqvist static void
serial_putc(char ch)369e487d8dSFredrik Holmqvist serial_putc(char ch)
379e487d8dSFredrik Holmqvist {
389e487d8dSFredrik Holmqvist 	if (!sSerialEnabled)
399e487d8dSFredrik Holmqvist 		return;
409e487d8dSFredrik Holmqvist 
4181a892edSAlexander von Gluck IV 	// First we use EFI serial_io output if available
4281a892edSAlexander von Gluck IV 	if (sEFISerialIO != NULL) {
43485b5cf8SAlexander von Gluck IV 		size_t bufSize = 1;
4481a892edSAlexander von Gluck IV 		sEFISerialIO->Write(sEFISerialIO, &bufSize, &ch);
4504f1baa7SAlexander von Gluck IV 		return;
4604f1baa7SAlexander von Gluck IV 	}
4704f1baa7SAlexander von Gluck IV 
4881a892edSAlexander von Gluck IV #ifdef DEBUG
4981a892edSAlexander von Gluck IV 	// If we don't have EFI serial_io, fallback to EFI stdio
5081a892edSAlexander von Gluck IV 	if (sEFIAvailable) {
51f1fa58bfSX512 		char16_t ucsBuffer[2];
52f1fa58bfSX512 		ucsBuffer[0] = ch;
53f1fa58bfSX512 		ucsBuffer[1] = 0;
54f1fa58bfSX512 		kSystemTable->ConOut->OutputString(kSystemTable->ConOut, ucsBuffer);
55f1fa58bfSX512 		return;
56f1fa58bfSX512 	}
57f1fa58bfSX512 #endif
58f1fa58bfSX512 
5981a892edSAlexander von Gluck IV 	// If EFI services are unavailable... try any UART
6081a892edSAlexander von Gluck IV 	// this can happen when serial_io is unavailable, or EFI
6181a892edSAlexander von Gluck IV 	// is exiting
62f1fa58bfSX512 	if (gUART != NULL) {
63f1fa58bfSX512 		gUART->PutChar(ch);
64f1fa58bfSX512 		return;
65f1fa58bfSX512 	}
669e487d8dSFredrik Holmqvist }
679e487d8dSFredrik Holmqvist 
689e487d8dSFredrik Holmqvist 
699e487d8dSFredrik Holmqvist extern "C" void
serial_puts(const char * string,size_t size)709e487d8dSFredrik Holmqvist serial_puts(const char* string, size_t size)
719e487d8dSFredrik Holmqvist {
7281a892edSAlexander von Gluck IV 	if (!sSerialEnabled)
739e487d8dSFredrik Holmqvist 		return;
749e487d8dSFredrik Holmqvist 
759e487d8dSFredrik Holmqvist 	while (size-- != 0) {
769e487d8dSFredrik Holmqvist 		char ch = string[0];
779e487d8dSFredrik Holmqvist 
789e487d8dSFredrik Holmqvist 		if (ch == '\n') {
799e487d8dSFredrik Holmqvist 			serial_putc('\r');
809e487d8dSFredrik Holmqvist 			serial_putc('\n');
819e487d8dSFredrik Holmqvist 		} else if (ch != '\r')
829e487d8dSFredrik Holmqvist 			serial_putc(ch);
839e487d8dSFredrik Holmqvist 
849e487d8dSFredrik Holmqvist 		string++;
859e487d8dSFredrik Holmqvist 	}
869e487d8dSFredrik Holmqvist }
879e487d8dSFredrik Holmqvist 
889e487d8dSFredrik Holmqvist 
899e487d8dSFredrik Holmqvist extern "C" void
serial_disable(void)909e487d8dSFredrik Holmqvist serial_disable(void)
919e487d8dSFredrik Holmqvist {
929e487d8dSFredrik Holmqvist 	sSerialEnabled = false;
939e487d8dSFredrik Holmqvist }
949e487d8dSFredrik Holmqvist 
959e487d8dSFredrik Holmqvist 
969e487d8dSFredrik Holmqvist extern "C" void
serial_enable(void)979e487d8dSFredrik Holmqvist serial_enable(void)
989e487d8dSFredrik Holmqvist {
999e487d8dSFredrik Holmqvist 	sSerialEnabled = true;
100*b9cca28dSDavid Karoly 	if ((gUART != NULL) && !gUARTSkipInit)
10181a892edSAlexander von Gluck IV 		gUART->InitPort(kSerialBaudRate);
1029e487d8dSFredrik Holmqvist }
1039e487d8dSFredrik Holmqvist 
1049e487d8dSFredrik Holmqvist 
1059e487d8dSFredrik Holmqvist extern "C" void
serial_init(void)1069e487d8dSFredrik Holmqvist serial_init(void)
1079e487d8dSFredrik Holmqvist {
108b965b55dSAlexander von Gluck IV 	if (sEFIAvailable) {
10981a892edSAlexander von Gluck IV 		// Check for EFI Serial
110485b5cf8SAlexander von Gluck IV 		efi_status status = kSystemTable->BootServices->LocateProtocol(
11181a892edSAlexander von Gluck IV 			&sSerialIOProtocolGUID, NULL, (void**)&sEFISerialIO);
1129e487d8dSFredrik Holmqvist 
11381a892edSAlexander von Gluck IV 		if (status != EFI_SUCCESS)
11481a892edSAlexander von Gluck IV 			sEFISerialIO = NULL;
1159e487d8dSFredrik Holmqvist 
11681a892edSAlexander von Gluck IV 		if (sEFISerialIO != NULL) {
1179e487d8dSFredrik Holmqvist 			// Setup serial, 0, 0 = Default Receive FIFO queue and default timeout
11881a892edSAlexander von Gluck IV 			status = sEFISerialIO->SetAttributes(sEFISerialIO, kSerialBaudRate, 0, 0, NoParity, 8,
1199e487d8dSFredrik Holmqvist 				OneStopBit);
1209e487d8dSFredrik Holmqvist 
12181a892edSAlexander von Gluck IV 			if (status != EFI_SUCCESS)
12281a892edSAlexander von Gluck IV 				sEFISerialIO = NULL;
12381a892edSAlexander von Gluck IV 
12481a892edSAlexander von Gluck IV 			// serial_io was successful.
125b965b55dSAlexander von Gluck IV 			return;
126b965b55dSAlexander von Gluck IV 		}
1279e487d8dSFredrik Holmqvist 	}
12881a892edSAlexander von Gluck IV 
12981a892edSAlexander von Gluck IV #if defined(__i386__) || defined(__x86_64__)
13081a892edSAlexander von Gluck IV 	// On x86, we can try to setup COM1 as a gUART too
13181a892edSAlexander von Gluck IV 	// while this serial port may not physically exist,
13281a892edSAlexander von Gluck IV 	// the location is fixed on the x86 arch.
13381a892edSAlexander von Gluck IV 	// TODO: We could also try to pull from acpi?
13481a892edSAlexander von Gluck IV 	if (gUART == NULL) {
13581a892edSAlexander von Gluck IV 		gUART = arch_get_uart_8250(0x3f8, 1843200);
136b965b55dSAlexander von Gluck IV 
137b965b55dSAlexander von Gluck IV 		// TODO: convert over to exclusively arch_args.uart?
138b965b55dSAlexander von Gluck IV 		memset(gKernelArgs.platform_args.serial_base_ports, 0,
139b965b55dSAlexander von Gluck IV 			sizeof(uint16) * MAX_SERIAL_PORTS);
140b965b55dSAlexander von Gluck IV 		gKernelArgs.platform_args.serial_base_ports[0] = 0x3f8;
14181a892edSAlexander von Gluck IV 	}
14281a892edSAlexander von Gluck IV #endif
143b965b55dSAlexander von Gluck IV 
144b965b55dSAlexander von Gluck IV 	if (gUART != NULL)
145b965b55dSAlexander von Gluck IV 		gUART->InitEarly();
1469e487d8dSFredrik Holmqvist }
1479e487d8dSFredrik Holmqvist 
1489e487d8dSFredrik Holmqvist 
1499e487d8dSFredrik Holmqvist extern "C" void
serial_kernel_handoff(void)15081a892edSAlexander von Gluck IV serial_kernel_handoff(void)
1519e487d8dSFredrik Holmqvist {
15281a892edSAlexander von Gluck IV 	// The console was provided by boot services, disable it ASAP
15381a892edSAlexander von Gluck IV 	stdout = NULL;
15481a892edSAlexander von Gluck IV 	stderr = NULL;
15581a892edSAlexander von Gluck IV 
15681a892edSAlexander von Gluck IV 	// Disconnect from EFI serial_io services is important as we leave the bootloader
15781a892edSAlexander von Gluck IV 	sEFISerialIO = NULL;
15881a892edSAlexander von Gluck IV 	sEFIAvailable = false;
159f1fa58bfSX512 }
160