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