From 0a7d3aba93774684ff7f12d7870ada9470ab5026 Mon Sep 17 00:00:00 2001 From: Nelson Ho Date: Tue, 22 Oct 2024 19:56:36 +0000 Subject: [PATCH] fault injection: add QMP memread/memwrite commands Introduce two new QMP commands, memwrite and memread. These interfaces are intended to be used for runtime memory fault injection. Change is based off of Frederic Konrad's previous work. Signed-off-by: Nelson Ho --- qapi/injection.json | 72 +++++++++++++++++++++++++++++++++++++++++++ qapi/meson.build | 1 + qapi/qapi-schema.json | 1 + system/cpus.c | 57 ++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 qapi/injection.json diff --git a/qapi/injection.json b/qapi/injection.json new file mode 100644 index 0000000000..060b5090bd --- /dev/null +++ b/qapi/injection.json @@ -0,0 +1,72 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# Copyright (C) 2024 Wind River Systems +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## +# @InjectionValue: +# +# A single unsigned 64-bit integer. +# +# @value: single unsigned 64-bit integer. +# +# Since: 9.1 +## +{ 'struct': 'InjectionValue', + 'data': { 'value': 'uint64'} } + +## +# @memwrite: +# +# Write a guest memory location from CPU[cpu_id] +# +# @addr: the virtual address to write to +# +# @size: the number of bytes to write at addr (max is 8) +# +# @val: the unsigned 64-bit integer value to write at addr +# +# @cpuid: the index of the virtual CPU to use for translating the +# virtual address +# +# Since: 9.1 +# +## +{ 'command': 'memwrite', + 'data': {'addr': 'uint64', 'size': 'int', 'val': 'uint64', 'cpuid': 'int'} +} + +## +# @memread: +# +# Read a guest memory location from CPU[cpu_id] +# +# @addr: the virtual address to read from +# +# @size: the number of bytes to read at addr (max is 8) +# +# @cpuid: the index of the virtual CPU to use for translating the +# virtual address +# +# Returns: InjectionValue, a structure with the unsigned 64-bit integer value read +# +# Since: 9.1 +# +## +{ 'command': 'memread', + 'data': {'addr': 'uint64', 'size': 'int', 'cpuid': 'int'}, + 'returns' : 'InjectionValue' +} diff --git a/qapi/meson.build b/qapi/meson.build index e7bc54e5d0..089a8e0566 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -35,6 +35,7 @@ qapi_all_modules = [ 'dump', 'ebpf', 'error', + 'injection', 'introspect', 'job', 'machine-common', diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index b1581988e4..d8e922a5f5 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -81,3 +81,4 @@ { 'include': 'vfio.json' } { 'include': 'cryptodev.json' } { 'include': 'cxl.json' } +{ 'include': 'injection.json' } diff --git a/system/cpus.c b/system/cpus.c index 1c818ff682..8bc1741535 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -20,6 +20,8 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. + * + * 22 Oct 2024 - Add QMP memread/memwrite APIs */ #include "qemu/osdep.h" @@ -29,6 +31,8 @@ #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-events-run-state.h" +#include "qapi/qapi-types-injection.h" +#include "qapi/qapi-commands-injection.h" #include "qapi/qmp/qerror.h" #include "exec/gdbstub.h" #include "sysemu/hw_accel.h" @@ -871,6 +875,59 @@ void qmp_pmemsave(uint64_t addr, uint64_t size, const char *filename, fclose(f); } +void qmp_memwrite(uint64_t addr, int64_t size, uint64_t val, + int64_t cpu_id, Error **errp) +{ + CPUState *cpu; + + if (size <= 0 || size > sizeof(uint64_t)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", + "out of range"); + return; + } + + cpu = qemu_get_cpu(cpu_id); + if (cpu == NULL) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); + return; + } + + if (cpu_memory_rw_debug(cpu, addr, (uint64_t *)&val, size, 1)) { + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRIu64 + " specified", addr, size); + } +} + +InjectionValue *qmp_memread(uint64_t addr, int64_t size, + int64_t cpu_id, Error **errp) +{ + InjectionValue *ret = g_new0(InjectionValue, 1); + CPUState *cpu; + + ret->value = 0; + + if (size <= 0 || size > sizeof(uint64_t)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", + "out of range"); + return NULL; + } + + cpu = qemu_get_cpu(cpu_id); + if (cpu == NULL) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); + return NULL; + } + + if (cpu_memory_rw_debug(cpu, addr, &ret->value, size, 0) != 0) { + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRIu64 + " specified", addr, size); + } + + return ret; +} + void qmp_inject_nmi(Error **errp) { nmi_monitor_handle(monitor_get_cpu_index(monitor_cur()), errp);