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