xref: /optee_os/core/kernel/console.c (revision c211d0a49ecf2673dfb90f29fa31840eda12a18d)
11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
2756aea59SJerome Forissier /*
3756aea59SJerome Forissier  * Copyright (c) 2017, Linaro Limited
4756aea59SJerome Forissier  */
5756aea59SJerome Forissier 
6756aea59SJerome Forissier #include <console.h>
7756aea59SJerome Forissier #include <compiler.h>
8756aea59SJerome Forissier #include <drivers/serial.h>
92f82082fSEdison Ai #include <kernel/generic_boot.h>
104dc31c52SJerome Forissier #include <kernel/panic.h>
11756aea59SJerome Forissier #include <stdlib.h>
124dc31c52SJerome Forissier #include <string.h>
134dc31c52SJerome Forissier 
144dc31c52SJerome Forissier #ifdef CFG_DT
154dc31c52SJerome Forissier #include <kernel/dt.h>
164dc31c52SJerome Forissier #include <libfdt.h>
174dc31c52SJerome Forissier #endif
18756aea59SJerome Forissier 
19*c211d0a4SVolodymyr Babchuk static struct serial_chip *serial_console __nex_bss;
20756aea59SJerome Forissier 
21756aea59SJerome Forissier void __weak console_putc(int ch)
22756aea59SJerome Forissier {
23756aea59SJerome Forissier 	if (!serial_console)
24756aea59SJerome Forissier 		return;
25756aea59SJerome Forissier 
26756aea59SJerome Forissier 	if (ch == '\n')
27756aea59SJerome Forissier 		serial_console->ops->putc(serial_console, '\r');
28756aea59SJerome Forissier 	serial_console->ops->putc(serial_console, ch);
29756aea59SJerome Forissier }
30756aea59SJerome Forissier 
31756aea59SJerome Forissier void __weak console_flush(void)
32756aea59SJerome Forissier {
33756aea59SJerome Forissier 	if (!serial_console)
34756aea59SJerome Forissier 		return;
35756aea59SJerome Forissier 
36756aea59SJerome Forissier 	serial_console->ops->flush(serial_console);
37756aea59SJerome Forissier }
38756aea59SJerome Forissier 
39756aea59SJerome Forissier void register_serial_console(struct serial_chip *chip)
40756aea59SJerome Forissier {
41756aea59SJerome Forissier 	serial_console = chip;
42756aea59SJerome Forissier }
434dc31c52SJerome Forissier 
444dc31c52SJerome Forissier #ifdef CFG_DT
4508baa8c9SEtienne Carriere static int find_chosen_node(void *fdt)
4608baa8c9SEtienne Carriere {
4708baa8c9SEtienne Carriere 	if (!fdt)
4808baa8c9SEtienne Carriere 		return -1;
4908baa8c9SEtienne Carriere 
5008baa8c9SEtienne Carriere 	int offset = fdt_path_offset(fdt, "/secure-chosen");
5108baa8c9SEtienne Carriere 
5208baa8c9SEtienne Carriere 	if (offset < 0)
5308baa8c9SEtienne Carriere 		offset = fdt_path_offset(fdt, "/chosen");
5408baa8c9SEtienne Carriere 
5508baa8c9SEtienne Carriere 	return offset;
5608baa8c9SEtienne Carriere }
574dc31c52SJerome Forissier 
58770b2afaSEtienne Carriere TEE_Result get_console_node_from_dt(void **fdt_out, int *offs_out,
59770b2afaSEtienne Carriere 				    const char **path_out,
60770b2afaSEtienne Carriere 				    const char **params_out)
614dc31c52SJerome Forissier {
624dc31c52SJerome Forissier 	const struct fdt_property *prop;
634dc31c52SJerome Forissier 	const char *uart;
644dc31c52SJerome Forissier 	const char *parms = NULL;
6508baa8c9SEtienne Carriere 	void *fdt;
664dc31c52SJerome Forissier 	int offs;
67770b2afaSEtienne Carriere 	char *stdout_data;
684dc31c52SJerome Forissier 	char *p;
69770b2afaSEtienne Carriere 	TEE_Result rc = TEE_ERROR_GENERIC;
704dc31c52SJerome Forissier 
7108baa8c9SEtienne Carriere 	/* Probe console from secure DT and fallback to non-secure DT */
7208baa8c9SEtienne Carriere 	fdt = get_embedded_dt();
7308baa8c9SEtienne Carriere 	offs = find_chosen_node(fdt);
7408baa8c9SEtienne Carriere 	if (offs < 0) {
7508baa8c9SEtienne Carriere 		fdt = get_external_dt();
7608baa8c9SEtienne Carriere 		offs = find_chosen_node(fdt);
7708baa8c9SEtienne Carriere 	}
7808baa8c9SEtienne Carriere 	if (offs < 0) {
7908baa8c9SEtienne Carriere 		DMSG("No console directive from DTB");
80770b2afaSEtienne Carriere 		return TEE_ERROR_ITEM_NOT_FOUND;
8108baa8c9SEtienne Carriere 	}
824dc31c52SJerome Forissier 
834dc31c52SJerome Forissier 	prop = fdt_get_property(fdt, offs, "stdout-path", NULL);
844dc31c52SJerome Forissier 	if (!prop) {
854dc31c52SJerome Forissier 		/*
8608baa8c9SEtienne Carriere 		 * A secure-chosen or chosen node is present but defined
8708baa8c9SEtienne Carriere 		 * no stdout-path property: no console expected
884dc31c52SJerome Forissier 		 */
894dc31c52SJerome Forissier 		IMSG("Switching off console");
904dc31c52SJerome Forissier 		register_serial_console(NULL);
91770b2afaSEtienne Carriere 		return TEE_ERROR_ITEM_NOT_FOUND;
924dc31c52SJerome Forissier 	}
934dc31c52SJerome Forissier 
944dc31c52SJerome Forissier 	stdout_data = strdup(prop->data);
954dc31c52SJerome Forissier 	if (!stdout_data)
96770b2afaSEtienne Carriere 		panic();
974dc31c52SJerome Forissier 	p = strchr(stdout_data, ':');
984dc31c52SJerome Forissier 	if (p) {
994dc31c52SJerome Forissier 		*p = '\0';
1004dc31c52SJerome Forissier 		parms = p + 1;
1014dc31c52SJerome Forissier 	}
1024dc31c52SJerome Forissier 
1034dc31c52SJerome Forissier 	/* stdout-path may refer to an alias */
1044dc31c52SJerome Forissier 	uart = fdt_get_alias(fdt, stdout_data);
1054dc31c52SJerome Forissier 	if (!uart) {
1064dc31c52SJerome Forissier 		/* Not an alias, assume we have a node path */
1074dc31c52SJerome Forissier 		uart = stdout_data;
1084dc31c52SJerome Forissier 	}
1094dc31c52SJerome Forissier 	offs = fdt_path_offset(fdt, uart);
110770b2afaSEtienne Carriere 	if (offs >= 0) {
111770b2afaSEtienne Carriere 		if (fdt_out)
112770b2afaSEtienne Carriere 			*fdt_out = fdt;
113770b2afaSEtienne Carriere 		if (offs_out)
114770b2afaSEtienne Carriere 			*offs_out = offs;
115770b2afaSEtienne Carriere 		if (params_out)
116770b2afaSEtienne Carriere 			*params_out = parms;
117770b2afaSEtienne Carriere 		if (path_out)
118770b2afaSEtienne Carriere 			*path_out = uart;
119770b2afaSEtienne Carriere 
120770b2afaSEtienne Carriere 		rc = TEE_SUCCESS;
121770b2afaSEtienne Carriere 	}
122770b2afaSEtienne Carriere 
123770b2afaSEtienne Carriere 	free(stdout_data);
124770b2afaSEtienne Carriere 
125770b2afaSEtienne Carriere 	return rc;
126770b2afaSEtienne Carriere }
127770b2afaSEtienne Carriere 
128770b2afaSEtienne Carriere void configure_console_from_dt(void)
129770b2afaSEtienne Carriere {
130770b2afaSEtienne Carriere 	const struct dt_driver *dt_drv;
131770b2afaSEtienne Carriere 	const struct serial_driver *sdrv;
132770b2afaSEtienne Carriere 	struct serial_chip *dev;
133770b2afaSEtienne Carriere 	const char *uart;
134770b2afaSEtienne Carriere 	const char *parms;
135770b2afaSEtienne Carriere 	void *fdt;
136770b2afaSEtienne Carriere 	int offs;
137770b2afaSEtienne Carriere 
138770b2afaSEtienne Carriere 	if (get_console_node_from_dt(&fdt, &offs, &uart, &parms))
139770b2afaSEtienne Carriere 		return;
1404dc31c52SJerome Forissier 
1414dc31c52SJerome Forissier 	dt_drv = dt_find_compatible_driver(fdt, offs);
1424dc31c52SJerome Forissier 	if (!dt_drv)
143770b2afaSEtienne Carriere 		return;
1444dc31c52SJerome Forissier 
1454dc31c52SJerome Forissier 	sdrv = (const struct serial_driver *)dt_drv->driver;
1464dc31c52SJerome Forissier 	if (!sdrv)
147770b2afaSEtienne Carriere 		return;
148770b2afaSEtienne Carriere 
1494dc31c52SJerome Forissier 	dev = sdrv->dev_alloc();
1504dc31c52SJerome Forissier 	if (!dev)
151770b2afaSEtienne Carriere 		return;
152770b2afaSEtienne Carriere 
1534dc31c52SJerome Forissier 	/*
1544dc31c52SJerome Forissier 	 * If the console is the same as the early console, dev_init() might
1554dc31c52SJerome Forissier 	 * clear pending data. Flush to avoid that.
1564dc31c52SJerome Forissier 	 */
1574dc31c52SJerome Forissier 	console_flush();
1584dc31c52SJerome Forissier 	if (sdrv->dev_init(dev, fdt, offs, parms) < 0) {
1594dc31c52SJerome Forissier 		sdrv->dev_free(dev);
160770b2afaSEtienne Carriere 		return;
1614dc31c52SJerome Forissier 	}
1624dc31c52SJerome Forissier 
1634dc31c52SJerome Forissier 	IMSG("Switching console to device: %s", uart);
1644dc31c52SJerome Forissier 	register_serial_console(dev);
1654dc31c52SJerome Forissier }
1664dc31c52SJerome Forissier 
1674dc31c52SJerome Forissier #endif /* CFG_DT */
168