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