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