Commit 0a4b27ec authored by Michel JAOUEN's avatar Michel JAOUEN Committed by Øyvind Harboe
Browse files

rtos : linux awareness



Change-Id: I41294ccaa4a3cd253919c8b1b558205903bcb695
Signed-off-by: default avatarMichel JAOUEN <michel.jaouen@stericsson.com>
Reviewed-on: http://openocd.zylin.com/348


Tested-by: jenkins
Reviewed-by: default avatarHeythem Bouhaja <heythem.bouhaja-nonst@stericsson.com>
Reviewed-by: default avatarØyvind Harboe <oyvindharboe@gmail.com>
parent 433ca26f
......@@ -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
......
/***************************************************************************
* 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 <helper/time_support.h>
#include <jtag/jtag.h>
#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 <target/register.h>
#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, &reg_list, &reg_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, &reg_list,
&reg_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;
}