xref: /optee_os/core/kernel/console.c (revision 336e32995d9c419d9fc2a6fd5974f99761285415)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2017, Linaro Limited
4  */
5 
6 #include <console.h>
7 #include <compiler.h>
8 #include <drivers/serial.h>
9 #include <kernel/generic_boot.h>
10 #include <kernel/panic.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #ifdef CFG_DT
15 #include <kernel/dt.h>
16 #include <libfdt.h>
17 #endif
18 
19 static struct serial_chip *serial_console;
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)
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 	if (!fdt)
48 		return -1;
49 
50 	int offset = fdt_path_offset(fdt, "/secure-chosen");
51 
52 	if (offset < 0)
53 		offset = fdt_path_offset(fdt, "/chosen");
54 
55 	return offset;
56 }
57 
58 TEE_Result get_console_node_from_dt(void **fdt_out, int *offs_out,
59 				    const char **path_out,
60 				    const char **params_out)
61 {
62 	const struct fdt_property *prop;
63 	const char *uart;
64 	const char *parms = NULL;
65 	void *fdt;
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 	fdt = get_embedded_dt();
73 	offs = find_chosen_node(fdt);
74 	if (offs < 0) {
75 		fdt = get_external_dt();
76 		offs = find_chosen_node(fdt);
77 	}
78 	if (offs < 0) {
79 		DMSG("No console directive from DTB");
80 		return TEE_ERROR_ITEM_NOT_FOUND;
81 	}
82 
83 	prop = fdt_get_property(fdt, offs, "stdout-path", NULL);
84 	if (!prop) {
85 		/*
86 		 * A secure-chosen or chosen node is present but defined
87 		 * no stdout-path property: no console expected
88 		 */
89 		IMSG("Switching off console");
90 		register_serial_console(NULL);
91 		return TEE_ERROR_ITEM_NOT_FOUND;
92 	}
93 
94 	stdout_data = strdup(prop->data);
95 	if (!stdout_data)
96 		panic();
97 	p = strchr(stdout_data, ':');
98 	if (p) {
99 		*p = '\0';
100 		parms = p + 1;
101 	}
102 
103 	/* stdout-path may refer to an alias */
104 	uart = fdt_get_alias(fdt, stdout_data);
105 	if (!uart) {
106 		/* Not an alias, assume we have a node path */
107 		uart = stdout_data;
108 	}
109 	offs = fdt_path_offset(fdt, uart);
110 	if (offs >= 0) {
111 		if (fdt_out)
112 			*fdt_out = fdt;
113 		if (offs_out)
114 			*offs_out = offs;
115 		if (params_out)
116 			*params_out = parms;
117 		if (path_out)
118 			*path_out = uart;
119 
120 		rc = TEE_SUCCESS;
121 	}
122 
123 	free(stdout_data);
124 
125 	return rc;
126 }
127 
128 void configure_console_from_dt(void)
129 {
130 	const struct dt_driver *dt_drv;
131 	const struct serial_driver *sdrv;
132 	struct serial_chip *dev;
133 	const char *uart;
134 	const char *parms;
135 	void *fdt;
136 	int offs;
137 
138 	if (get_console_node_from_dt(&fdt, &offs, &uart, &parms))
139 		return;
140 
141 	dt_drv = dt_find_compatible_driver(fdt, offs);
142 	if (!dt_drv)
143 		return;
144 
145 	sdrv = (const struct serial_driver *)dt_drv->driver;
146 	if (!sdrv)
147 		return;
148 
149 	dev = sdrv->dev_alloc();
150 	if (!dev)
151 		return;
152 
153 	/*
154 	 * If the console is the same as the early console, dev_init() might
155 	 * clear pending data. Flush to avoid that.
156 	 */
157 	console_flush();
158 	if (sdrv->dev_init(dev, fdt, offs, parms) < 0) {
159 		sdrv->dev_free(dev);
160 		return;
161 	}
162 
163 	IMSG("Switching console to device: %s", uart);
164 	register_serial_console(dev);
165 }
166 
167 #endif /* CFG_DT */
168