stlink: expose ap number and csw in memory r/w
[openocd.git] / src / rtos / linux.c
1 /***************************************************************************
2 * Copyright (C) 2011 by STEricsson *
3 * Heythem Bouhaja heythem.bouhaja@stericsson.com : creation *
4 * Michel JAOUEN michel.jaouen@stericsson.com : adaptation to rtos *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 ***************************************************************************/
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <helper/time_support.h>
25 #include <jtag/jtag.h>
26 #include "target/target.h"
27 #include "target/target_type.h"
28 #include "helper/log.h"
29 #include "helper/types.h"
30 #include "rtos.h"
31 #include "rtos_standard_stackings.h"
32 #include <target/register.h>
33 #include "server/gdb_server.h"
34
35 #define LINUX_USER_KERNEL_BORDER 0xc0000000
36 #include "linux_header.h"
37 #define PHYS
38 #define MAX_THREADS 200
39 /* specific task */
40 struct linux_os {
41 const char *name;
42 uint32_t init_task_addr;
43 int thread_count;
44 int threadid_count;
45 int preupdtate_threadid_count;
46 int nr_cpus;
47 int threads_lookup;
48 int threads_needs_update;
49 struct current_thread *current_threads;
50 struct threads *thread_list;
51 /* virt2phys parameter */
52 uint32_t phys_mask;
53 uint32_t phys_base;
54 };
55
56 struct current_thread {
57 int64_t threadid;
58 int32_t core_id;
59 #ifdef PID_CHECK
60 uint32_t pid;
61 #endif
62 uint32_t TS;
63 struct current_thread *next;
64 };
65
66 struct threads {
67 char name[17];
68 uint32_t base_addr; /* address to read magic */
69 uint32_t state; /* magic value : filled only at creation */
70 uint32_t pid; /* linux pid : id for identifying a thread */
71 uint32_t oncpu; /* content cpu number in current thread */
72 uint32_t asid; /* filled only at creation */
73 int64_t threadid;
74 int status; /* dead = 1 alive = 2 current = 3 alive and current */
75 /* value that should not change during the live of a thread ? */
76 uint32_t thread_info_addr; /* contain latest thread_info_addr computed */
77 /* retrieve from thread_info */
78 struct cpu_context *context;
79 struct threads *next;
80 };
81
82 struct cpu_context {
83 uint32_t R4;
84 uint32_t R5;
85 uint32_t R6;
86 uint32_t R7;
87 uint32_t R8;
88 uint32_t R9;
89 uint32_t IP;
90 uint32_t FP;
91 uint32_t SP;
92 uint32_t PC;
93 uint32_t preempt_count;
94 };
95 static struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr,
96 uint32_t *info_addr);
97 static int insert_into_threadlist(struct target *target, struct threads *t);
98
99 static int linux_os_create(struct target *target);
100
101 static int linux_os_dummy_update(struct rtos *rtos)
102 {
103 /* update is done only when thread request come
104 * too many thread to do it on each stop */
105 return 0;
106 }
107
108 static int linux_compute_virt2phys(struct target *target, target_addr_t address)
109 {
110 struct linux_os *linux_os = (struct linux_os *)
111 target->rtos->rtos_specific_params;
112 target_addr_t pa = 0;
113 int retval = target->type->virt2phys(target, address, &pa);
114 if (retval != ERROR_OK) {
115 LOG_ERROR("Cannot compute linux virt2phys translation");
116 /* fixes default address */
117 linux_os->phys_base = 0;
118 return ERROR_FAIL;
119 }
120
121 linux_os->init_task_addr = address;
122 address = address & linux_os->phys_mask;
123 linux_os->phys_base = pa - address;
124 return ERROR_OK;
125 }
126
127 static int linux_read_memory(struct target *target,
128 uint32_t address, uint32_t size, uint32_t count,
129 uint8_t *buffer)
130 {
131 #ifdef PHYS
132 struct linux_os *linux_os = (struct linux_os *)
133 target->rtos->rtos_specific_params;
134 uint32_t pa = (address & linux_os->phys_mask) + linux_os->phys_base;
135 #endif
136 if (address < 0xc000000) {
137 LOG_ERROR("linux awareness : address in user space");
138 return ERROR_FAIL;
139 }
140 #ifdef PHYS
141 target_read_phys_memory(target, pa, size, count, buffer);
142 #endif
143 target_read_memory(target, address, size, count, buffer);
144 return ERROR_OK;
145 }
146
147 static int fill_buffer(struct target *target, uint32_t addr, uint8_t *buffer)
148 {
149
150 if ((addr & 0xfffffffc) != addr)
151 LOG_INFO("unaligned address %" PRIx32 "!!", addr);
152
153 int retval = linux_read_memory(target, addr, 4, 1, buffer);
154 return retval;
155
156 }
157
158 static uint32_t get_buffer(struct target *target, const uint8_t *buffer)
159 {
160 uint32_t value = 0;
161 const uint8_t *value_ptr = buffer;
162 value = target_buffer_get_u32(target, value_ptr);
163 return value;
164 }
165
166 static int linux_os_thread_reg_list(struct rtos *rtos,
167 int64_t thread_id, struct rtos_reg **reg_list, int *num_regs)
168 {
169 struct target *target = rtos->target;
170 struct linux_os *linux_os = (struct linux_os *)
171 target->rtos->rtos_specific_params;
172 struct current_thread *tmp = linux_os->current_threads;
173 struct current_thread *next;
174 int found = 0;
175 int retval;
176 /* check if a current thread is requested */
177 next = tmp;
178
179 do {
180 if (next->threadid == thread_id)
181 found = 1;
182 else
183 next = next->next;
184 } while ((found == 0) && (next != tmp) && (next));
185
186 if (found == 0) {
187 LOG_ERROR("could not find thread: %" PRIx64, thread_id);
188 return ERROR_FAIL;
189 }
190
191 /* search target to perform the access */
192 struct reg **gdb_reg_list;
193 struct target_list *head;
194 head = target->head;
195 found = 0;
196 do {
197 if (head->target->coreid == next->core_id) {
198 target = head->target;
199 found = 1;
200 break;
201 }
202 head = head->next;
203 } while (head);
204
205 if (found == 0) {
206 LOG_ERROR
207 (
208 "current thread %" PRIx64 ": no target to perform access of core id %" PRIx32,
209 thread_id,
210 next->core_id);
211 return ERROR_FAIL;
212 }
213
214 /*LOG_INFO("thread %lx current on core %x",thread_id, target->coreid);*/
215 retval = target_get_gdb_reg_list(target, &gdb_reg_list, num_regs, REG_CLASS_GENERAL);
216 if (retval != ERROR_OK)
217 return retval;
218
219 *reg_list = calloc(*num_regs, sizeof(struct rtos_reg));
220
221 for (int i = 0; i < *num_regs; ++i) {
222 if (!gdb_reg_list[i]->valid)
223 gdb_reg_list[i]->type->get(gdb_reg_list[i]);
224
225 (*reg_list)[i].number = gdb_reg_list[i]->number;
226 (*reg_list)[i].size = gdb_reg_list[i]->size;
227
228 buf_cpy(gdb_reg_list[i]->value, (*reg_list)[i].value, (*reg_list)[i].size);
229 }
230
231 return ERROR_OK;
232 }
233
234 static bool linux_os_detect(struct target *target)
235 {
236 LOG_INFO("should no be called");
237 return false;
238 }
239
240 static int linux_os_smp_init(struct target *target);
241 static int linux_os_clean(struct target *target);
242 #define INIT_TASK 0
243 static const char * const linux_symbol_list[] = {
244 "init_task",
245 NULL
246 };
247
248 static int linux_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
249 {
250 unsigned int i;
251 *symbol_list = (struct symbol_table_elem *)
252 calloc(ARRAY_SIZE(linux_symbol_list), sizeof(struct symbol_table_elem));
253
254 for (i = 0; i < ARRAY_SIZE(linux_symbol_list); i++)
255 (*symbol_list)[i].symbol_name = linux_symbol_list[i];
256
257 return 0;
258 }
259
260 static char *linux_ps_command(struct target *target);
261
262 const struct rtos_type linux_rtos = {
263 .name = "linux",
264 .detect_rtos = linux_os_detect,
265 .create = linux_os_create,
266 .smp_init = linux_os_smp_init,
267 .update_threads = linux_os_dummy_update,
268 .get_thread_reg_list = linux_os_thread_reg_list,
269 .get_symbol_list_to_lookup = linux_get_symbol_list_to_lookup,
270 .clean = linux_os_clean,
271 .ps_command = linux_ps_command,
272 };
273
274 static int linux_thread_packet(struct connection *connection, char const *packet,
275 int packet_size);
276 static void linux_identify_current_threads(struct target *target);
277
278 #ifdef PID_CHECK
279 int fill_task_pid(struct target *target, struct threads *t)
280 {
281 uint32_t pid_addr = t->base_addr + PID;
282 uint8_t buffer[4];
283 int retval = fill_buffer(target, pid_addr, buffer);
284
285 if (retval == ERROR_OK) {
286 uint32_t val = get_buffer(target, buffer);
287 t->pid = val;
288 } else
289 LOG_ERROR("fill_task_pid: unable to read memory");
290
291 return retval;
292 }
293 #endif
294
295 static int fill_task(struct target *target, struct threads *t)
296 {
297 int retval;
298 uint32_t pid_addr = t->base_addr + PID;
299 uint32_t mem_addr = t->base_addr + MEM;
300 uint32_t on_cpu = t->base_addr + ONCPU;
301 uint8_t *buffer = calloc(1, 4);
302 retval = fill_buffer(target, t->base_addr, buffer);
303
304 if (retval == ERROR_OK) {
305 uint32_t val = get_buffer(target, buffer);
306 t->state = val;
307 } else
308 LOG_ERROR("fill_task: unable to read memory");
309
310 retval = fill_buffer(target, pid_addr, buffer);
311
312 if (retval == ERROR_OK) {
313 uint32_t val = get_buffer(target, buffer);
314 t->pid = val;
315 } else
316 LOG_ERROR("fill task: unable to read memory");
317
318 retval = fill_buffer(target, on_cpu, buffer);
319
320 if (retval == ERROR_OK) {
321 uint32_t val = get_buffer(target, buffer);
322 t->oncpu = val;
323 } else
324 LOG_ERROR("fill task: unable to read memory");
325
326 retval = fill_buffer(target, mem_addr, buffer);
327
328 if (retval == ERROR_OK) {
329 uint32_t val = get_buffer(target, buffer);
330
331 if (val != 0) {
332 uint32_t asid_addr = val + MM_CTX;
333 retval = fill_buffer(target, asid_addr, buffer);
334
335 if (retval == ERROR_OK) {
336 val = get_buffer(target, buffer);
337 t->asid = val;
338 } else
339 LOG_ERROR
340 ("fill task: unable to read memory -- ASID");
341 } else
342 t->asid = 0;
343 } else
344 LOG_ERROR("fill task: unable to read memory");
345
346 free(buffer);
347
348 return retval;
349 }
350
351 static int get_name(struct target *target, struct threads *t)
352 {
353 int retval;
354 uint32_t full_name[4];
355 uint32_t comm = t->base_addr + COMM;
356 int i;
357
358 for (i = 0; i < 17; i++)
359 t->name[i] = 0;
360
361 retval = linux_read_memory(target, comm, 4, 4, (uint8_t *) full_name);
362
363 if (retval != ERROR_OK) {
364 LOG_ERROR("get_name: unable to read memory\n");
365 return ERROR_FAIL;
366 }
367
368 uint32_t raw_name = target_buffer_get_u32(target,
369 (const uint8_t *)
370 &full_name[0]);
371 t->name[3] = raw_name >> 24;
372 t->name[2] = raw_name >> 16;
373 t->name[1] = raw_name >> 8;
374 t->name[0] = raw_name;
375 raw_name =
376 target_buffer_get_u32(target, (const uint8_t *)&full_name[1]);
377 t->name[7] = raw_name >> 24;
378 t->name[6] = raw_name >> 16;
379 t->name[5] = raw_name >> 8;
380 t->name[4] = raw_name;
381 raw_name =
382 target_buffer_get_u32(target, (const uint8_t *)&full_name[2]);
383 t->name[11] = raw_name >> 24;
384 t->name[10] = raw_name >> 16;
385 t->name[9] = raw_name >> 8;
386 t->name[8] = raw_name;
387 raw_name =
388 target_buffer_get_u32(target, (const uint8_t *)&full_name[3]);
389 t->name[15] = raw_name >> 24;
390 t->name[14] = raw_name >> 16;
391 t->name[13] = raw_name >> 8;
392 t->name[12] = raw_name;
393 return ERROR_OK;
394
395 }
396
397 static int get_current(struct target *target, int create)
398 {
399 struct target_list *head;
400 head = target->head;
401 uint8_t *buf;
402 uint32_t val;
403 uint32_t ti_addr;
404 uint8_t *buffer = calloc(1, 4);
405 struct linux_os *linux_os = (struct linux_os *)
406 target->rtos->rtos_specific_params;
407 struct current_thread *ctt = linux_os->current_threads;
408
409 /* invalid current threads content */
410 while (ctt) {
411 ctt->threadid = -1;
412 ctt->TS = 0xdeadbeef;
413 ctt = ctt->next;
414 }
415
416 while (head) {
417 struct reg **reg_list;
418 int reg_list_size;
419 int retval;
420
421 if (target_get_gdb_reg_list(head->target, &reg_list,
422 &reg_list_size, REG_CLASS_GENERAL) != ERROR_OK) {
423 free(buffer);
424 return ERROR_TARGET_FAILURE;
425 }
426
427 if (!reg_list[13]->valid)
428 reg_list[13]->type->get(reg_list[13]);
429
430 buf = reg_list[13]->value;
431 val = get_buffer(target, buf);
432 ti_addr = (val & 0xffffe000);
433 uint32_t ts_addr = ti_addr + 0xc;
434 retval = fill_buffer(target, ts_addr, buffer);
435
436 if (retval == ERROR_OK) {
437 uint32_t TS = get_buffer(target, buffer);
438 uint32_t cpu, on_cpu = TS + ONCPU;
439 retval = fill_buffer(target, on_cpu, buffer);
440
441 if (retval == ERROR_OK) {
442 /*uint32_t cpu = get_buffer(target, buffer);*/
443 struct current_thread *ct =
444 linux_os->current_threads;
445 cpu = head->target->coreid;
446
447 while ((ct) && (ct->core_id != (int32_t) cpu))
448 ct = ct->next;
449
450 if ((ct) && (ct->TS == 0xdeadbeef))
451 ct->TS = TS;
452 else
453 LOG_ERROR
454 ("error in linux current thread update");
455
456 if (create && ct) {
457 struct threads *t;
458 t = calloc(1, sizeof(struct threads));
459 t->base_addr = ct->TS;
460 fill_task(target, t);
461 get_name(target, t);
462 t->oncpu = cpu;
463 insert_into_threadlist(target, t);
464 t->status = 3;
465 t->thread_info_addr = 0xdeadbeef;
466 ct->threadid = t->threadid;
467 linux_os->thread_count++;
468 #ifdef PID_CHECK
469 ct->pid = t->pid;
470 #endif
471 /*LOG_INFO("Creation of current thread %s",t->name);*/
472 }
473 }
474 }
475
476 free(reg_list);
477 head = head->next;
478 }
479
480 free(buffer);
481
482 return ERROR_OK;
483 }
484
485 static struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr,
486 uint32_t *thread_info_addr_old)
487 {
488 struct cpu_context *context = calloc(1, sizeof(struct cpu_context));
489 uint32_t preempt_count_addr = 0;
490 uint32_t registers[10];
491 uint8_t *buffer = calloc(1, 4);
492 uint32_t stack = base_addr + QAT;
493 uint32_t thread_info_addr = 0;
494 uint32_t thread_info_addr_update = 0;
495 int retval = ERROR_FAIL;
496 context->R4 = 0xdeadbeef;
497 context->R5 = 0xdeadbeef;
498 context->R6 = 0xdeadbeef;
499 context->R7 = 0xdeadbeef;
500 context->R8 = 0xdeadbeef;
501 context->R9 = 0xdeadbeef;
502 context->IP = 0xdeadbeef;
503 context->FP = 0xdeadbeef;
504 context->SP = 0xdeadbeef;
505 context->PC = 0xdeadbeef;
506 retry:
507
508 if (*thread_info_addr_old == 0xdeadbeef) {
509 retval = fill_buffer(target, stack, buffer);
510
511 if (retval == ERROR_OK)
512 thread_info_addr = get_buffer(target, buffer);
513 else
514 LOG_ERROR("cpu_context: unable to read memory");
515
516 thread_info_addr_update = thread_info_addr;
517 } else
518 thread_info_addr = *thread_info_addr_old;
519
520 preempt_count_addr = thread_info_addr + PREEMPT;
521 retval = fill_buffer(target, preempt_count_addr, buffer);
522
523 if (retval == ERROR_OK)
524 context->preempt_count = get_buffer(target, buffer);
525 else {
526 if (*thread_info_addr_old != 0xdeadbeef) {
527 LOG_ERROR
528 ("cpu_context: cannot read at thread_info_addr");
529
530 if (*thread_info_addr_old < LINUX_USER_KERNEL_BORDER)
531 LOG_INFO
532 ("cpu_context : thread_info_addr in userspace!!!");
533
534 *thread_info_addr_old = 0xdeadbeef;
535 goto retry;
536 }
537
538 LOG_ERROR("cpu_context: unable to read memory");
539 }
540
541 thread_info_addr += CPU_CONT;
542
543 retval = linux_read_memory(target, thread_info_addr, 4, 10,
544 (uint8_t *) registers);
545
546 if (retval != ERROR_OK) {
547 free(buffer);
548 LOG_ERROR("cpu_context: unable to read memory\n");
549 return context;
550 }
551
552 context->R4 =
553 target_buffer_get_u32(target, (const uint8_t *)&registers[0]);
554 context->R5 =
555 target_buffer_get_u32(target, (const uint8_t *)&registers[1]);
556 context->R6 =
557 target_buffer_get_u32(target, (const uint8_t *)&registers[2]);
558 context->R7 =
559 target_buffer_get_u32(target, (const uint8_t *)&registers[3]);
560 context->R8 =
561 target_buffer_get_u32(target, (const uint8_t *)&registers[4]);
562 context->R9 =
563 target_buffer_get_u32(target, (const uint8_t *)&registers[5]);
564 context->IP =
565 target_buffer_get_u32(target, (const uint8_t *)&registers[6]);
566 context->FP =
567 target_buffer_get_u32(target, (const uint8_t *)&registers[7]);
568 context->SP =
569 target_buffer_get_u32(target, (const uint8_t *)&registers[8]);
570 context->PC =
571 target_buffer_get_u32(target, (const uint8_t *)&registers[9]);
572
573 if (*thread_info_addr_old == 0xdeadbeef)
574 *thread_info_addr_old = thread_info_addr_update;
575
576 free(buffer);
577
578 return context;
579 }
580
581 static uint32_t next_task(struct target *target, struct threads *t)
582 {
583 uint8_t *buffer = calloc(1, 4);
584 uint32_t next_addr = t->base_addr + NEXT;
585 int retval = fill_buffer(target, next_addr, buffer);
586
587 if (retval == ERROR_OK) {
588 uint32_t val = get_buffer(target, buffer);
589 val = val - NEXT;
590 free(buffer);
591 return val;
592 } else
593 LOG_ERROR("next task: unable to read memory");
594
595 free(buffer);
596
597 return 0;
598 }
599
600 static struct current_thread *add_current_thread(struct current_thread *currents,
601 struct current_thread *ct)
602 {
603 ct->next = NULL;
604
605 if (!currents) {
606 currents = ct;
607 return currents;
608 } else {
609 struct current_thread *temp = currents;
610
611 while (temp->next)
612 temp = temp->next;
613
614 temp->next = ct;
615 return currents;
616 }
617 }
618
619 static struct threads *liste_del_task(struct threads *task_list, struct threads **t,
620 struct threads *prev)
621 {
622 LOG_INFO("del task %" PRId64, (*t)->threadid);
623 if (prev)
624 prev->next = (*t)->next;
625 else
626 task_list = (*t)->next;
627
628 /* free content of threads */
629 free((*t)->context);
630
631 free(*t);
632 *t = prev ? prev : task_list;
633 return task_list;
634 }
635
636 static struct threads *liste_add_task(struct threads *task_list, struct threads *t,
637 struct threads **last)
638 {
639 t->next = NULL;
640
641 if (!*last)
642 if (!task_list) {
643 task_list = t;
644 return task_list;
645 } else {
646 struct threads *temp = task_list;
647
648 while (temp->next)
649 temp = temp->next;
650
651 temp->next = t;
652 *last = t;
653 return task_list;
654 } else {
655 (*last)->next = t;
656 *last = t;
657 return task_list;
658 }
659 }
660
661 #ifdef PID_CHECK
662 static int current_pid(struct linux_os *linux_os, uint32_t pid)
663 #else
664 static int current_base_addr(struct linux_os *linux_os, uint32_t base_addr)
665 #endif
666 {
667 struct current_thread *ct = linux_os->current_threads;
668 #ifdef PID_CHECK
669
670 while ((ct) && (ct->pid != pid))
671 #else
672 while ((ct) && (ct->TS != base_addr))
673 #endif
674 ct = ct->next;
675 #ifdef PID_CHECK
676 if ((ct) && (ct->pid == pid))
677 #else
678 if ((ct) && (ct->TS == base_addr))
679 #endif
680 return 1;
681
682 return 0;
683 }
684
685 static int linux_get_tasks(struct target *target, int context)
686 {
687 int loop = 0;
688 int retval = 0;
689 struct linux_os *linux_os = (struct linux_os *)
690 target->rtos->rtos_specific_params;
691 linux_os->thread_list = NULL;
692 linux_os->thread_count = 0;
693
694 if (linux_os->init_task_addr == 0xdeadbeef) {
695 LOG_INFO("no init symbol\n");
696 return ERROR_FAIL;
697 }
698
699 int64_t start = timeval_ms();
700
701 struct threads *t = calloc(1, sizeof(struct threads));
702 struct threads *last = NULL;
703 t->base_addr = linux_os->init_task_addr;
704 /* retrieve the thread id , currently running in the different smp core */
705 get_current(target, 1);
706
707 while (((t->base_addr != linux_os->init_task_addr) &&
708 (t->base_addr != 0)) || (loop == 0)) {
709 loop++;
710 fill_task(target, t);
711 retval = get_name(target, t);
712
713 if (loop > MAX_THREADS) {
714 free(t);
715 LOG_INFO("more than %d threads !!", MAX_THREADS);
716 return ERROR_FAIL;
717 }
718
719 if (retval != ERROR_OK) {
720 free(t);
721 return ERROR_FAIL;
722 }
723
724 /* check that this thread is not one the current threads already
725 * created */
726 uint32_t base_addr;
727 #ifdef PID_CHECK
728
729 if (!current_pid(linux_os, t->pid)) {
730 #else
731 if (!current_base_addr(linux_os, t->base_addr)) {
732 #endif
733 t->threadid = linux_os->threadid_count;
734 t->status = 1;
735 linux_os->threadid_count++;
736
737 linux_os->thread_list =
738 liste_add_task(linux_os->thread_list, t, &last);
739 /* no interest to fill the context if it is a current thread. */
740 linux_os->thread_count++;
741 t->thread_info_addr = 0xdeadbeef;
742
743 if (context)
744 t->context =
745 cpu_context_read(target, t->base_addr,
746 &t->thread_info_addr);
747 base_addr = next_task(target, t);
748 } else {
749 /*LOG_INFO("thread %s is a current thread already created",t->name); */
750 base_addr = next_task(target, t);
751 free(t);
752 }
753
754 t = calloc(1, sizeof(struct threads));
755 t->base_addr = base_addr;
756 }
757
758 linux_os->threads_lookup = 1;
759 linux_os->threads_needs_update = 0;
760 linux_os->preupdtate_threadid_count = linux_os->threadid_count - 1;
761 /* check that all current threads have been identified */
762
763 LOG_INFO("complete time %" PRId64 ", thread mean %" PRId64 "\n",
764 (timeval_ms() - start),
765 (timeval_ms() - start) / linux_os->threadid_count);
766
767 LOG_INFO("threadid count %d", linux_os->threadid_count);
768 free(t);
769
770 return ERROR_OK;
771 }
772
773 static int clean_threadlist(struct target *target)
774 {
775 struct linux_os *linux_os = (struct linux_os *)
776 target->rtos->rtos_specific_params;
777 struct threads *old, *temp = linux_os->thread_list;
778
779 while (temp) {
780 old = temp;
781
782 free(temp->context);
783
784 temp = temp->next;
785 free(old);
786 }
787
788 return ERROR_OK;
789 }
790
791 static int linux_os_clean(struct target *target)
792 {
793 struct linux_os *os_linux = (struct linux_os *)
794 target->rtos->rtos_specific_params;
795 clean_threadlist(target);
796 os_linux->init_task_addr = 0xdeadbeef;
797 os_linux->name = "linux";
798 os_linux->thread_list = NULL;
799 os_linux->thread_count = 0;
800 os_linux->nr_cpus = 0;
801 os_linux->threads_lookup = 0;
802 os_linux->threads_needs_update = 0;
803 os_linux->threadid_count = 1;
804 return ERROR_OK;
805 }
806
807 static int insert_into_threadlist(struct target *target, struct threads *t)
808 {
809 struct linux_os *linux_os = (struct linux_os *)
810 target->rtos->rtos_specific_params;
811 struct threads *temp = linux_os->thread_list;
812 t->threadid = linux_os->threadid_count;
813 linux_os->threadid_count++;
814 t->status = 1;
815 t->next = NULL;
816
817 if (!temp)
818 linux_os->thread_list = t;
819 else {
820 while (temp->next)
821 temp = temp->next;
822
823 t->next = NULL;
824 temp->next = t;
825 }
826
827 return ERROR_OK;
828 }
829
830 static void linux_identify_current_threads(struct target *target)
831 {
832 struct linux_os *linux_os = (struct linux_os *)
833 target->rtos->rtos_specific_params;
834 struct threads *thread_list = linux_os->thread_list;
835 struct current_thread *ct = linux_os->current_threads;
836 struct threads *t = NULL;
837
838 while ((ct)) {
839 if (ct->threadid == -1) {
840
841 /* un-identified thread */
842 int found = 0;
843 t = calloc(1, sizeof(struct threads));
844 t->base_addr = ct->TS;
845 #ifdef PID_CHECK
846
847 if (fill_task_pid(target, t) != ERROR_OK) {
848 error_handling:
849 free(t);
850 LOG_ERROR
851 ("linux identify_current_threads: unable to read pid");
852 return;
853 }
854 #endif
855
856 /* search in the list of threads if pid
857 already present */
858 while ((thread_list) && (found == 0)) {
859 #ifdef PID_CHECK
860 if (thread_list->pid == t->pid) {
861 #else
862 if (thread_list->base_addr == t->base_addr) {
863 #endif
864 free(t);
865 t = thread_list;
866 found = 1;
867 }
868 thread_list = thread_list->next;
869 }
870
871 if (!found) {
872 /* it is a new thread */
873 if (fill_task(target, t) != ERROR_OK)
874 goto error_handling;
875
876 get_name(target, t);
877 insert_into_threadlist(target, t);
878 t->thread_info_addr = 0xdeadbeef;
879 }
880
881 t->status = 3;
882 ct->threadid = t->threadid;
883 #ifdef PID_CHECK
884 ct->pid = t->pid;
885 #endif
886 linux_os->thread_count++;
887 #if 0
888 if (found == 0)
889 LOG_INFO("current thread core %x identified %s",
890 ct->core_id, t->name);
891 else
892 LOG_INFO("current thread core %x, reused %s",
893 ct->core_id, t->name);
894 #endif
895 }
896 #if 0
897 else {
898 struct threads tmp;
899 tmp.base_addr = ct->TS;
900 get_name(target, &tmp);
901 LOG_INFO("current thread core %x , already identified %s !!!",
902 ct->core_id, tmp.name);
903 }
904 #endif
905 ct = ct->next;
906 }
907
908 return;
909 #ifndef PID_CHECK
910 error_handling:
911 free(t);
912 LOG_ERROR("unable to read pid");
913 return;
914
915 #endif
916 }
917
918 static int linux_task_update(struct target *target, int context)
919 {
920 struct linux_os *linux_os = (struct linux_os *)
921 target->rtos->rtos_specific_params;
922 struct threads *thread_list = linux_os->thread_list;
923 int retval;
924 int loop = 0;
925 linux_os->thread_count = 0;
926
927 /*thread_list = thread_list->next; skip init_task*/
928 while (thread_list) {
929 thread_list->status = 0; /*setting all tasks to dead state*/
930
931 free(thread_list->context);
932 thread_list->context = NULL;
933
934 thread_list = thread_list->next;
935 }
936
937 int found = 0;
938
939 if (linux_os->init_task_addr == 0xdeadbeef) {
940 LOG_INFO("no init symbol\n");
941 return ERROR_FAIL;
942 }
943 int64_t start = timeval_ms();
944 struct threads *t = calloc(1, sizeof(struct threads));
945 uint32_t previous = 0xdeadbeef;
946 t->base_addr = linux_os->init_task_addr;
947 retval = get_current(target, 0);
948 /*check that all current threads have been identified */
949 linux_identify_current_threads(target);
950
951 while (((t->base_addr != linux_os->init_task_addr) &&
952 (t->base_addr != previous)) || (loop == 0)) {
953 /* for avoiding any permanent loop for any reason possibly due to
954 * target */
955 loop++;
956 previous = t->base_addr;
957 /* read only pid */
958 #ifdef PID_CHECK
959 retval = fill_task_pid(target, t);
960 #endif
961
962 if (retval != ERROR_OK) {
963 free(t);
964 return ERROR_FAIL;
965 }
966
967 thread_list = linux_os->thread_list;
968
969 while (thread_list) {
970 #ifdef PID_CHECK
971 if (t->pid == thread_list->pid) {
972 #else
973 if (t->base_addr == thread_list->base_addr) {
974 #endif
975 if (!thread_list->status) {
976 #ifdef PID_CHECK
977 if (t->base_addr != thread_list->base_addr)
978 LOG_INFO("thread base_addr has changed !!");
979 #endif
980 /* this is not a current thread */
981 thread_list->base_addr = t->base_addr;
982 thread_list->status = 1;
983
984 /* we don 't update this field any more */
985
986 /*thread_list->state = t->state;
987 thread_list->oncpu = t->oncpu;
988 thread_list->asid = t->asid;
989 */
990 if (context)
991 thread_list->context =
992 cpu_context_read(target,
993 thread_list->base_addr,
994 &thread_list->thread_info_addr);
995 } else {
996 /* it is a current thread no need to read context */
997 }
998
999 linux_os->thread_count++;
1000 found = 1;
1001 break;
1002 } else {
1003 found = 0;
1004 thread_list = thread_list->next;
1005 }
1006 }
1007
1008 if (found == 0) {
1009 uint32_t base_addr;
1010 fill_task(target, t);
1011 get_name(target, t);
1012 retval = insert_into_threadlist(target, t);
1013 t->thread_info_addr = 0xdeadbeef;
1014
1015 if (context)
1016 t->context =
1017 cpu_context_read(target, t->base_addr,
1018 &t->thread_info_addr);
1019
1020 base_addr = next_task(target, t);
1021 t = calloc(1, sizeof(struct threads));
1022 t->base_addr = base_addr;
1023 linux_os->thread_count++;
1024 } else
1025 t->base_addr = next_task(target, t);
1026 }
1027
1028 LOG_INFO("update thread done %" PRId64 ", mean%" PRId64 "\n",
1029 (timeval_ms() - start), (timeval_ms() - start) / loop);
1030 free(t);
1031 linux_os->threads_needs_update = 0;
1032 return ERROR_OK;
1033 }
1034
1035 static int linux_gdb_thread_packet(struct target *target,
1036 struct connection *connection, char const *packet,
1037 int packet_size)
1038 {
1039 int retval;
1040 struct linux_os *linux_os =
1041 (struct linux_os *)target->rtos->rtos_specific_params;
1042
1043 if (linux_os->init_task_addr == 0xdeadbeef) {
1044 /* it has not been initialized */
1045 LOG_INFO("received thread request without init task address");
1046 gdb_put_packet(connection, "l", 1);
1047 return ERROR_OK;
1048 }
1049
1050 retval = linux_get_tasks(target, 1);
1051
1052 if (retval != ERROR_OK)
1053 return ERROR_TARGET_FAILURE;
1054
1055 char *out_str = calloc(MAX_THREADS * 17 + 10, 1);
1056 char *tmp_str = out_str;
1057 tmp_str += sprintf(tmp_str, "m");
1058 struct threads *temp = linux_os->thread_list;
1059
1060 while (temp) {
1061 tmp_str += sprintf(tmp_str, "%016" PRIx64, temp->threadid);
1062 temp = temp->next;
1063 if (temp)
1064 tmp_str += sprintf(tmp_str, ",");
1065 }
1066
1067 gdb_put_packet(connection, out_str, strlen(out_str));
1068 free(out_str);
1069 return ERROR_OK;
1070 }
1071
1072 static int linux_gdb_thread_update(struct target *target,
1073 struct connection *connection, char const *packet,
1074 int packet_size)
1075 {
1076 int found = 0;
1077 struct linux_os *linux_os = (struct linux_os *)
1078 target->rtos->rtos_specific_params;
1079 struct threads *temp = linux_os->thread_list;
1080
1081 while (temp) {
1082 if (temp->threadid == linux_os->preupdtate_threadid_count + 1) {
1083 /*LOG_INFO("FOUND");*/
1084 found = 1;
1085 break;
1086 } else
1087 temp = temp->next;
1088 }
1089
1090 if (found == 1) {
1091 /*LOG_INFO("INTO GDB THREAD UPDATE FOUNDING START TASK");*/
1092 char *out_strr = calloc(MAX_THREADS * 17 + 10, 1);
1093 char *tmp_strr = out_strr;
1094 tmp_strr += sprintf(tmp_strr, "m");
1095 /*LOG_INFO("CHAR MALLOC & M DONE");*/
1096 tmp_strr += sprintf(tmp_strr, "%016" PRIx64, temp->threadid);
1097
1098 temp = temp->next;
1099
1100 while (temp) {
1101 /*LOG_INFO("INTO GDB THREAD UPDATE WHILE");*/
1102 tmp_strr += sprintf(tmp_strr, ",");
1103 tmp_strr +=
1104 sprintf(tmp_strr, "%016" PRIx64, temp->threadid);
1105 temp = temp->next;
1106 }
1107
1108 /*tmp_str[0] = 0;*/
1109 gdb_put_packet(connection, out_strr, strlen(out_strr));
1110 linux_os->preupdtate_threadid_count =
1111 linux_os->threadid_count - 1;
1112 free(out_strr);
1113 } else
1114 gdb_put_packet(connection, "l", 1);
1115
1116 return ERROR_OK;
1117 }
1118
1119 static int linux_thread_extra_info(struct target *target,
1120 struct connection *connection, char const *packet,
1121 int packet_size)
1122 {
1123 int64_t threadid = 0;
1124 struct linux_os *linux_os = (struct linux_os *)
1125 target->rtos->rtos_specific_params;
1126 sscanf(packet, "qThreadExtraInfo,%" SCNx64, &threadid);
1127 /*LOG_INFO("lookup extra info for thread %" SCNx64, threadid);*/
1128 struct threads *temp = linux_os->thread_list;
1129
1130 while (temp) {
1131 if (temp->threadid == threadid) {
1132 char *pid = " PID: ";
1133 char *pid_current = "*PID: ";
1134 char *name = "Name: ";
1135 int str_size = strlen(pid) + strlen(name);
1136 char *tmp_str = calloc(1, str_size + 50);
1137 char *tmp_str_ptr = tmp_str;
1138
1139 /* discriminate current task */
1140 if (temp->status == 3)
1141 tmp_str_ptr += sprintf(tmp_str_ptr, "%s",
1142 pid_current);
1143 else
1144 tmp_str_ptr += sprintf(tmp_str_ptr, "%s", pid);
1145
1146 tmp_str_ptr += sprintf(tmp_str_ptr, "%d, ", (int)temp->pid);
1147 sprintf(tmp_str_ptr, "%s", name);
1148 sprintf(tmp_str_ptr, "%s", temp->name);
1149 char *hex_str = calloc(1, strlen(tmp_str) * 2 + 1);
1150 size_t pkt_len = hexify(hex_str, (const uint8_t *)tmp_str,
1151 strlen(tmp_str), strlen(tmp_str) * 2 + 1);
1152 gdb_put_packet(connection, hex_str, pkt_len);
1153 free(hex_str);
1154 free(tmp_str);
1155 return ERROR_OK;
1156 }
1157
1158 temp = temp->next;
1159 }
1160
1161 LOG_INFO("thread not found");
1162 return ERROR_OK;
1163 }
1164
1165 static int linux_gdb_t_packet(struct connection *connection,
1166 struct target *target, char const *packet, int packet_size)
1167 {
1168 int64_t threadid;
1169 struct linux_os *linux_os = (struct linux_os *)
1170 target->rtos->rtos_specific_params;
1171 int retval = ERROR_OK;
1172 sscanf(packet, "T%" SCNx64, &threadid);
1173
1174 if (linux_os->threads_needs_update == 0) {
1175 struct threads *temp = linux_os->thread_list;
1176 struct threads *prev = NULL;
1177
1178 while (temp) {
1179 if (temp->threadid == threadid) {
1180 if (temp->status != 0) {
1181 gdb_put_packet(connection, "OK", 2);
1182 return ERROR_OK;
1183 } else {
1184 /* delete item in the list */
1185 linux_os->thread_list =
1186 liste_del_task(linux_os->thread_list,
1187 &temp, prev);
1188 linux_os->thread_count--;
1189 gdb_put_packet(connection, "E01", 3);
1190 return ERROR_OK;
1191 }
1192 }
1193
1194 /* for deletion */
1195 prev = temp;
1196 temp = temp->next;
1197 }
1198
1199 LOG_INFO("gdb requested status on non existing thread");
1200 gdb_put_packet(connection, "E01", 3);
1201 return ERROR_OK;
1202
1203 } else {
1204 retval = linux_task_update(target, 1);
1205 struct threads *temp = linux_os->thread_list;
1206
1207 while (temp) {
1208 if (temp->threadid == threadid) {
1209 if (temp->status == 1) {
1210 gdb_put_packet(connection, "OK", 2);
1211 return ERROR_OK;
1212 } else {
1213 gdb_put_packet(connection, "E01", 3);
1214 return ERROR_OK;
1215 }
1216 }
1217
1218 temp = temp->next;
1219 }
1220 }
1221
1222 return retval;
1223 }
1224
1225 static int linux_gdb_h_packet(struct connection *connection,
1226 struct target *target, char const *packet, int packet_size)
1227 {
1228 struct linux_os *linux_os = (struct linux_os *)
1229 target->rtos->rtos_specific_params;
1230 struct current_thread *ct = linux_os->current_threads;
1231
1232 /* select to display the current thread of the selected target */
1233 while ((ct) && (ct->core_id != target->coreid))
1234 ct = ct->next;
1235
1236 int64_t current_gdb_thread_rq;
1237
1238 if (linux_os->threads_lookup == 1) {
1239 if ((ct) && (ct->threadid == -1)) {
1240 ct = linux_os->current_threads;
1241
1242 while ((ct) && (ct->threadid == -1))
1243 ct = ct->next;
1244 }
1245
1246 if (!ct) {
1247 /* no current thread can be identified
1248 * any way with smp */
1249 LOG_INFO("no current thread identified");
1250 /* attempt to display the name of the 2 threads identified with
1251 * get_current */
1252 struct threads t;
1253 ct = linux_os->current_threads;
1254
1255 while ((ct) && (ct->threadid == -1)) {
1256 t.base_addr = ct->TS;
1257 get_name(target, &t);
1258 LOG_INFO("name of unidentified thread %s",
1259 t.name);
1260 ct = ct->next;
1261 }
1262
1263 gdb_put_packet(connection, "OK", 2);
1264 return ERROR_OK;
1265 }
1266
1267 if (packet[1] == 'g') {
1268 sscanf(packet, "Hg%16" SCNx64, &current_gdb_thread_rq);
1269
1270 if (current_gdb_thread_rq == 0) {
1271 target->rtos->current_threadid = ct->threadid;
1272 gdb_put_packet(connection, "OK", 2);
1273 } else {
1274 target->rtos->current_threadid =
1275 current_gdb_thread_rq;
1276 gdb_put_packet(connection, "OK", 2);
1277 }
1278 } else if (packet[1] == 'c') {
1279 sscanf(packet, "Hc%16" SCNx64, &current_gdb_thread_rq);
1280
1281 if ((current_gdb_thread_rq == 0) ||
1282 (current_gdb_thread_rq == ct->threadid)) {
1283 target->rtos->current_threadid = ct->threadid;
1284 gdb_put_packet(connection, "OK", 2);
1285 } else
1286 gdb_put_packet(connection, "E01", 3);
1287 }
1288 } else
1289 gdb_put_packet(connection, "OK", 2);
1290
1291 return ERROR_OK;
1292 }
1293
1294 static int linux_thread_packet(struct connection *connection, char const *packet,
1295 int packet_size)
1296 {
1297 int retval = ERROR_OK;
1298 struct current_thread *ct;
1299 struct target *target = get_target_from_connection(connection);
1300 struct linux_os *linux_os = (struct linux_os *)
1301 target->rtos->rtos_specific_params;
1302
1303 switch (packet[0]) {
1304 case 'T': /* Is thread alive?*/
1305
1306 linux_gdb_t_packet(connection, target, packet, packet_size);
1307 break;
1308 case 'H': /* Set current thread */
1309 /* ( 'c' for step and continue, 'g' for all other operations )*/
1310 /*LOG_INFO(" H packet received '%s'", packet);*/
1311 linux_gdb_h_packet(connection, target, packet, packet_size);
1312 break;
1313 case 'q':
1314
1315 if (strncmp(packet, "qSymbol", 7) == 0) {
1316 if (rtos_qsymbol(connection, packet, packet_size) == 1) {
1317 linux_compute_virt2phys(target,
1318 target->rtos->symbols[INIT_TASK].address);
1319 }
1320
1321 break;
1322 } else if (strncmp(packet, "qfThreadInfo", 12) == 0) {
1323 if (!linux_os->thread_list) {
1324 retval = linux_gdb_thread_packet(target,
1325 connection,
1326 packet,
1327 packet_size);
1328 break;
1329 } else {
1330 retval = linux_gdb_thread_update(target,
1331 connection,
1332 packet,
1333 packet_size);
1334 break;
1335 }
1336 } else if (strncmp(packet, "qsThreadInfo", 12) == 0) {
1337 gdb_put_packet(connection, "l", 1);
1338 break;
1339 } else if (strncmp(packet, "qThreadExtraInfo,", 17) == 0) {
1340 linux_thread_extra_info(target, connection, packet,
1341 packet_size);
1342 break;
1343 } else {
1344 retval = GDB_THREAD_PACKET_NOT_CONSUMED;
1345 break;
1346 }
1347
1348 case 'Q':
1349 /* previously response was : thread not found
1350 * gdb_put_packet(connection, "E01", 3); */
1351 retval = GDB_THREAD_PACKET_NOT_CONSUMED;
1352 break;
1353 case 'c':
1354 case 's': {
1355 if (linux_os->threads_lookup == 1) {
1356 ct = linux_os->current_threads;
1357
1358 while ((ct) && (ct->core_id) != target->coreid)
1359 ct = ct->next;
1360
1361 if ((ct) && (ct->threadid == -1)) {
1362 ct = linux_os->current_threads;
1363
1364 while ((ct) && (ct->threadid == -1))
1365 ct = ct->next;
1366 }
1367
1368 if ((ct) && (ct->threadid !=
1369 target->rtos->current_threadid)
1370 && (target->rtos->current_threadid != -1))
1371 LOG_WARNING("WARNING! current GDB thread do not match "
1372 "current thread running. "
1373 "Switch thread in GDB to threadid %d",
1374 (int)ct->threadid);
1375
1376 LOG_INFO("threads_needs_update = 1");
1377 linux_os->threads_needs_update = 1;
1378 }
1379 }
1380
1381 /* if a packet handler returned an error, exit input loop */
1382 if (retval != ERROR_OK)
1383 return retval;
1384 }
1385
1386 return retval;
1387 }
1388
1389 static int linux_os_smp_init(struct target *target)
1390 {
1391 struct target_list *head;
1392 /* keep only target->rtos */
1393 struct rtos *rtos = target->rtos;
1394 struct linux_os *os_linux =
1395 (struct linux_os *)rtos->rtos_specific_params;
1396 struct current_thread *ct;
1397 head = target->head;
1398
1399 while (head) {
1400 if (head->target->rtos != rtos) {
1401 struct linux_os *smp_os_linux =
1402 (struct linux_os *)head->target->rtos->rtos_specific_params;
1403 /* remap smp target on rtos */
1404 free(head->target->rtos);
1405 head->target->rtos = rtos;
1406 /* reuse allocated ct */
1407 ct = smp_os_linux->current_threads;
1408 ct->threadid = -1;
1409 ct->TS = 0xdeadbeef;
1410 ct->core_id = head->target->coreid;
1411 os_linux->current_threads =
1412 add_current_thread(os_linux->current_threads, ct);
1413 os_linux->nr_cpus++;
1414 free(smp_os_linux);
1415 }
1416
1417 head = head->next;
1418 }
1419
1420 return ERROR_OK;
1421 }
1422
1423 static int linux_os_create(struct target *target)
1424 {
1425 struct linux_os *os_linux = calloc(1, sizeof(struct linux_os));
1426 struct current_thread *ct = calloc(1, sizeof(struct current_thread));
1427 LOG_INFO("linux os creation\n");
1428 os_linux->init_task_addr = 0xdeadbeef;
1429 os_linux->name = "linux";
1430 os_linux->thread_list = NULL;
1431 os_linux->thread_count = 0;
1432 target->rtos->current_threadid = -1;
1433 os_linux->nr_cpus = 1;
1434 os_linux->threads_lookup = 0;
1435 os_linux->threads_needs_update = 0;
1436 os_linux->threadid_count = 1;
1437 os_linux->current_threads = NULL;
1438 target->rtos->rtos_specific_params = os_linux;
1439 ct->core_id = target->coreid;
1440 ct->threadid = -1;
1441 ct->TS = 0xdeadbeef;
1442 os_linux->current_threads =
1443 add_current_thread(os_linux->current_threads, ct);
1444 /* overload rtos thread default handler */
1445 target->rtos->gdb_thread_packet = linux_thread_packet;
1446 /* initialize a default virt 2 phys translation */
1447 os_linux->phys_mask = ~0xc0000000;
1448 os_linux->phys_base = 0x0;
1449 return JIM_OK;
1450 }
1451
1452 static char *linux_ps_command(struct target *target)
1453 {
1454 struct linux_os *linux_os = (struct linux_os *)
1455 target->rtos->rtos_specific_params;
1456 int retval = ERROR_OK;
1457 char *display;
1458
1459 if (linux_os->threads_lookup == 0)
1460 retval = linux_get_tasks(target, 1);
1461 else {
1462 if (linux_os->threads_needs_update != 0)
1463 retval = linux_task_update(target, 0);
1464 }
1465
1466 if (retval == ERROR_OK) {
1467 struct threads *temp = linux_os->thread_list;
1468 char *tmp;
1469 LOG_INFO("allocation for %d threads line",
1470 linux_os->thread_count);
1471 display = calloc((linux_os->thread_count + 2) * 80, 1);
1472
1473 if (!display)
1474 goto error;
1475
1476 tmp = display;
1477 tmp += sprintf(tmp, "PID\t\tCPU\t\tASID\t\tNAME\n");
1478 tmp += sprintf(tmp, "---\t\t---\t\t----\t\t----\n");
1479
1480 while (temp) {
1481 if (temp->status) {
1482 if (temp->context)
1483 tmp +=
1484 sprintf(tmp,
1485 "%" PRIu32 "\t\t%" PRIu32 "\t\t%" PRIx32 "\t\t%s\n",
1486 temp->pid, temp->oncpu,
1487 temp->asid, temp->name);
1488 else
1489 tmp +=
1490 sprintf(tmp,
1491 "%" PRIu32 "\t\t%" PRIu32 "\t\t%" PRIx32 "\t\t%s\n",
1492 temp->pid, temp->oncpu,
1493 temp->asid, temp->name);
1494 }
1495
1496 temp = temp->next;
1497 }
1498
1499 return display;
1500 }
1501
1502 error:
1503 display = calloc(40, 1);
1504 sprintf(display, "linux_ps_command failed\n");
1505 return display;
1506 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)