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