This introduces memread, memwrite, pmemread and pmemwrite qmp commands. The memread and memwrite read virtual memory from a given cpu point of view. If no cpu index is specified, the cpu-id 0 is used.
The pmemread and pmemwrite directly read physical memory. The data is passed/returned in a list of bytes. The maximum length is set to 8, they can be used to 64bits. This is based on the work of Frederic Konrad. Signed-off-by: Damien Hedde <damien.he...@greensocs.com> --- cpus.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++ qapi/misc.json | 119 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+) diff --git a/cpus.c b/cpus.c index 1af51b73dd..7aae35c098 100644 --- a/cpus.c +++ b/cpus.c @@ -2484,3 +2484,129 @@ void dump_drift_info(void) qemu_printf("Max guest advance NA\n"); } } + +static Bytes *memread(int64_t addr, int64_t size, CPUState *cpu, Error **errp) +{ + uint32_t l = 0; + uint8List *prev = NULL; + Bytes *res; + uint8_t buf[8]; + + if (size <= 0 || size > sizeof(buf)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", + "out of range"); + return NULL; + } + + if (cpu) { + if (cpu_memory_rw_debug(cpu, addr, buf, size, 0) != 0) { + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRId64 + " specified", addr, size); + return NULL; + } + } else { + MemTxResult r = address_space_read(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, buf, l); + if (r != MEMTX_OK) { + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRId64 + " specified", addr, size); + return NULL; + } + } + + res = g_new0(Bytes, 1); + while (l < size) { + uint8List *cur = g_new0(uint8List, 1); + cur->value = buf[l++]; + if (!prev) { + res->bytes = cur; + } else { + prev->next = cur; + } + prev = cur; + } + + return res; + +} + +Bytes *qmp_memread(int64_t addr, int64_t size, + bool has_cpu, int64_t cpu_index, Error **errp) +{ + CPUState *cpu; + + if (!has_cpu) { + cpu_index = 0; + } + + cpu = qemu_get_cpu(cpu_index); + if (cpu == NULL) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); + return NULL; + } + + return memread(addr, size, cpu, errp); +} + +Bytes *qmp_pmemread(int64_t addr, int64_t size, Error **errp) +{ + return memread(addr, size, NULL, errp); +} + +static void memwrite(int64_t addr, uint8List *bytes, CPUState *cpu, + Error **errp) +{ + uint32_t l = 0; + uint8_t buf[8]; + + while (bytes != NULL) { + if (l >= sizeof(buf)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "bytes", + "too long"); + return; + } + buf[l++] = bytes->value; + bytes = bytes->next; + } + + if (cpu) { + if (cpu_memory_rw_debug(cpu, addr, buf, l, 1) != 0) { + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRIu32 + " specified", addr, l); + return; + } + } else { + MemTxResult r = address_space_write(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, buf, l); + if (r != MEMTX_OK) { + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRId64 + " specified", addr, size); + return; + } + } +} + +void qmp_memwrite(int64_t addr, uint8List *bytes, + bool has_cpu, int64_t cpu_index, Error **errp) +{ + CPUState *cpu; + + if (!has_cpu) { + cpu_index = 0; + } + + cpu = qemu_get_cpu(cpu_index); + if (cpu == NULL) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); + return; + } + + memwrite(addr, bytes, cpu, errp); +} + +void qmp_pmemwrite(int64_t addr, uint8List *bytes, Error **errp) +{ + memwrite(addr, bytes, NULL, errp); +} diff --git a/qapi/misc.json b/qapi/misc.json index dc4cf9da20..3aca91b4ac 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -3047,3 +3047,122 @@ 'data': 'NumaOptions', 'allow-preconfig': true } + +## +# @Bytes: +# +# An array of bytes. +# +# @bytes: the list of bytes +# +# Since: 4.1 +## +{ 'struct': 'Bytes', 'data': {'bytes': ['uint8'] } } + +## +# @memread: +# +# Read a portion of guest memory. +# +# @addr: the virtual address of the guest to read from +# +# @size: the size of memory region to read (max is 8) +# +# @cpu-index: the index of the virtual CPU to use for translating the +# virtual address (defaults to CPU 0) +# +# Returns: The read bytes +# +# Since: 4.1 +# +# Example: +# +# -> { "execute": "memread", +# "arguments": { "addr": 10, +# "size": 4 } } +# <- { "return": { 'bytes' : [10, 78, 231, 7] } } +# +## +{ 'command': 'memread', + 'data': {'addr': 'int', 'size': 'int', '*cpu-index': 'int'}, + 'returns' : 'Bytes' +} + +## +# @memwrite: +# +# Write a portion of guest memory. +# +# @addr: the virtual address of the guest to write to +# +# @bytes: the bytes to write into memory region (max length is 8) +# +# @cpu-index: the index of the virtual CPU to use for translating the +# virtual address (defaults to CPU 0) +# +# Since: 4.1 +# +# Returns: nothing on success. +# +# Example: +# +# -> { "execute": "memread", +# "arguments": { "addr": 10, +# "bytes": [10, 78, 231, 7] } } +# <- { "return": {} } +# +## +{ 'command': 'memwrite', + 'data': {'addr': 'int', 'bytes': ['uint8'], '*cpu-index': 'int'} +} + +## +# @pmemread: +# +# Read a portion of guest memory. +# +# @addr: the physical address of the guest to read from +# +# @size: the size of memory region to read (max is 8) +# +# Returns: The read bytes +# +# Since: 4.1 +# +# Example: +# +# -> { "execute": "memread", +# "arguments": { "addr": 10, +# "size": 4 } } +# <- { "return": { 'bytes' : [10, 78, 231, 7] } } +# +## +{ 'command': 'pmemread', + 'data': {'addr': 'int', 'size': 'int'}, + 'returns' : 'Bytes' +} + +## +# @pmemwrite: +# +# Write a portion of guest memory. +# +# @addr: the physical address of the guest to write to +# +# @bytes: the bytes to write into memory region (max length is 8) +# +# Since: 4.1 +# +# Returns: nothing on success. +# +# Example: +# +# -> { "execute": "memread", +# "arguments": { "addr": 10, +# "bytes": [10, 78, 231, 7] } } +# <- { "return": {} } +# +## +{ 'command': 'pmemwrite', + 'data': {'addr': 'int', 'bytes': ['uint8']} +} -- 2.22.0