// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* Copyright (C) 2011 by STEricsson *
* Heythem Bouhaja heythem.bouhaja@stericsson.com : creation *
* Michel JAOUEN michel.jaouen@stericsson.com : adaptation to rtos *
***************************************************************************/
#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 "helper/types.h"
#include "rtos.h"
#include "rtos_standard_stackings.h"
#include <target/register.h>
#include <target/smp.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 {
const 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;
};
static 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, target_addr_t address)
{
struct linux_os *linux_os = (struct linux_os *)
target->rtos->rtos_specific_params;
target_addr_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_read_phys_memory(target, pa, size, count, buffer);
#endif
target_read_memory(target, address, size, count, buffer);
return ERROR_OK;
}
static int fill_buffer(struct target *target, uint32_t addr, uint8_t *buffer)
{
if ((addr & 0xfffffffc) != addr)
LOG_INFO("unaligned address %" PRIx32 "!!", addr);
int retval = linux_read_memory(target, addr, 4, 1, buffer);
return retval;
}
static 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, struct rtos_reg **reg_list, int *num_regs)
{
struct target *target = rtos->target;
struct linux_os *linux_os = (struct linux_os *)
target->rtos->rtos_specific_params;
struct current_thread *tmp = linux_os->current_threads;
struct current_thread *next;
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));
if (found == 0) {
LOG_ERROR("could not find thread: %" PRIx64, thread_id);
return ERROR_FAIL;
}
/* search target to perform the access */
struct reg **gdb_reg_list;
struct target_list *head;
found = 0;
foreach_smp_target(head, target->smp_targets) {
if (head->target->coreid == next->core_id) {
target = head->target;
found = 1;
break;
}
}
if (found == 0) {
LOG_ERROR
(
"current thread %" PRIx64 ": no target to perform access of core id %" PRIx32,
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, &gdb_reg_list, num_regs, REG_CLASS_GENERAL);
if (retval != ERROR_OK)
return retval;
*reg_list = calloc(*num_regs, sizeof(struct rtos_reg));
for (int i = 0; i < *num_regs; ++i) {
if (!gdb_reg_list[i]->valid)
|