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 if (!fdt) 46 return -1; 47 48 int offset = fdt_path_offset(fdt, "/secure-chosen"); 49 50 if (offset < 0) 51 offset = fdt_path_offset(fdt, "/chosen"); 52 53 return offset; 54 } 55 56 TEE_Result get_console_node_from_dt(void *fdt, int *offs_out, 57 char **path_out, char **params_out) 58 { 59 const struct fdt_property *prop; 60 const char *uart; 61 const char *parms = NULL; 62 int offs; 63 char *stdout_data; 64 char *p; 65 TEE_Result rc = TEE_ERROR_GENERIC; 66 67 /* Probe console from secure DT and fallback to non-secure DT */ 68 offs = find_chosen_node(fdt); 69 if (offs < 0) { 70 DMSG("No console directive from DTB"); 71 return TEE_ERROR_ITEM_NOT_FOUND; 72 } 73 74 prop = fdt_get_property(fdt, offs, "stdout-path", NULL); 75 if (!prop) { 76 /* 77 * A secure-chosen or chosen node is present but defined 78 * no stdout-path property: no console expected 79 */ 80 IMSG("Switching off console"); 81 register_serial_console(NULL); 82 return TEE_ERROR_ITEM_NOT_FOUND; 83 } 84 85 stdout_data = nex_strdup(prop->data); 86 if (!stdout_data) 87 panic(); 88 p = strchr(stdout_data, ':'); 89 if (p) { 90 *p = '\0'; 91 parms = p + 1; 92 } 93 94 /* stdout-path may refer to an alias */ 95 uart = fdt_get_alias(fdt, stdout_data); 96 if (!uart) { 97 /* Not an alias, assume we have a node path */ 98 uart = stdout_data; 99 } 100 offs = fdt_path_offset(fdt, uart); 101 if (offs >= 0) { 102 if (offs_out) 103 *offs_out = offs; 104 if (params_out) 105 *params_out = parms ? nex_strdup(parms) : NULL; 106 if (path_out) 107 *path_out = uart ? nex_strdup(uart) : NULL; 108 109 rc = TEE_SUCCESS; 110 } 111 112 nex_free(stdout_data); 113 114 return rc; 115 } 116 117 void configure_console_from_dt(void) 118 { 119 const struct dt_driver *dt_drv; 120 const struct serial_driver *sdrv; 121 struct serial_chip *dev; 122 char *uart = NULL; 123 char *parms = NULL; 124 void *fdt; 125 int offs; 126 127 fdt = get_dt(); 128 if (get_console_node_from_dt(fdt, &offs, &uart, &parms)) 129 return; 130 131 dt_drv = dt_find_compatible_driver(fdt, offs); 132 if (!dt_drv) 133 goto out; 134 135 sdrv = (const struct serial_driver *)dt_drv->driver; 136 if (!sdrv) 137 goto out; 138 139 dev = sdrv->dev_alloc(); 140 if (!dev) 141 goto out; 142 143 /* 144 * If the console is the same as the early console, dev_init() might 145 * clear pending data. Flush to avoid that. 146 */ 147 console_flush(); 148 if (sdrv->dev_init(dev, fdt, offs, parms) < 0) { 149 sdrv->dev_free(dev); 150 goto out; 151 } 152 153 IMSG("Switching console to device: %s", uart); 154 register_serial_console(dev); 155 out: 156 nex_free(uart); 157 nex_free(parms); 158 } 159 160 #endif /* CFG_DT */ 161