blob: cbc77a36f24d370945e51e4e61f17e8469459de2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2010 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
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* TCF Memory - memory access service.
*/
#include <config.h>
#if SERVICE_Memory
#include <assert.h>
#include <framework/protocol.h>
#include <framework/context.h>
#include <framework/json.h>
#include <framework/exceptions.h>
#include <framework/myalloc.h>
#include <framework/channel.h>
#include <framework/trace.h>
#include <services/memoryservice.h>
#include <services/runctrl.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 (128 * MEM_USAGE_FACTOR)
typedef struct MemoryCommandArgs {
Channel * c;
char token[256];
ContextAddress addr;
unsigned long size;
int word_size;
int mode;
Context * ctx;
} MemoryCommandArgs;
static void write_context(OutputStream * out, Context * ctx) {
assert(!ctx->exited);
write_stream(out, '{');
json_write_string(out, "ID");
write_stream(out, ':');
json_write_string(out, ctx->id);
if (ctx->parent != NULL) {
write_stream(out, ',');
json_write_string(out, "ParentID");
write_stream(out, ':');
json_write_string(out, ctx->parent->id);
}
write_stream(out, ',');
json_write_string(out, "ProcessID");
write_stream(out, ':');
json_write_string(out, context_get_group(ctx, CONTEXT_GROUP_PROCESS)->id);
if (ctx->name != NULL) {
write_stream(out, ',');
json_write_string(out, "Name");
write_stream(out, ':');
json_write_string(out, ctx->name);
}
write_stream(out, ',');
json_write_string(out, "BigEndian");
write_stream(out, ':');
json_write_boolean(out, ctx->big_endian);
if (ctx->mem_access) {
int cnt = 0;
write_stream(out, ',');
json_write_string(out, "AddressSize");
write_stream(out, ':');
json_write_ulong(out, context_word_size(ctx));
write_stream(out, ',');
json_write_string(out, "AccessTypes");
write_stream(out, ':');
write_stream(out, '[');
if (ctx->mem_access & MEM_ACCESS_INSTRUCTION) {
if (cnt++) write_stream(out, ',');
json_write_string(out, "instruction");
}
if (ctx->mem_access & MEM_ACCESS_DATA) {
if (cnt++) write_stream(out, ',');
json_write_string(out, "data");
}
if (ctx->mem_access & MEM_ACCESS_IO) {
if (cnt++) write_stream(out, ',');
json_write_string(out, "io");
}
if (ctx->mem_access & MEM_ACCESS_USER) {
if (cnt++) write_stream(out, ',');
json_write_string(out, "user");
}
if (ctx->mem_access & MEM_ACCESS_SUPERVISOR) {
if (cnt++) write_stream(out, ',');
json_write_string(out, "supervisor");
}
if (ctx->mem_access & MEM_ACCESS_HYPERVISOR) {
if (cnt++) write_stream(out, ',');
json_write_string(out, "hypervisor");
}
if (ctx->mem_access & MEM_ACCESS_VIRTUAL) {
if (cnt++) write_stream(out, ',');
json_write_string(out, "virtual");
}
if (ctx->mem_access & MEM_ACCESS_PHYSICAL) {
if (cnt++) write_stream(out, ',');
json_write_string(out, "physical");
}
if (ctx->mem_access & MEM_ACCESS_CACHE) {
if (cnt++) write_stream(out, ',');
json_write_string(out, "cache");
}
if (ctx->mem_access & MEM_ACCESS_TLB) {
if (cnt++) write_stream(out, ',');
json_write_string(out, "tlb");
}
write_stream(out, ']');
}
write_stream(out, '}');
}
static void write_ranges(OutputStream * out, ContextAddress addr, int offs, int status, MemoryErrorInfo * err_info) {
int cnt = 0;
size_t size_valid = 0;
size_t size_error = 0;
if (err_info->error) {
size_valid = err_info->size_valid + offs;
size_error = err_info->size_error;
}
else {
size_valid = offs;
}
write_stream(out, '[');
if (size_valid > 0) {
write_stream(out, '{');
json_write_string(out, "addr");
write_stream(out, ':');
json_write_uint64(out, addr);
write_stream(out, ',');
json_write_string(out, "size");
write_stream(out, ':');
json_write_ulong(out, size_valid);
write_stream(out, ',');
json_write_string(out, "stat");
write_stream(out, ':');
json_write_ulong(out, 0);
write_stream(out, '}');
cnt++;
}
if (size_error > 0) {
if (cnt > 0) write_stream(out, ',');
write_stream(out, '{');
json_write_string(out, "addr");
write_stream(out, ':');
json_write_uint64(out, addr + size_valid);
write_stream(out, ',');
json_write_string(out, "size");
write_stream(out, ':');
json_write_ulong(out, size_error);
write_stream(out, ',');
json_write_string(out, "stat");
write_stream(out, ':');
json_write_ulong(out, BYTE_INVALID | status);
write_stream(out, ',');
json_write_string(out, "msg");
write_stream(out, ':');
write_error_object(out, err_info->error);
write_stream(out, '}');
}
write_stream(out, ']');
write_stream(out, 0);
}
static void command_get_context(char * token, Channel * c) {
int err = 0;
char id[256];
Context * ctx = NULL;
json_read_string(&c->inp, id, sizeof(id));
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
ctx = id2ctx(id);
if (ctx == NULL || ctx->mem_access == 0) err = ERR_INV_CONTEXT;
else if (ctx->exited) err = ERR_ALREADY_EXITED;
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
if (err == 0) {
write_context(&c->out, ctx);
}
else {
write_string(&c->out, "null");
}
write_stream(&c->out, 0);
write_stream(&c->out, MARKER_EOM);
}
static void command_get_children(char * token, Channel * c) {
char id[256];
json_read_string(&c->inp, id, sizeof(id));
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, 0);
write_stream(&c->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->parent != NULL) continue;
if (ctx->exited) continue;
if (ctx->mem_access == 0 && list_is_empty(&ctx->children)) continue;
if (cnt > 0) write_stream(&c->out, ',');
json_write_string(&c->out, ctx->id);
cnt++;
}
}
else {
Context * parent = id2ctx(id);
if (parent != NULL) {
LINK * l;
int cnt = 0;
for (l = parent->children.next; l != &parent->children; l = l->next) {
Context * ctx = cldl2ctxp(l);
assert(ctx->parent == parent);
if (ctx->exited) continue;
if (ctx->mem_access == 0 && list_is_empty(&ctx->children)) continue;
if (cnt > 0) write_stream(&c->out, ',');
json_write_string(&c->out, ctx->id);
cnt++;
}
}
}
write_stream(&c->out, ']');
write_stream(&c->out, 0);
write_stream(&c->out, MARKER_EOM);
}
static MemoryCommandArgs * read_command_args(char * token, Channel * c, int cmd) {
int err = 0;
char id[256];
MemoryCommandArgs buf;
memset(&buf, 0, sizeof(buf));
json_read_string(&c->inp, id, sizeof(id));
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
buf.addr = json_read_ulong(&c->inp);
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
buf.word_size = (int)json_read_long(&c->inp);
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
buf.size = (int)json_read_long(&c->inp);
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
buf.mode = (int)json_read_long(&c->inp);
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
if (cmd == CMD_GET && read_stream(&c->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;
else if (buf.ctx->mem_access == 0) err = ERR_INV_CONTEXT;
if (err != 0) {
if (cmd != CMD_GET) {
int ch;
while ((ch = read_stream(&c->inp)) != 0) {
if (ch < 0) exception(ERR_JSON_SYNTAX);
}
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
}
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
if (cmd == CMD_GET) write_stringz(&c->out, "null");
write_errno(&c->out, err);
write_stringz(&c->out, "null");
write_stream(&c->out, MARKER_EOM);
return NULL;
}
else {
MemoryCommandArgs * args = (MemoryCommandArgs *)loc_alloc(sizeof(MemoryCommandArgs));
*args = buf;
args->c = c;
strlcpy(args->token, token, sizeof(args->token));
channel_lock(c);
context_lock(buf.ctx);
return args;
}
}
static void send_event_memory_changed(OutputStream * out, Context * ctx, ContextAddress addr, unsigned long size) {
write_stringz(out, "E");
write_stringz(out, MEMORY);
write_stringz(out, "memoryChanged");
json_write_string(out, ctx->id);
write_stream(out, 0);
/* <array of addres ranges> */
write_stream(out, '[');
write_stream(out, '{');
json_write_string(out, "addr");
write_stream(out, ':');
json_write_uint64(out, addr);
write_stream(out, ',');
json_write_string(out, "size");
write_stream(out, ':');
json_write_ulong(out, size);
write_stream(out, '}');
write_stream(out, ']');
write_stream(out, 0);
write_stream(out, MARKER_EOM);
}
static void safe_memory_set(void * parm) {
MemoryCommandArgs * args = (MemoryCommandArgs *)parm;
Channel * c = args->c;
Context * ctx = args->ctx;
if (!is_channel_closed(c)) {
Trap trap;
if (set_trap(&trap)) {
InputStream * inp = &c->inp;
OutputStream * out = &c->out;
char * token = args->token;
ContextAddress addr0 = args->addr;
ContextAddress addr = args->addr;
unsigned long size = 0;
char buf[BUF_SIZE];
int err = 0;
MemoryErrorInfo err_info;
JsonReadBinaryState state;
memset(&err_info, 0, sizeof(err_info));
if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
json_read_binary_start(&state, inp);
for (;;) {
int rd = json_read_binary_data(&state, 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;
#if ENABLE_ExtendedMemoryErrorReports
context_get_mem_error_info(&err_info);
#endif
}
else {
addr += rd;
}
}
size += rd;
}
json_read_binary_end(&state);
if (read_stream(inp) != 0) exception(ERR_JSON_SYNTAX);
if (read_stream(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
send_event_memory_changed(&c->bcg->out, ctx, addr0, size);
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, err);
if (err == 0) {
write_stringz(out, "null");
}
else {
write_ranges(out, addr0, (int)(addr - addr0), BYTE_CANNOT_WRITE, &err_info);
}
write_stream(out, MARKER_EOM);
clear_trap(&trap);
}
else {
trace(LOG_ALWAYS, "Exception in message handler: %d %s",
trap.error, errno_to_str(trap.error));
channel_close(c);
}
}
channel_unlock(c);
context_unlock(ctx);
loc_free(args);
}
static void command_set(char * token, Channel * c) {
MemoryCommandArgs * args = read_command_args(token, c, CMD_SET);
if (args != NULL) post_safe_event(args->ctx, safe_memory_set, args);
}
static void safe_memory_get(void * parm) {
MemoryCommandArgs * args = (MemoryCommandArgs *)parm;
Channel * c = args->c;
Context * ctx = args->ctx;
if (!is_channel_closed(c)) {
Trap trap;
if (set_trap(&trap)) {
OutputStream * out = &args->c->out;
char * token = args->token;
ContextAddress addr0 = args->addr;
ContextAddress addr = args->addr;
unsigned long size = args->size;
unsigned long pos = 0;
char buf[BUF_SIZE];
int err = 0;
MemoryErrorInfo err_info;
JsonWriteBinaryState state;
if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
write_stringz(out, "R");
write_stringz(out, token);
json_write_binary_start(&state, out, size);
while (pos < size) {
int rd = size - pos;
if (rd > BUF_SIZE) rd = BUF_SIZE;
/* TODO: word size, mode */
if (err == 0) {
if (context_read_mem(ctx, addr, buf, rd) < 0) {
err = errno;
#if ENABLE_ExtendedMemoryErrorReports
context_get_mem_error_info(&err_info);
#endif
}
else {
addr += rd;
}
}
else {
memset(buf, 0, rd);
}
json_write_binary_data(&state, buf, rd);
pos += rd;
}
json_write_binary_end(&state);
write_stream(out, 0);
write_errno(out, err);
if (err == 0) {
write_stringz(out, "null");
}
else {
write_ranges(out, addr0, (int)(addr - addr0), BYTE_CANNOT_READ, &err_info);
}
write_stream(out, MARKER_EOM);
clear_trap(&trap);
}
else {
trace(LOG_ALWAYS, "Exception in message handler: %d %s",
trap.error, errno_to_str(trap.error));
channel_close(c);
}
}
channel_unlock(c);
context_unlock(ctx);
loc_free(args);
}
static void command_get(char * token, Channel * c) {
MemoryCommandArgs * args = read_command_args(token, c, CMD_GET);
if (args != NULL) post_safe_event(args->ctx, safe_memory_get, args);
}
static void safe_memory_fill(void * parm) {
MemoryCommandArgs * args = (MemoryCommandArgs *)parm;
Channel * c = args->c;
Context * ctx = args->ctx;
if (!is_channel_closed(c)) {
Trap trap;
if (set_trap(&trap)) {
InputStream * inp = &c->inp;
OutputStream * out = &c->out;
char * token = args->token;
ContextAddress addr0 = args->addr;
ContextAddress addr = args->addr;
unsigned long size = args->size;
MemoryErrorInfo err_info;
char buf[0x1000];
int buf_pos = 0;
int err = 0;
if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
if (read_stream(inp) != '[') exception(ERR_JSON_SYNTAX);
if (peek_stream(inp) == ']') {
read_stream(inp);
}
else {
for (;;) {
int ch;
if (err == 0) {
if (buf_pos >= (int)sizeof(buf)) err = ERR_BUFFER_OVERFLOW;
else buf[buf_pos++] = (char)json_read_ulong(inp);
}
else {
json_read_ulong(inp);
}
ch = read_stream(inp);
if (ch == ',') continue;
if (ch == ']') break;
exception(ERR_JSON_SYNTAX);
}
}
if (read_stream(inp) != 0) exception(ERR_JSON_SYNTAX);
if (read_stream(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
while (err == 0 && buf_pos < (int)size && buf_pos <= (int)(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) {
char tmp[sizeof(buf)];
int wr = (int)(addr0 + size - addr);
if (wr > buf_pos) wr = buf_pos;
/* TODO: word size, mode */
memcpy(tmp, buf, wr);
if (context_write_mem(ctx, addr, tmp, wr) < 0) {
err = errno;
#if ENABLE_ExtendedMemoryErrorReports
context_get_mem_error_info(&err_info);
#endif
}
else {
addr += wr;
}
}
send_event_memory_changed(&c->bcg->out, ctx, addr0, size);
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, err);
if (err == 0) {
write_stringz(out, "null");
}
else {
write_ranges(out, addr0, (int)(addr - addr0), BYTE_CANNOT_WRITE, &err_info);
}
write_stream(out, MARKER_EOM);
clear_trap(&trap);
}
else {
trace(LOG_ALWAYS, "Exception in message handler: %d %s",
trap.error, errno_to_str(trap.error));
channel_close(c);
}
}
channel_unlock(c);
context_unlock(ctx);
loc_free(args);
}
static void command_fill(char * token, Channel * c) {
MemoryCommandArgs * args = read_command_args(token, c, CMD_FILL);
if (args != NULL) post_safe_event(args->ctx, 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> */
write_stream(out, '[');
write_context(out, ctx);
write_stream(out, ']');
write_stream(out, 0);
write_stream(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> */
write_stream(out, '[');
write_context(out, ctx);
write_stream(out, ']');
write_stream(out, 0);
write_stream(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> */
write_stream(out, '[');
json_write_string(out, ctx->id);
write_stream(out, ']');
write_stream(out, 0);
write_stream(out, MARKER_EOM);
}
static void event_context_created(Context * ctx, void * client_data) {
TCFBroadcastGroup * bcg = (TCFBroadcastGroup *)client_data;
if (ctx->mem_access == 0) return;
send_event_context_added(&bcg->out, ctx);
}
static void event_context_changed(Context * ctx, void * client_data) {
TCFBroadcastGroup * bcg = (TCFBroadcastGroup *)client_data;
if (ctx->mem_access == 0) return;
send_event_context_changed(&bcg->out, ctx);
}
static void event_context_exited(Context * ctx, void * client_data) {
TCFBroadcastGroup * bcg = (TCFBroadcastGroup *)client_data;
if (ctx->mem_access == 0) return;
send_event_context_removed(&bcg->out, ctx);
}
void ini_memory_service(Protocol * proto, TCFBroadcastGroup * bcg) {
static ContextEventListener listener = {
event_context_created,
event_context_exited,
NULL,
NULL,
event_context_changed
};
add_context_event_listener(&listener, bcg);
add_command_handler(proto, MEMORY, "getContext", command_get_context);
add_command_handler(proto, MEMORY, "getChildren", command_get_children);
add_command_handler(proto, MEMORY, "set", command_set);
add_command_handler(proto, MEMORY, "get", command_get);
add_command_handler(proto, MEMORY, "fill", command_fill);
}
#endif /* SERVICE_Memory */