From 0a4b27ec4bcb303547af68490f007d65a00bff02 Mon Sep 17 00:00:00 2001 From: Michel JAOUEN Date: Wed, 11 Jan 2012 10:59:29 +0100 Subject: [PATCH] rtos : linux awareness MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Change-Id: I41294ccaa4a3cd253919c8b1b558205903bcb695 Signed-off-by: Michel JAOUEN Reviewed-on: http://openocd.zylin.com/348 Tested-by: jenkins Reviewed-by: Heythem Bouhaja Reviewed-by: Øyvind Harboe --- src/rtos/Makefile.am | 2 +- src/rtos/linux.c | 1602 +++++++++++++++++++++++++++++++++++++++ src/rtos/linux_header.h | 32 + src/rtos/rtos.c | 2 + 4 files changed, 1637 insertions(+), 1 deletion(-) create mode 100644 src/rtos/linux.c create mode 100644 src/rtos/linux_header.h diff --git a/src/rtos/Makefile.am b/src/rtos/Makefile.am index 6f17a5f939..e341caf0ab 100644 --- a/src/rtos/Makefile.am +++ b/src/rtos/Makefile.am @@ -23,7 +23,7 @@ include $(top_srcdir)/common.mk METASOURCES = AUTO noinst_LTLIBRARIES = librtos.la noinst_HEADERS = rtos.h rtos_standard_stackings.h rtos_ecos_stackings.h -librtos_la_SOURCES = rtos.c rtos_standard_stackings.c rtos_ecos_stackings.c FreeRTOS.c ThreadX.c eCos.c +librtos_la_SOURCES = rtos.c rtos_standard_stackings.c rtos_ecos_stackings.c FreeRTOS.c ThreadX.c eCos.c linux.c librtos_la_CFLAGS = if IS_MINGW diff --git a/src/rtos/linux.c b/src/rtos/linux.c new file mode 100644 index 0000000000..2ea3312225 --- /dev/null +++ b/src/rtos/linux.c @@ -0,0 +1,1602 @@ +/*************************************************************************** + * Copyright (C) 2011 by STEricsson * + * Heythem Bouhaja heythem.bouhaja@stericsson.com : creation * + * Michel JAOUEN michel.jaouen@stericsson.com : adaptation to rtos * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "target/target.h" +#include "target/target_type.h" +#include "helper/log.h" +#include "rtos.h" +#include "helper/log.h" +#include "rtos_standard_stackings.h" +#include +#include "server/gdb_server.h" + +#define LINUX_USER_KERNEL_BORDER 0xc0000000 +#include "linux_header.h" +#define PHYS +#define MAX_THREADS 200 +/* specific task */ +struct linux_os { + char *name; + uint32_t init_task_addr; + int thread_count; + int threadid_count; + int preupdtate_threadid_count; + int nr_cpus; + int threads_lookup; + int threads_needs_update; + struct current_thread *current_threads; + struct threads *thread_list; + /* virt2phys parameter */ + uint32_t phys_mask; + uint32_t phys_base; +}; + +struct current_thread { + int64_t threadid; + int32_t core_id; +#ifdef PID_CHECK + uint32_t pid; +#endif + uint32_t TS; + struct current_thread *next; +}; + +struct threads { + char name[17]; + uint32_t base_addr; /* address to read magic */ + uint32_t state; /* magic value : filled only at creation */ + uint32_t pid; /* linux pid : id for identifying a thread */ + uint32_t oncpu; /* content cpu number in current thread */ + uint32_t asid; /* filled only at creation */ + int64_t threadid; + int status; /* dead = 1 alive = 2 current = 3 alive and current */ + /* value that should not change during the live of a thread ? */ + uint32_t thread_info_addr; /* contain latest thread_info_addr computed */ + /* retrieve from thread_info */ + struct cpu_context *context; + struct threads *next; +}; + +struct cpu_context { + uint32_t R4; + uint32_t R5; + uint32_t R6; + uint32_t R7; + uint32_t R8; + uint32_t R9; + uint32_t IP; + uint32_t FP; + uint32_t SP; + uint32_t PC; + uint32_t preempt_count; +}; +struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr, + uint32_t *info_addr); +static int insert_into_threadlist(struct target *target, struct threads *t); + +static int linux_os_create(struct target *target); + +static int linux_os_dummy_update(struct rtos *rtos) +{ + /* update is done only when thread request come */ + /* too many thread to do it on each stop */ + return 0; +} + +static int linux_compute_virt2phys(struct target *target, uint32_t address) +{ + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + uint32_t pa = 0; + int retval = target->type->virt2phys(target, address, &pa); + if (retval != ERROR_OK) { + LOG_ERROR("Cannot compute linux virt2phys translation"); + /* fixes default address */ + linux_os->phys_base = 0; + return ERROR_FAIL; + } + + linux_os->init_task_addr = address; + address = address & linux_os->phys_mask; + linux_os->phys_base = pa - address; + return ERROR_OK; +} + +static int linux_read_memory(struct target *target, + uint32_t address, uint32_t size, uint32_t count, + uint8_t *buffer) +{ +#ifdef PHYS + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + uint32_t pa = (address & linux_os->phys_mask) + linux_os->phys_base; +#endif + if (address < 0xc000000) { + LOG_ERROR("linux awareness : address in user space"); + return ERROR_FAIL; + } +#ifdef PHYS + target->type->read_phys_memory(target, pa, size, count, buffer); +#endif + target->type->read_memory(target, address, size, count, buffer); + return ERROR_OK; +} + +static char *reg_converter(char *buffer, void *reg, int size) +{ + int i; + + for (i = 0; i < size; i++) + buffer += sprintf(buffer, "%02x", ((uint8_t *) reg)[i]); + + return buffer; +} + +int fill_buffer(struct target *target, uint32_t addr, uint8_t * buffer) +{ + + if ((addr & 0xfffffffc) != addr) + LOG_INFO("unaligned address %x!!", addr); + + int retval = linux_read_memory(target, addr, 4, 1, buffer); + return retval; + +} + +uint32_t get_buffer(struct target *target, const uint8_t *buffer) +{ + uint32_t value = 0; + const uint8_t *value_ptr = buffer; + value = target_buffer_get_u32(target, value_ptr); + return value; +} + +static int linux_os_thread_reg_list(struct rtos *rtos, + int64_t thread_id, char **hex_reg_list) +{ + struct target *target = rtos->target; + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + int i = 0; + struct current_thread *tmp = linux_os->current_threads; + struct current_thread *next; + char *hex_string; + int found = 0; + int retval; + /* check if a current thread is requested */ + next = tmp; + + do { + if (next->threadid == thread_id) + found = 1; + else + next = next->next; + } while ((found == 0) && (next != tmp) && (next != NULL)); + + if (found == 1) { + /* search target to perfom the access */ + struct reg **reg_list; + int reg_list_size, reg_packet_size = 0; + struct target_list *head; + head = target->head; + found = 0; + do { + if (head->target->coreid == next->core_id) { + + target = head->target; + found = 1; + } else + head = head->next; + + } while ((head != (struct target_list *)NULL) && (found == 0)); + + if (found == 0) { + LOG_ERROR + ("current thread %" PRIx64": no target to perform access of core id %x", + thread_id, next->core_id); + return ERROR_FAIL; + } + + /*LOG_INFO("thread %lx current on core %x",thread_id, + * target->coreid);*/ + retval = + target_get_gdb_reg_list(target, ®_list, ®_list_size); + + if (retval != ERROR_OK) + return retval; + + for (i = 0; i < reg_list_size; i++) + reg_packet_size += reg_list[i]->size; + + *hex_reg_list = malloc(DIV_ROUND_UP(reg_packet_size, 8) * 2); + + hex_string = *hex_reg_list; + + for (i = 0; i < reg_list_size; i++) { + if (!reg_list[i]->valid) + reg_list[i]->type->get(reg_list[i]); + + hex_string = reg_converter(hex_string, + reg_list[i]->value, + (reg_list[i]->size) / 8); + } + + free(reg_list); + + } else { + struct threads *temp = linux_os->thread_list; + *hex_reg_list = (char *)calloc(1, 500 * sizeof(char)); + hex_string = *hex_reg_list; + + for (i = 0; i < 16; i++) + hex_string += sprintf(hex_string, "%02x", 0); + + while ((temp != NULL) && + (temp->threadid != target->rtos->current_threadid)) + temp = temp->next; + + if (temp != NULL) { + if (temp->context == NULL) + temp->context = cpu_context_read(target, + temp-> + base_addr, + &temp-> + thread_info_addr); + + hex_string = + reg_converter(hex_string, &temp->context->R4, 4); + hex_string = + reg_converter(hex_string, &temp->context->R5, 4); + hex_string = + reg_converter(hex_string, &temp->context->R6, 4); + hex_string = + reg_converter(hex_string, &temp->context->R7, 4); + hex_string = + reg_converter(hex_string, &temp->context->R8, 4); + hex_string = + reg_converter(hex_string, &temp->context->R9, 4); + + for (i = 0; i < 4; i++) /*R10 = 0x0 */ + hex_string += sprintf(hex_string, "%02x", 0); + + hex_string = + reg_converter(hex_string, &temp->context->FP, 4); + hex_string = + reg_converter(hex_string, &temp->context->IP, 4); + hex_string = + reg_converter(hex_string, &temp->context->SP, 4); + + for (i = 0; i < 4; i++) + hex_string += sprintf(hex_string, "%02x", 0); + + hex_string = + reg_converter(hex_string, &temp->context->PC, 4); + + for (i = 0; i < 100; i++) { /*100 */ + hex_string += sprintf(hex_string, "%02x", 0); + } + + uint32_t cpsr = 0x00000000; + hex_string = reg_converter(hex_string, &cpsr, 4); + } + } + return ERROR_OK; +} + +static int linux_os_detect(struct target *target) +{ + LOG_INFO("should no be called"); + return 0; +} + +static int linux_os_smp_init(struct target *target); +static int linux_os_clean(struct target *target); +#define INIT_TASK 0 +static char *linux_symbol_list[] = { + "init_task", + NULL +}; + +static int linux_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) +{ + unsigned int i; + *symbol_list = (symbol_table_elem_t *) + malloc(sizeof(symbol_table_elem_t) / sizeof(char *)); + + for (i = 0; i < sizeof(linux_symbol_list) / sizeof(char *); i++) + (*symbol_list)[i].symbol_name = linux_symbol_list[i]; + + return 0; +} + +static char *linux_ps_command(struct target *target); + +const struct rtos_type Linux_os = { + .name = "linux", + .detect_rtos = linux_os_detect, + .create = linux_os_create, + .smp_init = linux_os_smp_init, + .update_threads = linux_os_dummy_update, + .get_thread_reg_list = linux_os_thread_reg_list, + .get_symbol_list_to_lookup = linux_get_symbol_list_to_lookup, + .clean = linux_os_clean, + .ps_command = linux_ps_command, +}; + +static int linux_thread_packet(struct connection *connection, char *packet, + int packet_size); +static void linux_identify_current_threads(struct target *target); + +#ifdef PID_CHECK +int fill_task_pid(struct target *target, struct threads *t) +{ + uint32_t pid_addr = t->base_addr + PID; + uint8_t buffer[4]; + int retval = fill_buffer(target, pid_addr, buffer); + + if (retval == ERROR_OK) { + uint32_t val = get_buffer(target, buffer); + t->pid = val; + } else + LOG_ERROR("fill_task_pid: unable to read memory"); + + return retval; +} +#endif + +int fill_task(struct target *target, struct threads *t) +{ + + int retval; + uint32_t pid_addr = t->base_addr + PID; + uint32_t mem_addr = t->base_addr + MEM; + uint32_t on_cpu = t->base_addr + ONCPU; + uint8_t *buffer = calloc(1, 4); + retval = fill_buffer(target, t->base_addr, buffer); + + if (retval == ERROR_OK) { + uint32_t val = get_buffer(target, buffer); + t->state = val; + } else + LOG_ERROR("fill_task: unable to read memory"); + + retval = fill_buffer(target, pid_addr, buffer); + + if (retval == ERROR_OK) { + uint32_t val = get_buffer(target, buffer); + t->pid = val; + } else + LOG_ERROR("fill task: unable to read memory"); + + retval = fill_buffer(target, on_cpu, buffer); + + if (retval == ERROR_OK) { + uint32_t val = get_buffer(target, buffer); + t->oncpu = val; + } else + LOG_ERROR("fill task: unable to read memory"); + + retval = fill_buffer(target, mem_addr, buffer); + + if (retval == ERROR_OK) { + uint32_t val = get_buffer(target, buffer); + + if (val != 0) { + uint32_t asid_addr = val + MM_CTX; + retval = fill_buffer(target, asid_addr, buffer); + + if (retval == ERROR_OK) { + val = get_buffer(target, buffer); + t->asid = val; + } else + LOG_ERROR + ("fill task: unable to read memory -- ASID"); + } else { + t->asid = 0; + } + } else + LOG_ERROR("fill task: unable to read memory"); + + return retval; +} + +int get_name(struct target *target, struct threads *t) +{ + int retval; + uint32_t full_name[4]; + uint32_t comm = t->base_addr + COMM; + int i; + + for (i = 0; i < 17; i++) + t->name[i] = 0; + + retval = linux_read_memory(target, comm, 4, 4, (uint8_t *) full_name); + + if (retval != ERROR_OK) { + LOG_ERROR("get_name: unable to read memory\n"); + return ERROR_FAIL; + } + + uint32_t raw_name = target_buffer_get_u32(target, + (const uint8_t *) + &full_name[0]); + t->name[3] = raw_name >> 24; + t->name[2] = raw_name >> 16; + t->name[1] = raw_name >> 8; + t->name[0] = raw_name; + raw_name = + target_buffer_get_u32(target, (const uint8_t *)&full_name[1]); + t->name[7] = raw_name >> 24; + t->name[6] = raw_name >> 16; + t->name[5] = raw_name >> 8; + t->name[4] = raw_name; + raw_name = + target_buffer_get_u32(target, (const uint8_t *)&full_name[2]); + t->name[11] = raw_name >> 24; + t->name[10] = raw_name >> 16; + t->name[9] = raw_name >> 8; + t->name[8] = raw_name; + raw_name = + target_buffer_get_u32(target, (const uint8_t *)&full_name[3]); + t->name[15] = raw_name >> 24; + t->name[14] = raw_name >> 16; + t->name[13] = raw_name >> 8; + t->name[12] = raw_name; + return ERROR_OK; + +} + +int get_current(struct target *target, int create) +{ + struct target_list *head; + head = target->head; + uint8_t *buf; + uint32_t val; + uint32_t ti_addr; + uint8_t *buffer = calloc(1, 4); + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + struct current_thread *ctt = linux_os->current_threads; + + /* invalid current threads content */ + while (ctt != NULL) { + ctt->threadid = -1; + ctt->TS = 0xdeadbeef; + ctt = ctt->next; + } + + while (head != (struct target_list *)NULL) { + struct reg **reg_list; + int reg_list_size; + int retval; + + if (target_get_gdb_reg_list(head->target, ®_list, + ®_list_size) != + ERROR_OK) + return ERROR_TARGET_FAILURE; + + if (!reg_list[13]->valid) + reg_list[13]->type->get(reg_list[13]); + + buf = reg_list[13]->value; + val = get_buffer(target, buf); + ti_addr = (val & 0xffffe000); + uint32_t TS_addr = ti_addr + 0xc; + retval = fill_buffer(target, TS_addr, buffer); + + if (retval == ERROR_OK) { + uint32_t TS = get_buffer(target, buffer); + uint32_t cpu, on_cpu = TS + ONCPU; + retval = fill_buffer(target, on_cpu, buffer); + + if (retval == ERROR_OK) { + /*uint32_t cpu = get_buffer(target, buffer);*/ + struct current_thread *ct = + linux_os->current_threads; + cpu = head->target->coreid; + + while ((ct != NULL) + && (ct->core_id != (int32_t) cpu)) { + ct = ct->next; + } + + if ((ct != NULL) && (ct->TS == 0xdeadbeef)) + ct->TS = TS; + else + LOG_ERROR + ("error in linux current thread update"); + + if (create) { + struct threads *t; + t = calloc(1, sizeof(struct threads)); + t->base_addr = ct->TS; + fill_task(target, t); + get_name(target, t); + t->oncpu = cpu; + insert_into_threadlist(target, t); + t->status = 3; + t->thread_info_addr = 0xdeadbeef; + ct->threadid = t->threadid; + linux_os->thread_count++; +#ifdef PID_CHECK + ct->pid = t->pid; +#endif + /*LOG_INFO("Creation of current thread %s",t->name);*/ + } + } + } + + free(reg_list); + head = head->next; + } + + return ERROR_OK; +} + +struct cpu_context *cpu_context_read(struct target *target, uint32_t base_addr, + uint32_t *thread_info_addr_old) +{ + struct cpu_context *context = calloc(1, sizeof(struct cpu_context)); + uint32_t preempt_count_addr = 0; + uint32_t registers[10]; + uint8_t *buffer = calloc(1, 4); + uint32_t stack = base_addr + QAT; + uint32_t thread_info_addr = 0; + uint32_t thread_info_addr_update = 0; + int retval = ERROR_FAIL; + context->R4 = 0xdeadbeef; + context->R5 = 0xdeadbeef; + context->R6 = 0xdeadbeef; + context->R7 = 0xdeadbeef; + context->R8 = 0xdeadbeef; + context->R9 = 0xdeadbeef; + context->IP = 0xdeadbeef; + context->FP = 0xdeadbeef; + context->SP = 0xdeadbeef; + context->PC = 0xdeadbeef; +retry: + + if (*thread_info_addr_old == 0xdeadbeef) { + retval = fill_buffer(target, stack, buffer); + + if (retval == ERROR_OK) + thread_info_addr = get_buffer(target, buffer); + else + LOG_ERROR("cpu_context: unable to read memory"); + + thread_info_addr_update = thread_info_addr; + } else + thread_info_addr = *thread_info_addr_old; + + preempt_count_addr = thread_info_addr + PREEMPT; + retval = fill_buffer(target, preempt_count_addr, buffer); + + if (retval == ERROR_OK) + context->preempt_count = get_buffer(target, buffer); + else { + if (*thread_info_addr_old != 0xdeadbeef) { + LOG_ERROR + ("cpu_context: cannot read at thread_info_addr"); + + if (*thread_info_addr_old < LINUX_USER_KERNEL_BORDER) + LOG_INFO + ("cpu_context : thread_info_addr in userspace!!!"); + + *thread_info_addr_old = 0xdeadbeef; + goto retry; + } + + LOG_ERROR("cpu_context: unable to read memory"); + } + + thread_info_addr += CPU_CONT; + + retval = linux_read_memory(target, thread_info_addr, 4, 10, + (uint8_t *) registers); + + if (retval != ERROR_OK) { + LOG_ERROR("cpu_context: unable to read memory\n"); + return context; + } + + context->R4 = + target_buffer_get_u32(target, (const uint8_t *)®isters[0]); + context->R5 = + target_buffer_get_u32(target, (const uint8_t *)®isters[1]); + context->R6 = + target_buffer_get_u32(target, (const uint8_t *)®isters[2]); + context->R7 = + target_buffer_get_u32(target, (const uint8_t *)®isters[3]); + context->R8 = + target_buffer_get_u32(target, (const uint8_t *)®isters[4]); + context->R9 = + target_buffer_get_u32(target, (const uint8_t *)®isters[5]); + context->IP = + target_buffer_get_u32(target, (const uint8_t *)®isters[6]); + context->FP = + target_buffer_get_u32(target, (const uint8_t *)®isters[7]); + context->SP = + target_buffer_get_u32(target, (const uint8_t *)®isters[8]); + context->PC = + target_buffer_get_u32(target, (const uint8_t *)®isters[9]); + + if (*thread_info_addr_old == 0xdeadbeef) + *thread_info_addr_old = thread_info_addr_update; + + return context; +} + +uint32_t next_task(struct target *target, struct threads *t) +{ + uint8_t *buffer = calloc(1, 4); + uint32_t next_addr = t->base_addr + NEXT; + int retval = fill_buffer(target, next_addr, buffer); + + if (retval == ERROR_OK) { + uint32_t val = get_buffer(target, buffer); + val = val - NEXT; + return val; + free(buffer); + } else + LOG_ERROR("next task: unable to read memory"); + + return 0; +} + +struct current_thread *add_current_thread(struct current_thread *currents, + struct current_thread *ct) +{ + ct->next = NULL; + + if (currents == NULL) { + currents = ct; + return currents; + } else { + struct current_thread *temp = currents; + + while (temp->next != NULL) + temp = temp->next; + + temp->next = ct; + return currents; + } +} + +struct threads *liste_del_task(struct threads *task_list, struct threads **t, + struct threads *prev) +{ + LOG_INFO("del task %" PRId64, (*t)->threadid); + prev->next = (*t)->next; + + if (prev == task_list) + task_list = prev; + + /* free content of threads */ + if ((*t)->context) + free((*t)->context); + + free(*t); + *t = prev; + return task_list; +} + +struct threads *liste_add_task(struct threads *task_list, struct threads *t, + struct threads **last) +{ + t->next = NULL; + + if (*last == NULL) + if (task_list == NULL) { + task_list = t; + return task_list; + } else { + struct threads *temp = task_list; + + while (temp->next != NULL) + temp = temp->next; + + temp->next = t; + *last = t; + return task_list; + } else { + (*last)->next = t; + *last = t; + return task_list; + } +} + +#ifdef PID_CHECK +static int current_pid(struct linux_os *linux_os, uint32_t pid) +#else +static int current_base_addr(struct linux_os *linux_os, uint32_t base_addr) +#endif +{ + struct current_thread *ct = linux_os->current_threads; +#ifdef PID_CHECK + + while ((ct != NULL) && (ct->pid != pid)) +#else + while ((ct != NULL) && (ct->TS != base_addr)) +#endif + ct = ct->next; +#ifdef PID_CHECK + if ((ct != NULL) && (ct->pid == pid)) +#else + if ((ct != NULL) && (ct->TS == base_addr)) +#endif + return 1; + + return 0; +} + +int linux_get_tasks(struct target *target, int context) +{ + int loop = 0; + int retval = 0; + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + linux_os->thread_list = NULL; + linux_os->thread_count = 0; + + if (linux_os->init_task_addr == 0xdeadbeef) { + LOG_INFO("no init symbol\n"); + return ERROR_FAIL; + } + + int64_t start = timeval_ms(); + + struct threads *t = calloc(1, sizeof(struct threads)); + struct threads *last = NULL; + t->base_addr = linux_os->init_task_addr; + /* retrieve the thread id , currently running in the different smp core */ + retval = get_current(target, 1); + + while (((t->base_addr != linux_os->init_task_addr) && + (t->base_addr != 0)) + || (loop == 0) + ) { + loop++; + retval = fill_task(target, t); + retval = get_name(target, t); + + if (loop > MAX_THREADS) { + LOG_INFO("more than %d threads !!", MAX_THREADS); + return ERROR_FAIL; + } + + if (retval != ERROR_OK) { + free(t); + return ERROR_FAIL; + } + + /* check that this thread is not one the current threads already + * created */ +#ifdef PID_CHECK + + if (!current_pid(linux_os, t->pid)) { +#else + if (!current_base_addr(linux_os, t->base_addr)) { +#endif + t->threadid = linux_os->threadid_count; + t->status = 1; + linux_os->threadid_count++; + + linux_os->thread_list = + liste_add_task(linux_os->thread_list, t, &last); + /* no interest to fill the context if it is a current thread. */ + linux_os->thread_count++; + t->thread_info_addr = 0xdeadbeef; + + if (context) + t->context = + cpu_context_read(target, t->base_addr, + &t->thread_info_addr); + } else { + /*LOG_INFO("thread %s is a current thread already created",t->name); */ + free(t); + } + + uint32_t base_addr = next_task(target, t); + t = calloc(1, sizeof(struct threads)); + t->base_addr = base_addr; + } + + linux_os->threads_lookup = 1; + linux_os->threads_needs_update = 0; + linux_os->preupdtate_threadid_count = linux_os->threadid_count - 1; + /* check that all current threads have been identified */ + + LOG_INFO("complete time %" PRId64", thread mean %" PRId64"\n", + (timeval_ms() - start), + (timeval_ms() - start) / linux_os->threadid_count); + + LOG_INFO("threadid count %d", linux_os->threadid_count); + + return ERROR_OK; +} + +static int clean_threadlist(struct target *target) +{ + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + struct threads *old, *temp = linux_os->thread_list; + + while (temp != NULL) { + old = temp; + + if (temp->context) + free(temp->context); + + temp = temp->next; + free(old); + } + + return ERROR_OK; +} + +static int linux_os_clean(struct target *target) +{ + + struct linux_os *os_linux = (struct linux_os *) + target->rtos->rtos_specific_params; + clean_threadlist(target); + os_linux->init_task_addr = 0xdeadbeef; + os_linux->name = "linux"; + os_linux->thread_list = NULL; + os_linux->thread_count = 0; + os_linux->nr_cpus = 0; + os_linux->threads_lookup = 0; + os_linux->threads_needs_update = 0; + os_linux->threadid_count = 1; + return ERROR_OK; +} + +static int insert_into_threadlist(struct target *target, struct threads *t) +{ + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + struct threads *temp = linux_os->thread_list; + t->threadid = linux_os->threadid_count; + linux_os->threadid_count++; + t->status = 1; + t->next = NULL; + + if (temp == NULL) + linux_os->thread_list = t; + else { + while (temp->next != NULL) + temp = temp->next; + + t->next = NULL; + temp->next = t; + } + + return ERROR_OK; +} + +static void linux_identify_current_threads(struct target *target) +{ + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + struct threads *thread_list = linux_os->thread_list; + struct current_thread *ct = linux_os->current_threads; + struct threads *t = NULL; + + while ((ct != NULL)) { + if (ct->threadid == -1) { + + /* un-identified thread */ + int found = 0; + t = calloc(1, sizeof(struct threads)); + t->base_addr = ct->TS; +#ifdef PID_CHECK + + if (fill_task_pid(target, t) != ERROR_OK) { +error_handling: + free(t); + LOG_ERROR + ("linux identify_current_threads: unable to read pid"); + return; + } +#endif + + /* search in the list of threads if pid + already present */ + while ((thread_list != NULL) && (found == 0)) { +#ifdef PID_CHECK + if (thread_list->pid == t->pid) { +#else + if (thread_list->base_addr == t->base_addr) { +#endif + free(t); + t = thread_list; + found = 1; + } + thread_list = thread_list->next; + } + + if (!found) { + /* it is a new thread */ + if (fill_task(target, t) != ERROR_OK) + goto error_handling; + + get_name(target, t); + insert_into_threadlist(target, t); + t->thread_info_addr = 0xdeadbeef; + } + + t->status = 3; + ct->threadid = t->threadid; +#ifdef PID_CHECK + ct->pid = t->pid; +#endif + linux_os->thread_count++; +#if 0 + if (found == 0) + LOG_INFO("current thread core %x identified %s", + ct->core_id, t->name); + else + LOG_INFO("current thread core %x, reused %s", + ct->core_id, t->name); +#endif + } +#if 0 + else { + struct threads tmp; + tmp.base_addr = ct->TS; + get_name(target, &tmp); + LOG_INFO("current thread core %x , already identified %s !!!", + ct->core_id, tmp.name); + } +#endif + ct = ct->next; + } + + return; +#ifndef PID_CHECK +error_handling: + free(t); + LOG_ERROR("unable toread pid"); + return; + +#endif + } + +static int linux_task_update(struct target *target, int context) +{ + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + struct threads *thread_list = linux_os->thread_list; + int retval; + int loop = 0; + linux_os->thread_count = 0; + + /*thread_list = thread_list->next; skip init_task*/ + while (thread_list != NULL) { + thread_list->status = 0; /*setting all tasks to dead state*/ + + if (thread_list->context) { + free(thread_list->context); + thread_list->context = NULL; + } + + thread_list = thread_list->next; + + } + + int found = 0; + + if (linux_os->init_task_addr == 0xdeadbeef) { + LOG_INFO("no init symbol\n"); + return ERROR_FAIL; + } + int64_t start = timeval_ms(); + struct threads *t = calloc(1, sizeof(struct threads)); + uint32_t previous = 0xdeadbeef; + t->base_addr = linux_os->init_task_addr; + retval = get_current(target, 0); + /*check that all current threads have been identified */ + linux_identify_current_threads(target); + + while (((t->base_addr != linux_os->init_task_addr) && + (t->base_addr != previous)) || (loop == 0)) { + /* for avoiding any permanent loop for any reason possibly due to + * target */ + loop++; + previous = t->base_addr; + /* read only pid */ +#ifdef PID_CHECK + retval = fill_task_pid(target, t); +#endif + + if (retval != ERROR_OK) { + free(t); + return ERROR_FAIL; + } + + thread_list = linux_os->thread_list; + + while (thread_list != NULL) { +#ifdef PID_CHECK + + if (t->pid == thread_list->pid) { +#else + if (t->base_addr == thread_list->base_addr) { +#endif + + if (!thread_list->status) { +#ifdef PID_CHECK + + if (t->base_addr != + thread_list->base_addr) { + LOG_INFO + ("thread base_addr has changed !!"); + } +#endif + /* this is not a current thread */ + thread_list->base_addr = t->base_addr; + thread_list->status = 1; + + /* we don 't update this field any more */ + + /*thread_list->state = t->state; + thread_list->oncpu = t->oncpu; + thread_list->asid = t->asid; + */ + if (context) + thread_list->context = + cpu_context_read(target, + thread_list-> + base_addr, + &thread_list-> + thread_info_addr); + } else { + /* it is a current thread no need to read context */ + } + + linux_os->thread_count++; + found = 1; + break; + } else { + found = 0; + thread_list = thread_list->next; + } + } + + if (found == 0) { + uint32_t base_addr; + fill_task(target, t); + get_name(target, t); + retval = insert_into_threadlist(target, t); + t->thread_info_addr = 0xdeadbeef; + + if (context) + t->context = + cpu_context_read(target, t->base_addr, + &t->thread_info_addr); + + base_addr = next_task(target, t); + t = calloc(1, sizeof(struct threads)); + t->base_addr = base_addr; + linux_os->thread_count++; + } else + t->base_addr = next_task(target, t); + + } + + LOG_INFO("update thread done %" PRId64", mean%" PRId64"\n", + (timeval_ms() - start), (timeval_ms() - start) / loop); + free(t); + linux_os->threads_needs_update = 0; + return ERROR_OK; +} + +int linux_gdb_thread_packet(struct target *target, + struct connection *connection, char *packet, + int packet_size) +{ + + int retval; + struct linux_os *linux_os = + (struct linux_os *)target->rtos->rtos_specific_params; + + if (linux_os->init_task_addr == 0xdeadbeef) { + /* it has not been initialized */ + LOG_INFO("received thread request without init task address"); + gdb_put_packet(connection, "l", 1); + return ERROR_OK; + } + + retval = linux_get_tasks(target, 1); + + if (retval != ERROR_OK) + return ERROR_TARGET_FAILURE; + + char *out_str = (char *)calloc(1, 350 * sizeof(int64_t)); + char *tmp_str = out_str; + tmp_str += sprintf(tmp_str, "m"); + struct threads *temp = linux_os->thread_list; + tmp_str += sprintf(tmp_str, "%016" PRIx64, temp->threadid); + temp = temp->next; + + while (temp != NULL) { + tmp_str += sprintf(tmp_str, ","); + tmp_str += sprintf(tmp_str, "%016" PRIx64, temp->threadid); + temp = temp->next; + } + + gdb_put_packet(connection, out_str, strlen(out_str)); + return ERROR_OK; +} + +int linux_gdb_thread_update(struct target *target, + struct connection *connection, char *packet, + int packet_size) +{ + int found = 0; + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + struct threads *temp = linux_os->thread_list; + + while (temp != NULL) { + if (temp->threadid == linux_os->preupdtate_threadid_count + 1) { + /*LOG_INFO("FOUND");*/ + found = 1; + break; + } else + temp = temp->next; + } + + if (found == 1) { + /*LOG_INFO("INTO GDB THREAD UPDATE FOUNDING START TASK");*/ + char *out_strr = (char *)calloc(1, 350 * sizeof(int64_t)); + char *tmp_strr = out_strr; + tmp_strr += sprintf(tmp_strr, "m"); + /*LOG_INFO("CHAR MALLOC & M DONE");*/ + tmp_strr += sprintf(tmp_strr, "%016" PRIx64, temp->threadid); + + temp = temp->next; + + while (temp != NULL) { + /*LOG_INFO("INTO GDB THREAD UPDATE WHILE");*/ + tmp_strr += sprintf(tmp_strr, ","); + tmp_strr += + sprintf(tmp_strr, "%016" PRIx64, temp->threadid); + temp = temp->next; + } + + /*tmp_str[0] = 0;*/ + gdb_put_packet(connection, out_strr, strlen(out_strr)); + linux_os->preupdtate_threadid_count = + linux_os->threadid_count - 1; + free(out_strr); + } else + gdb_put_packet(connection, "l", 1); + + return ERROR_OK; +} + +int linux_thread_extra_info(struct target *target, + struct connection *connection, char *packet, + int packet_size) +{ + int64_t threadid = 0; + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + sscanf(packet, "qThreadExtraInfo,%" SCNx64, &threadid); + /*LOG_INFO("lookup extra info for thread %" SCNx64, threadid);*/ + struct threads *temp = linux_os->thread_list; + + while (temp != NULL) { + if (temp->threadid == threadid) { + char *pid = " PID: "; + char *pid_current = "*PID: "; + char *name = "NAME: "; + int str_size = strlen(pid) + strlen(name); + char *tmp_str = (char *)calloc(1, str_size + 50); + char *tmp_str_ptr = tmp_str; + + /* discriminate cuurent task */ + if (temp->status == 3) + tmp_str_ptr += sprintf(tmp_str_ptr, "%s", + pid_current); + else + tmp_str_ptr += sprintf(tmp_str_ptr, "%s", pid); + + tmp_str_ptr += + sprintf(tmp_str_ptr, "%d", (int)temp->pid); + tmp_str_ptr += sprintf(tmp_str_ptr, "%s", " | "); + tmp_str_ptr += sprintf(tmp_str_ptr, "%s", name); + tmp_str_ptr += sprintf(tmp_str_ptr, "%s", temp->name); + char *hex_str = + (char *)calloc(1, strlen(tmp_str) * 2 + 1); + str_to_hex(hex_str, tmp_str); + gdb_put_packet(connection, hex_str, strlen(hex_str)); + free(hex_str); + free(tmp_str); + return ERROR_OK; + } + + temp = temp->next; + } + + LOG_INFO("thread not found"); + return ERROR_OK; +} + +int linux_gdb_T_packet(struct connection *connection, + struct target *target, char *packet, int packet_size) +{ + int64_t threadid; + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + int retval = ERROR_OK; + sscanf(packet, "T%" SCNx64, &threadid); + + if (linux_os->threads_needs_update == 0) { + struct threads *temp = linux_os->thread_list; + struct threads *prev = linux_os->thread_list; + + while (temp != NULL) { + if (temp->threadid == threadid) { + if (temp->status != 0) { + gdb_put_packet(connection, "OK", 2); + return ERROR_OK; + } else { + /* delete item in the list */ + linux_os->thread_list = + liste_del_task(linux_os-> + thread_list, &temp, + prev); + linux_os->thread_count--; + gdb_put_packet(connection, "E01", 3); + return ERROR_OK; + } + } + + /* for deletion */ + prev = temp; + temp = temp->next; + } + + LOG_INFO("gdb requested status on non existing thread"); + gdb_put_packet(connection, "E01", 3); + return ERROR_OK; + + } else { + retval = linux_task_update(target, 1); + struct threads *temp = linux_os->thread_list; + + while (temp != NULL) { + if (temp->threadid == threadid) { + if (temp->status == 1) { + gdb_put_packet(connection, "OK", 2); + return ERROR_OK; + } else { + gdb_put_packet(connection, "E01", 3); + return ERROR_OK; + } + } + + temp = temp->next; + } + } + + return retval; +} + +int linux_gdb_h_packet(struct connection *connection, + struct target *target, char *packet, int packet_size) +{ + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + struct current_thread *ct = linux_os->current_threads; + + /* select to display the current thread of the selected target */ + while ((ct != NULL) && (ct->core_id != target->coreid)) + ct = ct->next; + + int64_t current_gdb_thread_rq; + + if (linux_os->threads_lookup == 1) { + if ((ct != NULL) && (ct->threadid == -1)) { + ct = linux_os->current_threads; + + while ((ct != NULL) && (ct->threadid == -1)) + ct = ct->next; + } + + if (ct == NULL) { + /* no current thread can be identified */ + /* any way with smp */ + LOG_INFO("no current thread identified"); + /* attempt to display the name of the 2 threads identified with + * get_current */ + struct threads t; + ct = linux_os->current_threads; + + while ((ct != NULL) && (ct->threadid == -1)) { + t.base_addr = ct->TS; + get_name(target, &t); + LOG_INFO("name of unidentified thread %s", + t.name); + ct = ct->next; + } + + gdb_put_packet(connection, "OK", 2); + return ERROR_OK; + } + + if (packet[1] == 'g') { + sscanf(packet, "Hg%16" SCNx64, ¤t_gdb_thread_rq); + + if (current_gdb_thread_rq == 0) { + target->rtos->current_threadid = ct->threadid; + gdb_put_packet(connection, "OK", 2); + } else { + target->rtos->current_threadid = + current_gdb_thread_rq; + gdb_put_packet(connection, "OK", 2); + } + } else if (packet[1] == 'c') { + sscanf(packet, "Hc%16" SCNx64, ¤t_gdb_thread_rq); + + if ((current_gdb_thread_rq == 0) || + (current_gdb_thread_rq == ct->threadid)) { + target->rtos->current_threadid = ct->threadid; + gdb_put_packet(connection, "OK", 2); + } else + gdb_put_packet(connection, "E01", 3); + } + } else + gdb_put_packet(connection, "OK", 2); + + return ERROR_OK; +} + +static int linux_thread_packet(struct connection *connection, char *packet, + int packet_size) +{ + int retval = ERROR_OK; + struct current_thread *ct; + struct target *target = get_target_from_connection(connection); + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + + switch (packet[0]) { + case 'T': /* Is thread alive?*/ + + linux_gdb_T_packet(connection, target, packet, packet_size); + break; + case 'H': /* Set current thread */ + /* ( 'c' for step and continue, 'g' for all other operations )*/ + /*LOG_INFO(" H packet received '%s'", packet);*/ + linux_gdb_h_packet(connection, target, packet, packet_size); + break; + case 'q': + + if ((strstr(packet, "qSymbol"))) { + if (rtos_qsymbol(connection, packet, packet_size) == 1) { + gdb_put_packet(connection, "OK", 2); + + linux_compute_virt2phys(target, + target->rtos-> + symbols[INIT_TASK]. + address); + } + + break; + } else if (strstr(packet, "qfThreadInfo")) { + if (linux_os->thread_list == NULL) { + retval = linux_gdb_thread_packet(target, + connection, + packet, + packet_size); + break; + } else { + retval = linux_gdb_thread_update(target, + connection, + packet, + packet_size); + break; + } + } else if (strstr(packet, "qsThreadInfo")) { + gdb_put_packet(connection, "l", 1); + break; + } else if (strstr(packet, "qThreadExtraInfo,")) { + linux_thread_extra_info(target, connection, packet, + packet_size); + break; + } else { + retval = GDB_THREAD_PACKET_NOT_CONSUMED; + break; + } + + case 'Q': + /* previously response was : thread not found + * gdb_put_packet(connection, "E01", 3); */ + retval = GDB_THREAD_PACKET_NOT_CONSUMED; + break; + case 'c': + case 's':{ + if (linux_os->threads_lookup == 1) { + ct = linux_os->current_threads; + + while ((ct != NULL) + && (ct->core_id) != target->coreid) { + ct = ct->next; + } + + if ((ct != NULL) && (ct->threadid == -1)) { + ct = linux_os->current_threads; + + while ((ct != NULL) + && (ct->threadid == -1)) { + ct = ct->next; + } + } + + if ((ct != NULL) && (ct->threadid != + target->rtos-> + current_threadid) + && (target->rtos->current_threadid != -1)) + LOG_WARNING("WARNING! current GDB thread do not match"\ + "current thread running."\ + "Switch thread in GDB to threadid %d", (int)ct->threadid); + + LOG_INFO("threads_needs_update = 1"); + linux_os->threads_needs_update = 1; + } + } + + /* if a packet handler returned an error, exit input loop */ + if (retval != ERROR_OK) + return retval; + } + + return retval; +} + +static int linux_os_smp_init(struct target *target) +{ + struct target_list *head; + /* keep only target->rtos */ + struct rtos *rtos = target->rtos; + struct linux_os *os_linux = + (struct linux_os *)rtos->rtos_specific_params; + struct current_thread *ct; + head = target->head; + + while (head != (struct target_list *)NULL) { + if (head->target->rtos != rtos) { + struct linux_os *smp_os_linux = + (struct linux_os *)head->target->rtos-> + rtos_specific_params; + /* remap smp target on rtos */ + free(head->target->rtos); + head->target->rtos = rtos; + /* reuse allocated ct */ + ct = smp_os_linux->current_threads; + ct->threadid = -1; + ct->TS = 0xdeadbeef; + ct->core_id = head->target->coreid; + os_linux->current_threads = + add_current_thread(os_linux->current_threads, ct); + os_linux->nr_cpus++; + free(smp_os_linux); + } + + head = head->next; + } + + return ERROR_OK; +} + +static int linux_os_create(struct target *target) +{ + struct linux_os *os_linux = calloc(1, sizeof(struct linux_os)); + struct current_thread *ct = calloc(1, sizeof(struct current_thread)); + LOG_INFO("linux os creation\n"); + os_linux->init_task_addr = 0xdeadbeef; + os_linux->name = "linux"; + os_linux->thread_list = NULL; + os_linux->thread_count = 0; + target->rtos->current_threadid = -1; + os_linux->nr_cpus = 1; + os_linux->threads_lookup = 0; + os_linux->threads_needs_update = 0; + os_linux->threadid_count = 1; + os_linux->current_threads = NULL; + target->rtos->rtos_specific_params = (void *)os_linux; + ct->core_id = target->coreid; + ct->threadid = -1; + ct->TS = 0xdeadbeef; + os_linux->current_threads = + add_current_thread(os_linux->current_threads, ct); + /* overload rtos thread default handler */ + target->rtos->gdb_thread_packet = linux_thread_packet; + /* initialize a default virt 2 phys translation */ + os_linux->phys_mask = ~0xc0000000; + os_linux->phys_base = 0x0; + return JIM_OK; +} + +static char *linux_ps_command(struct target *target) +{ + struct linux_os *linux_os = (struct linux_os *) + target->rtos->rtos_specific_params; + int retval = ERROR_OK; + char *display; + + if (linux_os->threads_lookup == 0) { + retval = linux_get_tasks(target, 1); + } else { + if (linux_os->threads_needs_update != 0) + retval = linux_task_update(target, 0); + } + + if (retval == ERROR_OK) { + struct threads *temp = linux_os->thread_list; + char *tmp; + LOG_INFO("allocation for %d threads line", + linux_os->thread_count); + display = calloc((linux_os->thread_count + 2) * 80, 1); + + if (!display) + goto error; + + tmp = display; + tmp += sprintf(tmp, "PID\t\tCPU\t\tASID\t\tNAME\n"); + tmp += sprintf(tmp, "---\t\t---\t\t----\t\t----\n"); + + while (temp != NULL) { + if (temp->status) { + if (temp->context) + tmp += + sprintf(tmp, + "%d\t\t%d\t\t%x\t\t%s\n", + (int)temp->pid, temp->oncpu, + temp->asid, temp->name); + else + tmp += + sprintf(tmp, + "%d\t\t%d\t\t%x\t\t%s\n", + (int)temp->pid, temp->oncpu, + temp->asid, temp->name); + } + + temp = temp->next; + } + + return display; + } + +error: + display = calloc(40, 1); + sprintf(display, "linux_ps_command failed\n"); + return display; +} diff --git a/src/rtos/linux_header.h b/src/rtos/linux_header.h new file mode 100644 index 0000000000..faaf319b1c --- /dev/null +++ b/src/rtos/linux_header.h @@ -0,0 +1,32 @@ +/* gdb script to update the header file + according to kernel version and build option + before executing function awareness + kernel symbol must be loaded : symbol vmlinux + +define awareness + set logging off + set logging file linux_header.h + set logging on + + printf "#define QAT %p\n",&((struct task_struct *)(0))->stack + set $a=&((struct list_head *)(0))->next + set $a=(int)$a+(int)&((struct task_struct *)(0))->tasks + printf "#define NEXT %p\n",$a + printf "#define COMM %p\n",&((struct task_struct *)(0))->comm + printf "#define MEM %p\n",&((struct task_struct *)(0))->mm + printf "#define ONCPU %p\n",&((struct task_struct *)(0))->on_cpu + printf "#define PID %p\n",&((struct task_struct *)(0))->pid + printf "#define CPU_CONT %p\n",&((struct thread_info *)(0))->cpu_context + printf "#define PREEMPT %p\n",&((struct thread_info *)(0))->preempt_count + printf "#define MM_CTX %p\n",&((struct mm_struct *)(0))->context + end +*/ +#define QAT 0x4 +#define NEXT 0x1b0 +#define COMM 0x2d4 +#define MEM 0x1cc +#define ONCPU 0x18 +#define PID 0x1f4 +#define CPU_CONT 0x1c +#define PREEMPT 0x4 +#define MM_CTX 0x160 diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c index a0bbc82b5e..3deeb68e72 100644 --- a/src/rtos/rtos.c +++ b/src/rtos/rtos.c @@ -37,12 +37,14 @@ static void hex_to_str( char* dst, char * hex_src ); extern struct rtos_type FreeRTOS_rtos; extern struct rtos_type ThreadX_rtos; extern struct rtos_type eCos_rtos; +extern struct rtos_type Linux_os; static struct rtos_type *rtos_types[] = { &ThreadX_rtos, &FreeRTOS_rtos, &eCos_rtos, + &Linux_os, NULL }; -- 2.30.2