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