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