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