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