xref: /optee_os/core/kernel/console.c (revision 19a31ec40245ae01a9adcd206eec2a4bb4479fc9)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2017, Linaro Limited
4  */
5 
6 #include <compiler.h>
7 #include <console.h>
8 #include <drivers/cbmem_console.h>
9 #include <drivers/semihosting_console.h>
10 #include <drivers/serial.h>
11 #include <kernel/dt.h>
12 #include <kernel/dt_driver.h>
13 #include <kernel/panic.h>
14 #include <libfdt.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <string_ext.h>
18 
19 static struct serial_chip *serial_console __nex_bss;
20 
21 /* May be overridden by platform */
22 __weak void plat_console_init(void)
23 {
24 }
25 
26 void console_init(void)
27 {
28 	if (IS_ENABLED(CFG_SEMIHOSTING_CONSOLE))
29 		semihosting_console_init(CFG_SEMIHOSTING_CONSOLE_FILE);
30 	else
31 		plat_console_init();
32 }
33 
34 void __weak console_putc(int ch)
35 {
36 	if (!serial_console)
37 		return;
38 
39 	if (ch == '\n')
40 		serial_console->ops->putc(serial_console, '\r');
41 	serial_console->ops->putc(serial_console, ch);
42 }
43 
44 void __weak console_flush(void)
45 {
46 	if (!serial_console || !serial_console->ops->flush)
47 		return;
48 
49 	serial_console->ops->flush(serial_console);
50 }
51 
52 void register_serial_console(struct serial_chip *chip)
53 {
54 	serial_console = chip;
55 }
56 
57 #ifdef CFG_DT
58 static int find_chosen_node(void *fdt)
59 {
60 	int offset = 0;
61 
62 	if (!fdt)
63 		return -1;
64 
65 	offset = fdt_path_offset(fdt, "/secure-chosen");
66 
67 	if (offset < 0)
68 		offset = fdt_path_offset(fdt, "/chosen");
69 
70 	return offset;
71 }
72 
73 TEE_Result get_console_node_from_dt(void *fdt, int *offs_out,
74 				    char **path_out, char **params_out)
75 {
76 	const struct fdt_property *prop;
77 	const char *uart;
78 	const char *parms = NULL;
79 	int offs;
80 	char *stdout_data;
81 	char *p;
82 	TEE_Result rc = TEE_ERROR_GENERIC;
83 
84 	/* Probe console from secure DT and fallback to non-secure DT */
85 	offs = find_chosen_node(fdt);
86 	if (offs < 0) {
87 		DMSG("No console directive from DTB");
88 		return TEE_ERROR_ITEM_NOT_FOUND;
89 	}
90 
91 	prop = fdt_get_property(fdt, offs, "stdout-path", NULL);
92 	if (!prop) {
93 		/*
94 		 * A secure-chosen or chosen node is present but defined
95 		 * no stdout-path property: no console expected
96 		 */
97 		IMSG("Switching off console");
98 		register_serial_console(NULL);
99 		return TEE_ERROR_ITEM_NOT_FOUND;
100 	}
101 
102 	stdout_data = nex_strdup(prop->data);
103 	if (!stdout_data)
104 		panic();
105 	p = strchr(stdout_data, ':');
106 	if (p) {
107 		*p = '\0';
108 		parms = p + 1;
109 	}
110 
111 	/* stdout-path may refer to an alias */
112 	uart = fdt_get_alias(fdt, stdout_data);
113 	if (!uart) {
114 		/* Not an alias, assume we have a node path */
115 		uart = stdout_data;
116 	}
117 	offs = fdt_path_offset(fdt, uart);
118 	if (offs >= 0) {
119 		if (offs_out)
120 			*offs_out = offs;
121 		if (params_out)
122 			*params_out = parms ? nex_strdup(parms) : NULL;
123 		if (path_out)
124 			*path_out = uart ? nex_strdup(uart) : NULL;
125 
126 		rc = TEE_SUCCESS;
127 	}
128 
129 	nex_free(stdout_data);
130 
131 	return rc;
132 }
133 
134 void configure_console_from_dt(void)
135 {
136 	const struct dt_driver *dt_drv;
137 	const struct serial_driver *sdrv;
138 	struct serial_chip *dev;
139 	char *uart = NULL;
140 	char *parms = NULL;
141 	void *fdt;
142 	int offs;
143 
144 	fdt = get_dt();
145 
146 	if (IS_ENABLED(CFG_CBMEM_CONSOLE) && cbmem_console_init_from_dt(fdt))
147 		return;
148 
149 	if (get_console_node_from_dt(fdt, &offs, &uart, &parms))
150 		return;
151 
152 	dt_drv = dt_find_compatible_driver(fdt, offs);
153 	if (!dt_drv || dt_drv->type != DT_DRIVER_UART)
154 		goto out;
155 
156 	sdrv = (const struct serial_driver *)dt_drv->driver;
157 	if (!sdrv)
158 		goto out;
159 
160 	dev = sdrv->dev_alloc();
161 	if (!dev)
162 		goto out;
163 
164 	/*
165 	 * If the console is the same as the early console, dev_init() might
166 	 * clear pending data. Flush to avoid that.
167 	 */
168 	console_flush();
169 	if (sdrv->dev_init(dev, fdt, offs, parms) < 0) {
170 		sdrv->dev_free(dev);
171 		goto out;
172 	}
173 
174 	IMSG("Switching console to device: %s", uart);
175 	register_serial_console(dev);
176 out:
177 	nex_free(uart);
178 	nex_free(parms);
179 }
180 
181 #endif /* CFG_DT */
182