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