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