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