/******************************************************************************* * 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 #if SERVICE_Memory #include #include #include #include #include #include #include #include #include #include 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); /* */ 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"); /* */ 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"); /* */ 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"); /* */ 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 */