sconfig: Move (WEAK_)DEV_PTR from device.h to static.h
[coreboot.git] / payloads / libpayload / gdb / transport.c
blob66a6f20b9431b083b3d8a886648ed3c304071c35
1 /*
2 * Copyright 2014 Google Inc.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but without any warranty; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include <endian.h>
16 #include <gdb.h>
17 #include <libpayload.h>
19 #define OUTPUT_OVERRUN_MSG "GDB output buffer overrun (try increasing reply.size)!\n"
21 /* MMIO word size is not standardized, but *usually* 32 (even on ARM64) */
22 typedef u32 mmio_word_t;
24 static const int timeout_us = 100 * 1000;
26 /* Serial-specific glue code... add more transport layers here when desired. */
28 static void gdb_raw_putchar(u8 c)
30 serial_putchar(c);
33 static int gdb_raw_getchar(void)
35 u64 start = timer_us(0);
37 while (!serial_havechar())
38 if (timer_us(start) > timeout_us)
39 return -1;
41 return serial_getchar();
44 void gdb_transport_init(void)
46 console_remove_output_driver(serial_putchar);
49 void gdb_transport_teardown(void)
51 serial_console_init();
54 /* Hex digit character <-> number conversion (illegal chars undefined!). */
56 static u8 from_hex(unsigned char c)
58 static const s8 values[] = {
59 -1, 10, 11, 12, 13, 14, 15, -1,
60 -1, -1, -1, -1, -1, -1, -1, -1,
61 0, 1, 2, 3, 4, 5, 6, 7,
62 8, 9, -1, -1, -1, -1, -1, -1,
65 return values[c & 0x1f];
68 static char to_hex(u8 v)
70 static const char digits[] = "0123456789abcdef";
72 return digits[v & 0xf];
75 /* Message encode/decode functions (must access whole aligned words for MMIO) */
77 void gdb_message_encode_bytes(struct gdb_message *message, const void *data,
78 int length)
80 die_if(message->used + length * 2 > message->size, OUTPUT_OVERRUN_MSG);
81 const mmio_word_t *aligned =
82 (mmio_word_t *)ALIGN_DOWN((uintptr_t)data, sizeof(*aligned));
83 mmio_word_t word = be32toh(readl(aligned++));
84 while (length--) {
85 u8 byte = (word >> ((((void *)aligned - data) - 1) * 8));
86 message->buf[message->used++] = to_hex(byte >> 4);
87 message->buf[message->used++] = to_hex(byte & 0xf);
88 if (length && ++data == (void *)aligned)
89 word = be32toh(readl(aligned++));
93 void gdb_message_decode_bytes(const struct gdb_message *message, int offset,
94 void *data, int length)
96 die_if(offset + 2 * length > message->used, "Decode overrun in GDB "
97 "message: %.*s", message->used, message->buf);
98 mmio_word_t *aligned =
99 (mmio_word_t *)ALIGN_DOWN((uintptr_t)data, sizeof(*aligned));
100 int shift = ((void *)(aligned + 1) - data) * 8;
101 mmio_word_t word = be32toh(readl(aligned)) >> shift;
102 while (length--) {
103 word <<= 8;
104 word |= from_hex(message->buf[offset++]) << 4;
105 word |= from_hex(message->buf[offset++]);
106 if (++data - (void *)aligned == sizeof(*aligned))
107 writel(htobe32(word), aligned++);
109 if (data != (void *)aligned) {
110 shift = ((void *)(aligned + 1) - data) * 8;
111 clrsetbits_be32(aligned, ~((1 << shift) - 1), word << shift);
115 void gdb_message_encode_zero_bytes(struct gdb_message *message, int length)
117 die_if(message->used + length * 2 > message->size, OUTPUT_OVERRUN_MSG);
118 memset(message->buf + message->used, '0', length * 2);
119 message->used += length * 2;
122 void gdb_message_add_string(struct gdb_message *message, const char *string)
124 message->used += strlcpy((char *)message->buf + message->used,
125 string, message->size - message->used);
127 /* Check >= instead of > to account for strlcpy's trailing '\0'. */
128 die_if(message->used >= message->size, OUTPUT_OVERRUN_MSG);
131 void gdb_message_encode_int(struct gdb_message *message, uintptr_t val)
133 int length = sizeof(uintptr_t) * 2 - __builtin_clz(val) / 4;
134 die_if(message->used + length > message->size, OUTPUT_OVERRUN_MSG);
135 while (length--)
136 message->buf[message->used++] =
137 to_hex((val >> length * 4) & 0xf);
140 uintptr_t gdb_message_decode_int(const struct gdb_message *message, int offset,
141 int length)
143 uintptr_t val = 0;
145 die_if(length > sizeof(uintptr_t) * 2, "GDB decoding invalid number: "
146 "%.*s", message->used, message->buf);
148 while (length--) {
149 val <<= 4;
150 val |= from_hex(message->buf[offset++]);
153 return val;
156 /* Like strtok/strsep: writes back offset argument, returns original offset. */
157 int gdb_message_tokenize(const struct gdb_message *message, int *offset)
159 int token = *offset;
160 while (!strchr(",;:", message->buf[(*offset)++]))
161 die_if(*offset >= message->used, "Undelimited token in GDB "
162 "message at offset %d: %.*s",
163 token, message->used, message->buf);
164 return token;
167 /* High-level send/receive functions. */
169 void gdb_get_command(struct gdb_message *command)
171 enum command_state {
172 STATE_WAITING,
173 STATE_COMMAND,
174 STATE_CHECKSUM0,
175 STATE_CHECKSUM1,
178 u8 checksum = 0;
179 u8 running_checksum = 0;
180 enum command_state state = STATE_WAITING;
182 while (1) {
183 int c = gdb_raw_getchar();
184 if (c < 0) {
186 * Timeout waiting for a byte. Reset the
187 * state machine.
189 state = STATE_WAITING;
190 continue;
193 switch (state) {
194 case STATE_WAITING:
195 if (c == '$') {
196 running_checksum = 0;
197 command->used = 0;
198 state = STATE_COMMAND;
200 break;
201 case STATE_COMMAND:
202 if (c == '#') {
203 state = STATE_CHECKSUM0;
204 break;
206 die_if(command->used >= command->size, "GDB input buf"
207 "fer overrun (try increasing command.size)!\n");
208 command->buf[command->used++] = c;
209 running_checksum += c;
210 break;
211 case STATE_CHECKSUM0:
212 checksum = from_hex(c) << 4;
213 state = STATE_CHECKSUM1;
214 break;
215 case STATE_CHECKSUM1:
216 checksum += from_hex(c);
217 if (running_checksum == checksum) {
218 gdb_raw_putchar('+');
219 return;
220 } else {
221 state = STATE_WAITING;
222 gdb_raw_putchar('-');
224 break;
229 void gdb_send_reply(const struct gdb_message *reply)
231 int i;
232 int retries = 1 * 1000 * 1000 / timeout_us;
233 u8 checksum = 0;
235 for (i = 0; i < reply->used; i++)
236 checksum += reply->buf[i];
238 do {
239 gdb_raw_putchar('$');
240 for (i = 0; i < reply->used; i++)
241 gdb_raw_putchar(reply->buf[i]);
242 gdb_raw_putchar('#');
243 gdb_raw_putchar(to_hex(checksum >> 4));
244 gdb_raw_putchar(to_hex(checksum & 0xf));
245 } while (gdb_raw_getchar() != '+' && retries--);