1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (C) 2019 Leandro Lupori 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 #include <sys/linker_set.h> 29 30 #include <dev/ofw/openfirm.h> 31 #include <gdb/gdb.h> 32 33 #include "phyp-hvcall.h" 34 35 static gdb_probe_f uart_phyp_dbg_probe; 36 static gdb_init_f uart_phyp_dbg_init; 37 static gdb_term_f uart_phyp_dbg_term; 38 static gdb_getc_f uart_phyp_dbg_getc; 39 static gdb_putc_f uart_phyp_dbg_putc; 40 41 GDB_DBGPORT(uart_phyp, uart_phyp_dbg_probe, 42 uart_phyp_dbg_init, uart_phyp_dbg_term, 43 uart_phyp_dbg_getc, uart_phyp_dbg_putc); 44 45 static struct uart_phyp_dbgport { 46 cell_t vtermid; 47 union { 48 uint64_t u64[2]; 49 char str[16]; 50 } inbuf; 51 uint64_t inbuflen; 52 } dbgport; 53 54 static int 55 uart_phyp_dbg_probe(void) 56 { 57 char buf[64]; 58 cell_t reg; 59 phandle_t vty; 60 61 if (!getenv_string("hw.uart.dbgport", buf, sizeof(buf))) 62 return (-1); 63 64 if ((vty = OF_finddevice(buf)) == -1) 65 return (-1); 66 67 if (OF_getprop(vty, "name", buf, sizeof(buf)) <= 0) 68 return (-1); 69 if (strcmp(buf, "vty") != 0) 70 return (-1); 71 72 if (OF_getprop(vty, "device_type", buf, sizeof(buf)) == -1) 73 return (-1); 74 if (strcmp(buf, "serial") != 0) 75 return (-1); 76 77 if (OF_getprop(vty, "compatible", buf, sizeof(buf)) <= 0) 78 return (-1); 79 if (strcmp(buf, "hvterm1") != 0) 80 return (-1); 81 82 reg = ~0U; 83 OF_getencprop(vty, "reg", ®, sizeof(reg)); 84 if (reg == ~0U) 85 return (-1); 86 87 dbgport.vtermid = reg; 88 dbgport.inbuflen = 0; 89 90 return (0); 91 } 92 93 static void 94 uart_phyp_dbg_init(void) 95 { 96 } 97 98 static void 99 uart_phyp_dbg_term(void) 100 { 101 } 102 103 static int 104 uart_phyp_dbg_getc(void) 105 { 106 int c, err, next; 107 108 if (dbgport.inbuflen == 0) { 109 err = phyp_pft_hcall(H_GET_TERM_CHAR, dbgport.vtermid, 110 0, 0, 0, &dbgport.inbuflen, &dbgport.inbuf.u64[0], 111 &dbgport.inbuf.u64[1]); 112 if (err != H_SUCCESS) 113 return (-1); 114 } 115 116 if (dbgport.inbuflen == 0) 117 return (-1); 118 119 c = dbgport.inbuf.str[0]; 120 dbgport.inbuflen--; 121 122 if (dbgport.inbuflen == 0) 123 return (c); 124 125 /* 126 * Since version 2.11.0, QEMU became bug-compatible 127 * with PowerVM's vty, by inserting a \0 after every \r. 128 * Filter it here. 129 */ 130 next = 1; 131 if (c == '\r' && dbgport.inbuf.str[next] == '\0') { 132 next++; 133 dbgport.inbuflen--; 134 } 135 136 if (dbgport.inbuflen > 0) 137 memmove(&dbgport.inbuf.str[0], &dbgport.inbuf.str[next], 138 dbgport.inbuflen); 139 140 return (c); 141 } 142 143 static void 144 uart_phyp_dbg_putc(int c) 145 { 146 int err; 147 148 union { 149 uint64_t u64; 150 unsigned char bytes[8]; 151 } cbuf; 152 153 cbuf.bytes[0] = (unsigned char)c; 154 155 do { 156 err = phyp_hcall(H_PUT_TERM_CHAR, dbgport.vtermid, 1, 157 cbuf.u64, 0); 158 DELAY(100); 159 } while (err == H_BUSY); 160 } 161