xref: /optee_os/core/kernel/console.c (revision d4a87690eba3c5031f5ac9c7592bb7d2d150697d)
11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
2756aea59SJerome Forissier /*
3756aea59SJerome Forissier  * Copyright (c) 2017, Linaro Limited
4756aea59SJerome Forissier  */
5756aea59SJerome Forissier 
6756aea59SJerome Forissier #include <compiler.h>
7a2fc83d1SJerome Forissier #include <console.h>
8426790bdSJeffrey Kardatzke #include <drivers/cbmem_console.h>
9458ef442SAlvin Chang #include <drivers/semihosting_console.h>
10*d4a87690SSungbae Yoo #include <drivers/ffa_console.h>
11756aea59SJerome Forissier #include <drivers/serial.h>
12a2fc83d1SJerome Forissier #include <kernel/dt.h>
139e3c57c8SEtienne Carriere #include <kernel/dt_driver.h>
144dc31c52SJerome Forissier #include <kernel/panic.h>
15a2fc83d1SJerome Forissier #include <libfdt.h>
16756aea59SJerome Forissier #include <stdlib.h>
174dc31c52SJerome Forissier #include <string.h>
184a810b90SVolodymyr Babchuk #include <string_ext.h>
194dc31c52SJerome Forissier 
20c211d0a4SVolodymyr Babchuk static struct serial_chip *serial_console __nex_bss;
21756aea59SJerome Forissier 
2255ab8f06SAlvin Chang /* May be overridden by platform */
2355ab8f06SAlvin Chang __weak void plat_console_init(void)
2455ab8f06SAlvin Chang {
2555ab8f06SAlvin Chang }
2655ab8f06SAlvin Chang 
2755ab8f06SAlvin Chang void console_init(void)
2855ab8f06SAlvin Chang {
29458ef442SAlvin Chang 	if (IS_ENABLED(CFG_SEMIHOSTING_CONSOLE))
30458ef442SAlvin Chang 		semihosting_console_init(CFG_SEMIHOSTING_CONSOLE_FILE);
31*d4a87690SSungbae Yoo 	else if (IS_ENABLED(CFG_FFA_CONSOLE))
32*d4a87690SSungbae Yoo 		ffa_console_init();
33458ef442SAlvin Chang 	else
3455ab8f06SAlvin Chang 		plat_console_init();
3555ab8f06SAlvin Chang }
3655ab8f06SAlvin Chang 
37756aea59SJerome Forissier void __weak console_putc(int ch)
38756aea59SJerome Forissier {
39756aea59SJerome Forissier 	if (!serial_console)
40756aea59SJerome Forissier 		return;
41756aea59SJerome Forissier 
42756aea59SJerome Forissier 	if (ch == '\n')
43756aea59SJerome Forissier 		serial_console->ops->putc(serial_console, '\r');
44756aea59SJerome Forissier 	serial_console->ops->putc(serial_console, ch);
45756aea59SJerome Forissier }
46756aea59SJerome Forissier 
47756aea59SJerome Forissier void __weak console_flush(void)
48756aea59SJerome Forissier {
497b84e23dSJerome Forissier 	if (!serial_console || !serial_console->ops->flush)
50756aea59SJerome Forissier 		return;
51756aea59SJerome Forissier 
52756aea59SJerome Forissier 	serial_console->ops->flush(serial_console);
53756aea59SJerome Forissier }
54756aea59SJerome Forissier 
55756aea59SJerome Forissier void register_serial_console(struct serial_chip *chip)
56756aea59SJerome Forissier {
57756aea59SJerome Forissier 	serial_console = chip;
58756aea59SJerome Forissier }
594dc31c52SJerome Forissier 
604dc31c52SJerome Forissier #ifdef CFG_DT
6108baa8c9SEtienne Carriere static int find_chosen_node(void *fdt)
6208baa8c9SEtienne Carriere {
633c6cfce4SJens Wiklander 	int offset = 0;
643c6cfce4SJens Wiklander 
6508baa8c9SEtienne Carriere 	if (!fdt)
6608baa8c9SEtienne Carriere 		return -1;
6708baa8c9SEtienne Carriere 
683c6cfce4SJens Wiklander 	offset = fdt_path_offset(fdt, "/secure-chosen");
6908baa8c9SEtienne Carriere 
7008baa8c9SEtienne Carriere 	if (offset < 0)
7108baa8c9SEtienne Carriere 		offset = fdt_path_offset(fdt, "/chosen");
7208baa8c9SEtienne Carriere 
7308baa8c9SEtienne Carriere 	return offset;
7408baa8c9SEtienne Carriere }
754dc31c52SJerome Forissier 
76286c31d4SEtienne Carriere TEE_Result get_console_node_from_dt(void *fdt, int *offs_out,
77d2c717b2SJerome Forissier 				    char **path_out, char **params_out)
784dc31c52SJerome Forissier {
794dc31c52SJerome Forissier 	const struct fdt_property *prop;
804dc31c52SJerome Forissier 	const char *uart;
814dc31c52SJerome Forissier 	const char *parms = NULL;
824dc31c52SJerome Forissier 	int offs;
83770b2afaSEtienne Carriere 	char *stdout_data;
844dc31c52SJerome Forissier 	char *p;
85770b2afaSEtienne Carriere 	TEE_Result rc = TEE_ERROR_GENERIC;
864dc31c52SJerome Forissier 
8708baa8c9SEtienne Carriere 	/* Probe console from secure DT and fallback to non-secure DT */
8808baa8c9SEtienne Carriere 	offs = find_chosen_node(fdt);
8908baa8c9SEtienne Carriere 	if (offs < 0) {
9008baa8c9SEtienne Carriere 		DMSG("No console directive from DTB");
91770b2afaSEtienne Carriere 		return TEE_ERROR_ITEM_NOT_FOUND;
9208baa8c9SEtienne Carriere 	}
934dc31c52SJerome Forissier 
944dc31c52SJerome Forissier 	prop = fdt_get_property(fdt, offs, "stdout-path", NULL);
954dc31c52SJerome Forissier 	if (!prop) {
964dc31c52SJerome Forissier 		/*
9708baa8c9SEtienne Carriere 		 * A secure-chosen or chosen node is present but defined
9808baa8c9SEtienne Carriere 		 * no stdout-path property: no console expected
994dc31c52SJerome Forissier 		 */
1004dc31c52SJerome Forissier 		IMSG("Switching off console");
1014dc31c52SJerome Forissier 		register_serial_console(NULL);
102770b2afaSEtienne Carriere 		return TEE_ERROR_ITEM_NOT_FOUND;
1034dc31c52SJerome Forissier 	}
1044dc31c52SJerome Forissier 
1054a810b90SVolodymyr Babchuk 	stdout_data = nex_strdup(prop->data);
1064dc31c52SJerome Forissier 	if (!stdout_data)
107770b2afaSEtienne Carriere 		panic();
1084dc31c52SJerome Forissier 	p = strchr(stdout_data, ':');
1094dc31c52SJerome Forissier 	if (p) {
1104dc31c52SJerome Forissier 		*p = '\0';
1114dc31c52SJerome Forissier 		parms = p + 1;
1124dc31c52SJerome Forissier 	}
1134dc31c52SJerome Forissier 
1144dc31c52SJerome Forissier 	/* stdout-path may refer to an alias */
1154dc31c52SJerome Forissier 	uart = fdt_get_alias(fdt, stdout_data);
1164dc31c52SJerome Forissier 	if (!uart) {
1174dc31c52SJerome Forissier 		/* Not an alias, assume we have a node path */
1184dc31c52SJerome Forissier 		uart = stdout_data;
1194dc31c52SJerome Forissier 	}
1204dc31c52SJerome Forissier 	offs = fdt_path_offset(fdt, uart);
121770b2afaSEtienne Carriere 	if (offs >= 0) {
122770b2afaSEtienne Carriere 		if (offs_out)
123770b2afaSEtienne Carriere 			*offs_out = offs;
124770b2afaSEtienne Carriere 		if (params_out)
125d2c717b2SJerome Forissier 			*params_out = parms ? nex_strdup(parms) : NULL;
126770b2afaSEtienne Carriere 		if (path_out)
127d2c717b2SJerome Forissier 			*path_out = uart ? nex_strdup(uart) : NULL;
128770b2afaSEtienne Carriere 
129770b2afaSEtienne Carriere 		rc = TEE_SUCCESS;
130770b2afaSEtienne Carriere 	}
131770b2afaSEtienne Carriere 
1324a810b90SVolodymyr Babchuk 	nex_free(stdout_data);
133770b2afaSEtienne Carriere 
134770b2afaSEtienne Carriere 	return rc;
135770b2afaSEtienne Carriere }
136770b2afaSEtienne Carriere 
137770b2afaSEtienne Carriere void configure_console_from_dt(void)
138770b2afaSEtienne Carriere {
139770b2afaSEtienne Carriere 	const struct dt_driver *dt_drv;
140770b2afaSEtienne Carriere 	const struct serial_driver *sdrv;
141770b2afaSEtienne Carriere 	struct serial_chip *dev;
142d2c717b2SJerome Forissier 	char *uart = NULL;
143d2c717b2SJerome Forissier 	char *parms = NULL;
144770b2afaSEtienne Carriere 	void *fdt;
145770b2afaSEtienne Carriere 	int offs;
146770b2afaSEtienne Carriere 
14775dc8729SEtienne Carriere 	fdt = get_dt();
148426790bdSJeffrey Kardatzke 
149426790bdSJeffrey Kardatzke 	if (IS_ENABLED(CFG_CBMEM_CONSOLE) && cbmem_console_init_from_dt(fdt))
150426790bdSJeffrey Kardatzke 		return;
151426790bdSJeffrey Kardatzke 
152286c31d4SEtienne Carriere 	if (get_console_node_from_dt(fdt, &offs, &uart, &parms))
153770b2afaSEtienne Carriere 		return;
1544dc31c52SJerome Forissier 
1554dc31c52SJerome Forissier 	dt_drv = dt_find_compatible_driver(fdt, offs);
1565e588771SClément Léger 	if (!dt_drv || dt_drv->type != DT_DRIVER_UART)
157d2c717b2SJerome Forissier 		goto out;
1584dc31c52SJerome Forissier 
1594dc31c52SJerome Forissier 	sdrv = (const struct serial_driver *)dt_drv->driver;
1604dc31c52SJerome Forissier 	if (!sdrv)
161d2c717b2SJerome Forissier 		goto out;
162770b2afaSEtienne Carriere 
1634dc31c52SJerome Forissier 	dev = sdrv->dev_alloc();
1644dc31c52SJerome Forissier 	if (!dev)
165d2c717b2SJerome Forissier 		goto out;
166770b2afaSEtienne Carriere 
1674dc31c52SJerome Forissier 	/*
1684dc31c52SJerome Forissier 	 * If the console is the same as the early console, dev_init() might
1694dc31c52SJerome Forissier 	 * clear pending data. Flush to avoid that.
1704dc31c52SJerome Forissier 	 */
1714dc31c52SJerome Forissier 	console_flush();
1724dc31c52SJerome Forissier 	if (sdrv->dev_init(dev, fdt, offs, parms) < 0) {
1734dc31c52SJerome Forissier 		sdrv->dev_free(dev);
174d2c717b2SJerome Forissier 		goto out;
1754dc31c52SJerome Forissier 	}
1764dc31c52SJerome Forissier 
1774dc31c52SJerome Forissier 	IMSG("Switching console to device: %s", uart);
1784dc31c52SJerome Forissier 	register_serial_console(dev);
179d2c717b2SJerome Forissier out:
180d2c717b2SJerome Forissier 	nex_free(uart);
181d2c717b2SJerome Forissier 	nex_free(parms);
1824dc31c52SJerome Forissier }
1834dc31c52SJerome Forissier 
1844dc31c52SJerome Forissier #endif /* CFG_DT */
185