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 14 #ifdef CFG_DT 15 #include <kernel/dt.h> 16 #include <libfdt.h> 17 #endif 18 19 static struct serial_chip *serial_console; 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) 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 if (!fdt) 48 return -1; 49 50 int 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_out, int *offs_out, 59 const char **path_out, 60 const char **params_out) 61 { 62 const struct fdt_property *prop; 63 const char *uart; 64 const char *parms = NULL; 65 void *fdt; 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 fdt = get_embedded_dt(); 73 offs = find_chosen_node(fdt); 74 if (offs < 0) { 75 fdt = get_external_dt(); 76 offs = find_chosen_node(fdt); 77 } 78 if (offs < 0) { 79 DMSG("No console directive from DTB"); 80 return TEE_ERROR_ITEM_NOT_FOUND; 81 } 82 83 prop = fdt_get_property(fdt, offs, "stdout-path", NULL); 84 if (!prop) { 85 /* 86 * A secure-chosen or chosen node is present but defined 87 * no stdout-path property: no console expected 88 */ 89 IMSG("Switching off console"); 90 register_serial_console(NULL); 91 return TEE_ERROR_ITEM_NOT_FOUND; 92 } 93 94 stdout_data = strdup(prop->data); 95 if (!stdout_data) 96 panic(); 97 p = strchr(stdout_data, ':'); 98 if (p) { 99 *p = '\0'; 100 parms = p + 1; 101 } 102 103 /* stdout-path may refer to an alias */ 104 uart = fdt_get_alias(fdt, stdout_data); 105 if (!uart) { 106 /* Not an alias, assume we have a node path */ 107 uart = stdout_data; 108 } 109 offs = fdt_path_offset(fdt, uart); 110 if (offs >= 0) { 111 if (fdt_out) 112 *fdt_out = fdt; 113 if (offs_out) 114 *offs_out = offs; 115 if (params_out) 116 *params_out = parms; 117 if (path_out) 118 *path_out = uart; 119 120 rc = TEE_SUCCESS; 121 } 122 123 free(stdout_data); 124 125 return rc; 126 } 127 128 void configure_console_from_dt(void) 129 { 130 const struct dt_driver *dt_drv; 131 const struct serial_driver *sdrv; 132 struct serial_chip *dev; 133 const char *uart; 134 const char *parms; 135 void *fdt; 136 int offs; 137 138 if (get_console_node_from_dt(&fdt, &offs, &uart, &parms)) 139 return; 140 141 dt_drv = dt_find_compatible_driver(fdt, offs); 142 if (!dt_drv) 143 return; 144 145 sdrv = (const struct serial_driver *)dt_drv->driver; 146 if (!sdrv) 147 return; 148 149 dev = sdrv->dev_alloc(); 150 if (!dev) 151 return; 152 153 /* 154 * If the console is the same as the early console, dev_init() might 155 * clear pending data. Flush to avoid that. 156 */ 157 console_flush(); 158 if (sdrv->dev_init(dev, fdt, offs, parms) < 0) { 159 sdrv->dev_free(dev); 160 return; 161 } 162 163 IMSG("Switching console to device: %s", uart); 164 register_serial_console(dev); 165 } 166 167 #endif /* CFG_DT */ 168