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/semihosting_console.h> 10 #include <drivers/ffa_console.h> 11 #include <drivers/serial.h> 12 #include <initcall.h> 13 #include <kernel/dt.h> 14 #include <kernel/dt_driver.h> 15 #include <kernel/panic.h> 16 #include <libfdt.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <string_ext.h> 20 21 static struct serial_chip *serial_console __nex_bss; 22 23 /* May be overridden by platform */ 24 __weak void plat_console_init(void) 25 { 26 } 27 28 __weak void plat_trace_init(void) 29 { 30 } 31 32 void console_init(void) 33 { 34 plat_trace_init(); 35 36 if (IS_ENABLED(CFG_SEMIHOSTING_CONSOLE)) 37 semihosting_console_init(CFG_SEMIHOSTING_CONSOLE_FILE); 38 else if (IS_ENABLED(CFG_FFA_CONSOLE)) 39 ffa_console_init(); 40 else 41 plat_console_init(); 42 } 43 44 void __weak console_putc(int ch) 45 { 46 if (!serial_console) 47 return; 48 49 if (ch == '\n') 50 serial_console->ops->putc(serial_console, '\r'); 51 serial_console->ops->putc(serial_console, ch); 52 } 53 54 void __weak console_flush(void) 55 { 56 if (!serial_console || !serial_console->ops->flush) 57 return; 58 59 serial_console->ops->flush(serial_console); 60 } 61 62 void register_serial_console(struct serial_chip *chip) 63 { 64 serial_console = chip; 65 } 66 67 #ifdef CFG_CONSOLE_RUNTIME_SET 68 static TEE_Result console_runtime_set(void) 69 { 70 if (CFG_CONSOLE_RUNTIME_LOG_LEVEL == TRACE_MIN) 71 IMSG("Disabling output console"); 72 73 trace_set_level(CFG_CONSOLE_RUNTIME_LOG_LEVEL); 74 75 return TEE_SUCCESS; 76 } 77 78 boot_final(console_runtime_set); 79 #endif 80 81 #ifdef CFG_DT 82 static int find_chosen_node(void *fdt) 83 { 84 int offset = 0; 85 86 if (!fdt) 87 return -1; 88 89 offset = fdt_path_offset(fdt, "/secure-chosen"); 90 91 if (offset < 0) 92 offset = fdt_path_offset(fdt, "/chosen"); 93 94 return offset; 95 } 96 97 TEE_Result get_console_node_from_dt(void *fdt, int *offs_out, 98 char **path_out, char **params_out) 99 { 100 const struct fdt_property *prop; 101 const char *uart; 102 const char *parms = NULL; 103 int offs; 104 char *stdout_data; 105 char *p; 106 TEE_Result rc = TEE_ERROR_GENERIC; 107 108 /* Probe console from secure DT and fallback to non-secure DT */ 109 offs = find_chosen_node(fdt); 110 if (offs < 0) { 111 DMSG("No console directive from DTB"); 112 return TEE_ERROR_ITEM_NOT_FOUND; 113 } 114 115 prop = fdt_get_property(fdt, offs, "stdout-path", NULL); 116 if (!prop) { 117 /* 118 * A secure-chosen or chosen node is present but defined 119 * no stdout-path property: no console expected 120 */ 121 IMSG("Switching off console"); 122 register_serial_console(NULL); 123 return TEE_ERROR_ITEM_NOT_FOUND; 124 } 125 126 stdout_data = nex_strdup(prop->data); 127 if (!stdout_data) 128 panic(); 129 p = strchr(stdout_data, ':'); 130 if (p) { 131 *p = '\0'; 132 parms = p + 1; 133 } 134 135 /* stdout-path may refer to an alias */ 136 uart = fdt_get_alias(fdt, stdout_data); 137 if (!uart) { 138 /* Not an alias, assume we have a node path */ 139 uart = stdout_data; 140 } 141 offs = fdt_path_offset(fdt, uart); 142 if (offs >= 0) { 143 if (offs_out) 144 *offs_out = offs; 145 if (params_out) 146 *params_out = parms ? nex_strdup(parms) : NULL; 147 if (path_out) 148 *path_out = uart ? nex_strdup(uart) : NULL; 149 150 rc = TEE_SUCCESS; 151 } 152 153 nex_free(stdout_data); 154 155 return rc; 156 } 157 158 void configure_console_from_dt(void) 159 { 160 const struct dt_driver *dt_drv; 161 const struct serial_driver *sdrv; 162 struct serial_chip *dev; 163 char *uart = NULL; 164 char *parms = NULL; 165 void *fdt; 166 int offs; 167 168 fdt = get_dt(); 169 170 if (IS_ENABLED(CFG_CBMEM_CONSOLE) && cbmem_console_init_from_dt(fdt)) 171 return; 172 173 if (get_console_node_from_dt(fdt, &offs, &uart, &parms)) 174 return; 175 176 dt_drv = dt_find_compatible_driver(fdt, offs); 177 if (!dt_drv || dt_drv->type != DT_DRIVER_UART) 178 goto out; 179 180 sdrv = (const struct serial_driver *)dt_drv->driver; 181 if (!sdrv) 182 goto out; 183 184 dev = sdrv->dev_alloc(); 185 if (!dev) 186 goto out; 187 188 /* 189 * If the console is the same as the early console, dev_init() might 190 * clear pending data. Flush to avoid that. 191 */ 192 console_flush(); 193 if (sdrv->dev_init(dev, fdt, offs, parms) < 0) { 194 sdrv->dev_free(dev); 195 goto out; 196 } 197 198 IMSG("Switching console to device: %s", uart); 199 register_serial_console(dev); 200 out: 201 nex_free(uart); 202 nex_free(parms); 203 } 204 205 #endif /* CFG_DT */ 206