xref: /optee_os/core/kernel/console.c (revision 1bb929836182ecb96d2d9d268daa807c67596396)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2017, Linaro Limited
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <console.h>
30 #include <compiler.h>
31 #include <drivers/serial.h>
32 #include <kernel/panic.h>
33 #include <stdlib.h>
34 #include <string.h>
35 
36 #ifdef CFG_DT
37 #include <kernel/dt.h>
38 #include <libfdt.h>
39 #endif
40 
41 static struct serial_chip *serial_console;
42 
43 void __weak console_putc(int ch)
44 {
45 	if (!serial_console)
46 		return;
47 
48 	if (ch == '\n')
49 		serial_console->ops->putc(serial_console, '\r');
50 	serial_console->ops->putc(serial_console, ch);
51 }
52 
53 void __weak console_flush(void)
54 {
55 	if (!serial_console)
56 		return;
57 
58 	serial_console->ops->flush(serial_console);
59 }
60 
61 void register_serial_console(struct serial_chip *chip)
62 {
63 	serial_console = chip;
64 }
65 
66 #ifdef CFG_DT
67 
68 /*
69  * Check if the /secure-chosen node in the DT contains an stdout-path value
70  * for which we have a compatible driver. If so, switch the console to
71  * this device.
72  */
73 void configure_console_from_dt(unsigned long phys_fdt)
74 {
75 	const struct dt_driver *dt_drv;
76 	const struct serial_driver *sdrv;
77 	const struct fdt_property *prop;
78 	struct serial_chip *dev;
79 	char *stdout_data;
80 	const char *uart;
81 	const char *parms = NULL;
82 	void *fdt;
83 	int offs;
84 	char *p;
85 
86 	if (!phys_fdt)
87 		return;
88 	fdt = phys_to_virt(phys_fdt, MEM_AREA_IO_NSEC);
89 	if (!fdt)
90 		panic();
91 
92 	offs = fdt_path_offset(fdt, "/secure-chosen");
93 	if (offs < 0)
94 		return;
95 	prop = fdt_get_property(fdt, offs, "stdout-path", NULL);
96 	if (!prop) {
97 		/*
98 		 * /secure-chosen node present but no stdout-path property
99 		 * means we don't want any console output
100 		 */
101 		IMSG("Switching off console");
102 		register_serial_console(NULL);
103 		return;
104 	}
105 
106 	stdout_data = strdup(prop->data);
107 	if (!stdout_data)
108 		return;
109 	p = strchr(stdout_data, ':');
110 	if (p) {
111 		*p = '\0';
112 		parms = p + 1;
113 	}
114 
115 	/* stdout-path may refer to an alias */
116 	uart = fdt_get_alias(fdt, stdout_data);
117 	if (!uart) {
118 		/* Not an alias, assume we have a node path */
119 		uart = stdout_data;
120 	}
121 	offs = fdt_path_offset(fdt, uart);
122 	if (offs < 0)
123 		goto out;
124 
125 	dt_drv = dt_find_compatible_driver(fdt, offs);
126 	if (!dt_drv)
127 		goto out;
128 
129 	sdrv = (const struct serial_driver *)dt_drv->driver;
130 	if (!sdrv)
131 		goto out;
132 	dev = sdrv->dev_alloc();
133 	if (!dev)
134 		goto out;
135 	/*
136 	 * If the console is the same as the early console, dev_init() might
137 	 * clear pending data. Flush to avoid that.
138 	 */
139 	console_flush();
140 	if (sdrv->dev_init(dev, fdt, offs, parms) < 0) {
141 		sdrv->dev_free(dev);
142 		goto out;
143 	}
144 
145 	IMSG("Switching console to device: %s", uart);
146 	register_serial_console(dev);
147 out:
148 	free(stdout_data);
149 }
150 
151 #endif /* CFG_DT */
152