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
console_init(void)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
console_putc(int ch)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
console_flush(void)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
register_serial_console(struct serial_chip * chip)56 void register_serial_console(struct serial_chip *chip)
57 {
58 serial_console = chip;
59 }
60
61 #ifdef CFG_CONSOLE_RUNTIME_SET
console_runtime_set(void)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
find_chosen_node(void * fdt)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
get_console_node_from_dt(void * fdt,int * offs_out,char ** path_out,char ** params_out)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
configure_console_from_dt(void)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