blob: c524de44a6f0a93904acaddb64e59c2c6aadb80d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* TCF Registers - CPU registers access service.
*/
#include "config.h"
#if SERVICE_Registers
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "mdep.h"
#include "protocol.h"
#include "context.h"
#include "json.h"
#include "exceptions.h"
#include "registers.h"
static const char * REGISTERS = "Registers";
#if defined(_WRS_KERNEL)
# define regs_index taskRegName
# if defined(_WRS_REG_INDEX_REGWIDTH) || (CPU_FAMILY==COLDFIRE)
# define REG_WIDTH(x) (x).regWidth
# else
# define REG_WIDTH(x) 4
# endif
#else
typedef struct {
char *regName; /* pointer to register name */
int regOff; /* offset to entry in REG_SET */
int regWidth; /* register width in bytes */
} REG_INDEX;
#define REG_WIDTH(x) (x).regWidth
#define REG_OFFSET(name) offsetof(REG_SET, name)
#if defined(__i386__)
static REG_INDEX regs_index[] = {
{ "edi", REG_OFFSET(edi), 4},
{ "esi", REG_OFFSET(esi), 4},
{ "ebp", REG_OFFSET(ebp), 4},
{ "esp", REG_OFFSET(esp), 4},
{ "ebx", REG_OFFSET(ebx), 4},
{ "edx", REG_OFFSET(edx), 4},
{ "ecx", REG_OFFSET(ecx), 4},
{ "eax", REG_OFFSET(eax), 4},
{ "eflags", REG_OFFSET(eflags), 4},
{ "eip", REG_OFFSET(eip), 4},
{ NULL, 0, 0},
};
#else
/* TODO: Linux: support for CPU types other then I86 */
#error "Unknown CPU"
#endif
#endif /* _WRS_KERNEL */
static short endianess_test = 0x0201;
#define BIG_ENDIAN_DATA (*(char *)&endianess_test == 0x02)
static void write_context(OutputStream * out, char * id, Context * ctx, REG_INDEX * idx) {
assert(!ctx->exited);
out->write(out, '{');
json_write_string(out, "ID");
out->write(out, ':');
json_write_string(out, id);
out->write(out, ',');
json_write_string(out, "ParentID");
out->write(out, ':');
json_write_string(out, thread_id(ctx));
out->write(out, ',');
json_write_string(out, "Name");
out->write(out, ':');
json_write_string(out, idx->regName);
out->write(out, ',');
json_write_string(out, "Readable");
out->write(out, ':');
json_write_boolean(out, 1);
out->write(out, ',');
json_write_string(out, "Writeable");
out->write(out, ':');
json_write_boolean(out, 1);
out->write(out, ',');
json_write_string(out, "Formats");
out->write(out, ':');
out->write(out, '[');
json_write_string(out, "Hex");
out->write(out, ',');
json_write_string(out, "Decimal");
out->write(out, ']');
#if !defined(_WRS_KERNEL)
out->write(out, ',');
json_write_string(out, "ProcessID");
out->write(out, ':');
json_write_string(out, pid2id(ctx->mem, 0));
#endif
out->write(out, ',');
json_write_string(out, "BigEndian");
out->write(out, ':');
json_write_boolean(out, BIG_ENDIAN_DATA);
out->write(out, '}');
out->write(out, 0);
}
static int id2register(char * id, Context ** ctx, REG_INDEX ** idx) {
int i;
char name[64];
*ctx = NULL;
*idx = NULL;
if (*id++ != 'R') {
errno = ERR_INV_CONTEXT;
return -1;
}
i = 0;
while (*id != '.') {
if (*id == 0) {
errno = ERR_INV_CONTEXT;
return -1;
}
name[i++] = *id++;
}
name[i++] = 0;
id++;
for (i = 0; regs_index[i].regName != NULL; i++) {
if (strcmp(regs_index[i].regName, name) == 0) break;
}
if (regs_index[i].regName == NULL) {
errno = ERR_INV_CONTEXT;
return -1;
}
*ctx = id2ctx(id);
*idx = regs_index + i;
return 0;
}
static void command_get_context(char * token, InputStream * inp, OutputStream * out) {
int err = 0;
char id[256];
Context * ctx = NULL;
REG_INDEX * idx = NULL;
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
id2register(id, &ctx, &idx);
if (ctx == NULL) err = ERR_INV_CONTEXT;
else if (ctx->exited) err = ERR_ALREADY_EXITED;
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, err);
if (err == 0) {
write_context(out, id, ctx, idx);
}
else {
write_stringz(out, "null");
}
out->write(out, MARKER_EOM);
}
static void command_get_children(char * token, InputStream * inp, OutputStream * out) {
char id[256];
pid_t pid, parent;
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
pid = id2pid(id, &parent);
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, 0);
out->write(out, '[');
if (pid != 0 && parent != 0) {
Context * ctx = context_find_from_pid(pid);
if (ctx != NULL) {
char t_id[128];
REG_INDEX * idx = regs_index;
strcpy(t_id, thread_id(ctx));
while (idx->regName != NULL) {
char r_id[128];
if (idx != regs_index) out->write(out, ',');
snprintf(r_id, sizeof(r_id), "R%s.%s", idx->regName, t_id);
json_write_string(out, r_id);
idx ++;
}
}
}
out->write(out, ']');
out->write(out, 0);
out->write(out, MARKER_EOM);
}
static void command_get(char * token, InputStream * inp, OutputStream * out) {
int err = 0;
char id[256];
char fmt[256];
int hex = 0;
Context * ctx = NULL;
REG_INDEX * idx = NULL;
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
json_read_string(inp, fmt, sizeof(fmt));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
id2register(id, &ctx, &idx);
if (ctx == NULL || idx == NULL) err = ERR_INV_CONTEXT;
else if (ctx->exited) err = ERR_ALREADY_EXITED;
else if (!ctx->intercepted) err = ERR_IS_RUNNING;
else if (strcmp(fmt, "Hex") == 0) hex = 1;
else if (strcmp(fmt, "Decimal") != 0) err = ERR_INV_FORMAT;
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, err);
if (err == 0) {
int64 n = 0;
char val[64];
int val_len = 0;
assert(REG_WIDTH(*idx) <= sizeof(n));
memcpy( (char *)&n + (BIG_ENDIAN_DATA ? sizeof(n) - REG_WIDTH(*idx) : 0),
(char *)&ctx->regs + idx->regOff,
REG_WIDTH(*idx));
if (hex) {
while (val_len < REG_WIDTH(*idx) * 2) {
int i = (int)(n & 0xf);
val[val_len++] = i < 10 ? '0' + i : 'A' + i - 10;
n = n >> 4;
}
}
else {
int neg = n < 0;
uns64 m = neg ? -n : n;
do {
int i = (int)(m % 10);
val[val_len++] = '0' + i;
m = m / 10;
}
while (m != 0);
if (neg) val[val_len++] = '-';
}
out->write(out, '"');
while (val_len > 0) out->write(out, val[--val_len]);
out->write(out, '"');
out->write(out, 0);
}
else {
write_stringz(out, "null");
}
out->write(out, MARKER_EOM);
}
static void send_event_register_changed(OutputStream * out, char * id) {
write_stringz(out, "E");
write_stringz(out, REGISTERS);
write_stringz(out, "registerChanged");
json_write_string(out, id);
out->write(out, 0);
out->write(out, MARKER_EOM);
}
static void command_set(char * token, InputStream * inp, OutputStream * out) {
int err = 0;
char id[256];
char fmt[256];
char val[256];
int hex = 0;
char * ptr = NULL;
Context * ctx = NULL;
REG_INDEX * idx = NULL;
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
json_read_string(inp, fmt, sizeof(fmt));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
json_read_string(inp, val, sizeof(val));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
id2register(id, &ctx, &idx);
if (ctx == NULL || idx == NULL) err = ERR_INV_CONTEXT;
else if (ctx->exited) err = ERR_ALREADY_EXITED;
else if (!ctx->intercepted) err = ERR_IS_RUNNING;
else if (strcmp(fmt, "Hex") == 0) hex = 1;
else if (strcmp(fmt, "Decimal") != 0) err = ERR_INV_FORMAT;
if (err == 0) {
int64 n = 0;
ptr = val;
if (hex) {
while (1) {
if (*ptr >= '0' && *ptr <= '9') n = (n << 4) | (int64)(*ptr++ - '0');
else if (*ptr >= 'A' && *ptr <= 'F') n = (n << 4) | (int64)(*ptr++ - 'A' + 10);
else if (*ptr >= 'a' && *ptr <= 'f') n = (n << 4) | (int64)(*ptr++ - 'a' + 10);
else break;
}
}
else {
uns64 m = 0;
int neg = *ptr == '-';
if (neg) ptr++;
while (1) {
if (*ptr >= '0' && *ptr <= '9') m = (m * 10) + (uns64)(*ptr++ - '0');
else break;
}
n = neg ? (~m + 1) : m;
}
if (*ptr != 0) {
err = ERR_INV_NUMBER;
}
else {
assert(REG_WIDTH(*idx) <= sizeof(n));
memcpy( (char *)&ctx->regs + idx->regOff,
(char *)&n + (BIG_ENDIAN_DATA ? sizeof(n) - REG_WIDTH(*idx) : 0),
REG_WIDTH(*idx));
ctx->regs_dirty = 1;
send_event_register_changed(out, id);
}
}
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, err);
out->write(out, MARKER_EOM);
}
void ini_registers_service(void) {
add_command_handler(REGISTERS, "getContext", command_get_context);
add_command_handler(REGISTERS, "getChildren", command_get_children);
add_command_handler(REGISTERS, "get", command_get);
add_command_handler(REGISTERS, "set", command_set);
}
#endif