blob: 44d8a433ef8c879c8a91f1987d8fd16cde6d80c3 [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 Memory - memory access service.
*/
#include "config.h"
#if SERVICE_Memory
#include <assert.h>
#include "mdep.h"
#include "protocol.h"
#include "context.h"
#include "json.h"
#include "exceptions.h"
#include "runctrl.h"
#include "myalloc.h"
#include "channel.h"
#include "base64.h"
static const char * MEMORY = "Memory";
#define BYTE_VALID 0x00
#define BYTE_UNKNOWN 0x01
#define BYTE_INVALID 0x02
#define BYTE_CANNOT_READ 0x04
#define BYTE_CANNOT_WRITE 0x08
#define CMD_GET 1
#define CMD_SET 2
#define CMD_FILL 3
#define BUF_SIZE 0x1000
struct MemoryCommandArgs {
InputStream * inp;
OutputStream * out;
char token[256];
unsigned long addr;
unsigned long size;
int word_size;
int mode;
Context * ctx;
};
static void write_context(OutputStream * out, Context * ctx) {
assert(!ctx->exited);
out->write(out, '{');
json_write_string(out, "ID");
out->write(out, ':');
json_write_string(out, container_id(ctx));
#if !defined(_WRS_KERNEL)
out->write(out, ',');
json_write_string(out, "ProcessID");
out->write(out, ':');
json_write_string(out, pid2id(ctx->mem, 0));
#endif
/* Check endianness */
{
short n = 0x0201;
char * p = (char *)&n;
out->write(out, ',');
json_write_string(out, "BigEndian");
out->write(out, ':');
json_write_boolean(out, *p == 0x02);
}
out->write(out, ',');
json_write_string(out, "AddressSize");
out->write(out, ':');
json_write_ulong(out, sizeof(char *));
out->write(out, '}');
}
static void write_ranges(OutputStream * out, unsigned long addr, int size, int offs, int status, char * msg) {
out->write(out, '[');
if (offs > 0) {
out->write(out, '{');
json_write_string(out, "addr");
out->write(out, ':');
json_write_ulong(out, addr);
out->write(out, ',');
json_write_string(out, "size");
out->write(out, ':');
json_write_ulong(out, offs);
out->write(out, ',');
json_write_string(out, "stat");
out->write(out, ':');
json_write_ulong(out, 0);
out->write(out, '}');
out->write(out, ',');
}
if (offs < size) {
out->write(out, '{');
json_write_string(out, "addr");
out->write(out, ':');
json_write_ulong(out, addr + offs);
out->write(out, ',');
json_write_string(out, "size");
out->write(out, ':');
json_write_ulong(out, size - offs);
out->write(out, ',');
json_write_string(out, "stat");
out->write(out, ':');
json_write_ulong(out, status);
out->write(out, ',');
json_write_string(out, "msg");
out->write(out, ':');
json_write_string(out, msg);
out->write(out, '}');
}
out->write(out, ']');
out->write(out, 0);
}
static void command_get_context(char * token, InputStream * inp, OutputStream * out) {
int err = 0;
char id[256];
Context * ctx = 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);
ctx = id2ctx(id);
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, ctx);
}
else {
write_stringz(out, "null");
}
out->write(out, 0);
out->write(out, MARKER_EOM);
}
static void command_get_children(char * token, InputStream * inp, OutputStream * out) {
char id[256];
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);
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, 0);
out->write(out, '[');
if (id[0] == 0) {
LINK * qp;
int cnt = 0;
for (qp = context_root.next; qp != &context_root; qp = qp->next) {
Context * ctx = ctxl2ctxp(qp);
if (ctx->exited) continue;
if (ctx->parent != NULL) continue;
if (cnt > 0) out->write(out, ',');
json_write_string(out, container_id(ctx));
cnt++;
}
}
out->write(out, ']');
out->write(out, 0);
out->write(out, MARKER_EOM);
}
static struct MemoryCommandArgs * read_command_args(char * token, InputStream * inp, OutputStream * out, int cmd) {
int err = 0;
char id[256];
struct MemoryCommandArgs buf;
memset(&buf, 0, sizeof(buf));
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
buf.addr = json_read_ulong(inp);
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
buf.word_size = (int)json_read_long(inp);
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
buf.size = (int)json_read_long(inp);
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
buf.mode = (int)json_read_long(inp);
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (cmd == CMD_GET && inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
buf.ctx = id2ctx(id);
if (buf.ctx == NULL) err = ERR_INV_CONTEXT;
else if (buf.ctx->exited) err = ERR_ALREADY_EXITED;
if (err != 0) {
if (cmd == CMD_SET || cmd == CMD_FILL) {
if (inp->read(inp) != '[') exception(ERR_JSON_SYNTAX);
if (inp->peek(inp) == ']') {
inp->read(inp);
}
else {
while (1) {
char ch;
json_read_ulong(inp);
ch = inp->read(inp);
if (ch == ',') continue;
if (ch == ']') break;
exception(ERR_JSON_SYNTAX);
}
}
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
}
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(out, "R");
write_stringz(out, token);
write_stringz(out, "null");
write_errno(out, err);
write_stringz(out, "null");
out->write(out, MARKER_EOM);
return NULL;
}
else {
struct MemoryCommandArgs * args = (struct MemoryCommandArgs *)
loc_alloc(sizeof(struct MemoryCommandArgs));
*args = buf;
args->inp = inp;
args->out = out;
strncpy(args->token, token, sizeof(args->token));
stream_lock(out);
context_lock(buf.ctx);
return args;
}
}
static void send_event_memory_changed(OutputStream * out, Context * ctx, unsigned long addr, unsigned long size) {
write_stringz(out, "E");
write_stringz(out, MEMORY);
write_stringz(out, "memoryChanged");
json_write_string(out, container_id(ctx));
out->write(out, 0);
/* <array of addres ranges> */
out->write(out, '[');
out->write(out, '{');
json_write_string(out, "addr");
out->write(out, ':');
json_write_ulong(out, addr);
out->write(out, ',');
json_write_string(out, "size");
out->write(out, ':');
json_write_ulong(out, size);
out->write(out, '}');
out->write(out, ']');
out->write(out, 0);
out->write(out, MARKER_EOM);
}
static void safe_memory_set(void * parm) {
struct MemoryCommandArgs * args = (struct MemoryCommandArgs *)parm;
InputStream * inp = args->inp;
OutputStream * out = args->out;
char * token = args->token;
unsigned long addr0 = args->addr;
unsigned long addr = args->addr;
unsigned long size = 0;
int word_size = args->word_size;
int mode = args->mode;
Context * ctx = args->ctx;
char buf[BUF_SIZE];
int err = 0;
if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
if (inp->read(inp) != '"') exception(ERR_JSON_SYNTAX);
for (;;) {
int rd = read_base64(inp, buf, sizeof(buf));
if (rd == 0) break;
if (err == 0) {
// TODO: word size, mode
if (context_write_mem(ctx, addr, buf, rd) < 0) {
err = errno;
}
else {
addr += rd;
}
}
size += rd;
}
if (inp->read(inp) != '"') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
send_event_memory_changed(&broadcast_stream, ctx, addr0, size);
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, err);
if (err == 0) {
write_stringz(out, "null");
}
else {
char msg[0x400];
strncpy(msg, errno_to_str(err), sizeof(msg));
write_ranges(out, addr0, size, addr - addr0, BYTE_INVALID | BYTE_CANNOT_WRITE, msg);
}
out->write(out, MARKER_EOM);
out->flush(out);
stream_unlock(out);
context_unlock(ctx);
loc_free(args);
}
static void command_set(char * token, InputStream * inp, OutputStream * out) {
struct MemoryCommandArgs * args = read_command_args(token, inp, out, CMD_SET);
if (args != NULL) post_safe_event(safe_memory_set, args);
}
static void safe_memory_get(void * parm) {
struct MemoryCommandArgs * args = (struct MemoryCommandArgs *)parm;
OutputStream * out = args->out;
char * token = args->token;
unsigned long addr0 = args->addr;
unsigned long addr = args->addr;
unsigned long size = args->size;
int word_size = args->word_size;
int mode = args->mode;
Context * ctx = args->ctx;
char buf[BUF_SIZE + 4];
int buf_pos = 0;
int err = 0;
int rem = 0;
unsigned long chk = 0;
if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
write_stringz(out, "R");
write_stringz(out, token);
out->write(out, '"');
while (err == 0 && addr < addr0 + size) {
int rd = addr0 + size - addr;
if (rd > BUF_SIZE) rd = BUF_SIZE;
// TODO: word size, mode
if (context_read_mem(ctx, addr, buf + rem, rd) < 0) {
err = errno;
}
else {
int i = 0, j = rem + rd;
rem = j % 3;
chk += write_base64(out, buf, j - rem);
for (i = 0; i < rem; i++) buf[i] = buf[j - rem + i];
addr += rd;
}
}
chk += write_base64(out, buf, rem);
out->write(out, '"');
out->write(out, 0);
assert(chk == ((addr - addr0) + 2) / 3 * 4);
write_errno(out, err);
if (err == 0) {
write_stringz(out, "null");
}
else {
char msg[0x400];
strncpy(msg, errno_to_str(err), sizeof(msg));
write_ranges(out, addr0, size, addr - addr0, BYTE_INVALID | BYTE_CANNOT_READ, msg);
}
out->write(out, MARKER_EOM);
out->flush(out);
stream_unlock(out);
context_unlock(ctx);
loc_free(args);
}
static void command_get(char * token, InputStream * inp, OutputStream * out) {
struct MemoryCommandArgs * args = read_command_args(token, inp, out, CMD_GET);
if (args != NULL) post_safe_event(safe_memory_get, args);
}
static void safe_memory_fill(void * parm) {
struct MemoryCommandArgs * args = (struct MemoryCommandArgs *)parm;
InputStream * inp = args->inp;
OutputStream * out = args->out;
char * token = args->token;
unsigned long addr0 = args->addr;
unsigned long addr = args->addr;
unsigned long size = args->size;
int word_size = args->word_size;
int mode = args->mode;
Context * ctx = args->ctx;
char buf[0x1000];
int buf_pos = 0;
int err = 0;
if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
if (inp->read(inp) != '[') exception(ERR_JSON_SYNTAX);
if (inp->peek(inp) == ']') {
inp->read(inp);
}
else {
while (1) {
char ch;
if (err == 0) {
if (buf_pos >= sizeof(buf)) err = ERR_BUFFER_OVERFLOW;
else buf[buf_pos++] = (char)json_read_ulong(inp);
}
else {
json_read_ulong(inp);
}
ch = inp->read(inp);
if (ch == ',') continue;
if (ch == ']') break;
exception(ERR_JSON_SYNTAX);
}
}
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
while (err == 0 && buf_pos < (int)size && buf_pos <= sizeof(buf) / 2) {
if (buf_pos == 0) {
buf[buf_pos++] = 0;
}
else {
memcpy(buf + buf_pos, buf, buf_pos);
buf_pos *= 2;
}
}
while (err == 0 && addr < addr0 + size) {
int wr = addr0 + size - addr;
if (wr > buf_pos) wr = buf_pos;
// TODO: word size, mode
if (context_write_mem(ctx, addr, buf, wr) < 0) {
err = errno;
}
else {
addr += wr;
}
}
send_event_memory_changed(&broadcast_stream, ctx, addr0, size);
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, err);
if (err == 0) {
write_stringz(out, "null");
}
else {
char msg[0x400];
strncpy(msg, errno_to_str(err), sizeof(msg));
write_ranges(out, addr0, size, addr - addr0, BYTE_INVALID | BYTE_CANNOT_WRITE, msg);
}
out->write(out, MARKER_EOM);
out->flush(out);
stream_unlock(out);
context_unlock(ctx);
loc_free(args);
}
static void command_fill(char * token, InputStream * inp, OutputStream * out) {
struct MemoryCommandArgs * args = read_command_args(token, inp, out, CMD_FILL);
if (args != NULL) post_safe_event(safe_memory_fill, args);
}
static void send_event_context_added(OutputStream * out, Context * ctx) {
write_stringz(out, "E");
write_stringz(out, MEMORY);
write_stringz(out, "contextAdded");
/* <array of context data> */
out->write(out, '[');
write_context(out, ctx);
out->write(out, ']');
out->write(out, 0);
out->write(out, MARKER_EOM);
}
static void send_event_context_changed(OutputStream * out, Context * ctx) {
write_stringz(out, "E");
write_stringz(out, MEMORY);
write_stringz(out, "contextChanged");
/* <array of context data> */
out->write(out, '[');
write_context(out, ctx);
out->write(out, ']');
out->write(out, 0);
out->write(out, MARKER_EOM);
}
static void send_event_context_removed(OutputStream * out, Context * ctx) {
write_stringz(out, "E");
write_stringz(out, MEMORY);
write_stringz(out, "contextRemoved");
/* <array of context IDs> */
out->write(out, '[');
json_write_string(out, container_id(ctx));
out->write(out, ']');
out->write(out, 0);
out->write(out, MARKER_EOM);
}
static void event_context_created(Context * ctx) {
if (ctx->parent != NULL) return;
send_event_context_added(&broadcast_stream, ctx);
broadcast_stream.flush(&broadcast_stream);
}
static void event_context_changed(Context * ctx) {
if (ctx->parent != NULL) return;
send_event_context_changed(&broadcast_stream, ctx);
broadcast_stream.flush(&broadcast_stream);
}
static void event_context_exited(Context * ctx) {
if (ctx->parent != NULL) return;
send_event_context_removed(&broadcast_stream, ctx);
broadcast_stream.flush(&broadcast_stream);
}
void ini_memory_service(void) {
static ContextEventListener listener = {
event_context_created,
event_context_exited,
NULL,
NULL,
event_context_changed,
NULL
};
add_context_event_listener(&listener);
add_command_handler(MEMORY, "getContext", command_get_context);
add_command_handler(MEMORY, "getChildren", command_get_children);
add_command_handler(MEMORY, "set", command_set);
add_command_handler(MEMORY, "get", command_get);
add_command_handler(MEMORY, "fill", command_fill);
}
#endif