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