From nobody Sun Nov 24 12:52:18 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=oro.sh ARC-Seal: i=1; a=rsa-sha256; t=1722377994; cv=none; d=zohomail.com; s=zohoarc; b=GET47NDjnQ45r1EKIZtk2+i2emiWnvAh0sOCGP1I4XtLmpUUROxmfm9lrNgpyUiLFN6eK09Skq8BF+gEJOjnJhpwjzMuepF1NzFwVXe/p4YDQshZENO8NPQKvW2SIxAHRIJBspoZHLWi0+Ho4XakoB76CqJKuiMTUlRLng6DmMc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1722377994; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=NrrJLgIKd62quyPWeDHIZKB51onz13MFCNaJHq4eR4w=; b=M7Q6MgNKfF41Mja12+brMisjKD62Fsm1kBMnALuy9SyCYOe2UMxVeqB/c6fKk8sDfFveN05VbYKZzOamSc/jHwdj9y3xBmYORwHODQ1QkYaU+5wT/1ImQmp+SBjR9CbmN/gcz1dL5Z+Im7QWBwJ54Em1pd/5H2lde4uEwV6+jA4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1722377994528540.4137301051519; Tue, 30 Jul 2024 15:19:54 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sYvBA-0002Yx-6A; Tue, 30 Jul 2024 18:18:48 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sYuVU-0007IJ-Ng for qemu-devel@nongnu.org; Tue, 30 Jul 2024 17:35:44 -0400 Received: from out-172.mta1.migadu.com ([2001:41d0:203:375::ac]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sYuVQ-0006ue-OY for qemu-devel@nongnu.org; Tue, 30 Jul 2024 17:35:44 -0400 X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oro.sh; s=key1; t=1722375326; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=NrrJLgIKd62quyPWeDHIZKB51onz13MFCNaJHq4eR4w=; b=b788HJSVgsTaAvmKuVE1XhEYb01PXAkaPF8JFM52QCP34fusySYiI65uDxuYJrZn3NAeUJ ZfEhv0462Pd7awIA5LZy1mvlmHg61X3y6PTQu1IgvsbbgNvp/KTS8SdbqXJD0P0bIT9dI0 Qz5khSdoDON7nmavy95ElC7AkFmzm/k= From: Josh Junon To: qemu-devel@nongnu.org Cc: Josh Junon , "Dr. David Alan Gilbert" , Richard Henderson , Paolo Bonzini , Markus Armbruster , Eric Blake , Eduardo Habkost , Marcel Apfelbaum , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Yanan Wang , Zhao Liu , Peter Xu , David Hildenbrand , Thomas Huth , Laurent Vivier Subject: [PATCH] qmp: Add 'memtranslate' QMP command Date: Tue, 30 Jul 2024 23:34:32 +0200 Message-Id: <20240730213432.18074-1-junon@oro.sh> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2001:41d0:203:375::ac; envelope-from=junon@oro.sh; helo=out-172.mta1.migadu.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Tue, 30 Jul 2024 18:18:45 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @oro.sh) X-ZM-MESSAGEID: 1722377996843116600 Content-Type: text/plain; charset="utf-8" This commit adds a new QMP/HMP command `memtranslate`, which translates a virtual address to a physical address using the guest's MMU. This uses the same mechanism that `[p]memsave` does to perform the translation. This commit also fixes a long standing issue of `[p]memsave` not properly handling higher-half virtual addresses correctly, namely when used over QMP/the monitor. The use and assumption of signed integers caused issues when parsing otherwise valid virtual addresses that instead caused signed integer overflow or ERANGE errors. Signed-off-by: Josh Junon --- docs/devel/loads-stores.rst | 11 ++ hmp-commands.hx | 16 ++- hw/core/machine-hmp-cmds.c | 34 ++++- include/exec/cpu-common.h | 5 + include/monitor/hmp.h | 1 + include/qapi/qmp/qdict.h | 4 + monitor/hmp-expr.inc | 200 ++++++++++++++++++++++++++++++ monitor/hmp.c | 241 ++++++++---------------------------- qapi/machine.json | 42 ++++++- qobject/qdict.c | 38 ++++++ system/cpus.c | 31 ++++- system/physmem.c | 18 +++ tests/qtest/test-hmp.c | 1 + tests/unit/check-qdict.c | 39 ++++++ 14 files changed, 482 insertions(+), 199 deletions(-) create mode 100644 monitor/hmp-expr.inc diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index ec627aa9c0..c2bf4bd015 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -465,6 +465,17 @@ For new code they are better avoided: Regexes for git grep: - ``\`` =20 +``cpu_memory_translate`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +Translates a virtual address to a physical address. + +This function is intended for use by QMP/HMP and similar code. +It takes a virtual address and returns the physical address +as it's seen by the MMU via a lookup, along with other attributes +of the page as well as what occurred during the lookup itself. + + ``cpu_memory_rw_debug`` ~~~~~~~~~~~~~~~~~~~~~~~ =20 diff --git a/hmp-commands.hx b/hmp-commands.hx index 06746f0afc..213279e340 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -796,12 +796,24 @@ SRST Stop capture with a given *index*, index can be obtained with:: =20 info capture +ERST + + { + .name =3D "memtranslate", + .args_type =3D "val:u", + .params =3D "addr", + .help =3D "translate a guest virtual address 'addr' to a phy= sical address", + .cmd =3D hmp_memtranslate, + }, =20 +SRST +``memtranslate`` *addr* + translate a guest virtual address *val* to a physical address ERST =20 { .name =3D "memsave", - .args_type =3D "val:l,size:i,filename:s", + .args_type =3D "val:u,size:u,filename:s", .params =3D "addr size file", .help =3D "save to disk virtual memory dump starting at 'add= r' of size 'size'", .cmd =3D hmp_memsave, @@ -814,7 +826,7 @@ ERST =20 { .name =3D "pmemsave", - .args_type =3D "val:l,size:i,filename:s", + .args_type =3D "val:u,size:u,filename:s", .params =3D "addr size file", .help =3D "save to disk physical memory dump starting at 'ad= dr' of size 'size'", .cmd =3D hmp_pmemsave, diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 8701f00cc7..cf4adfa80a 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -194,11 +194,37 @@ void hmp_system_powerdown(Monitor *mon, const QDict *= qdict) qmp_system_powerdown(NULL); } =20 +void hmp_memtranslate(Monitor *mon, const QDict *qdict) +{ + uint64_t addr =3D qdict_get_uint(qdict, "val"); + Error *err =3D NULL; + int cpu_index =3D monitor_get_cpu_index(mon); + MemTranslation *translation; + + if (cpu_index < 0) { + monitor_printf(mon, "No CPU available\n"); + return; + } + + translation =3D qmp_memtranslate(addr, true, cpu_index, &err); + + if (err =3D=3D NULL) { + if (translation->phys =3D=3D -1) { + monitor_printf(mon, "failed to translate\n"); + } else { + monitor_printf(mon, "phys: 0x%" PRIx64 "\n", translation->phys= ); + } + } + + qapi_free_MemTranslation(translation); + hmp_handle_error(mon, err); +} + void hmp_memsave(Monitor *mon, const QDict *qdict) { - uint32_t size =3D qdict_get_int(qdict, "size"); + uint64_t size =3D qdict_get_uint(qdict, "size"); const char *filename =3D qdict_get_str(qdict, "filename"); - uint64_t addr =3D qdict_get_int(qdict, "val"); + uint64_t addr =3D qdict_get_uint(qdict, "val"); Error *err =3D NULL; int cpu_index =3D monitor_get_cpu_index(mon); =20 @@ -213,9 +239,9 @@ void hmp_memsave(Monitor *mon, const QDict *qdict) =20 void hmp_pmemsave(Monitor *mon, const QDict *qdict) { - uint32_t size =3D qdict_get_int(qdict, "size"); + uint64_t size =3D qdict_get_uint(qdict, "size"); const char *filename =3D qdict_get_str(qdict, "filename"); - uint64_t addr =3D qdict_get_int(qdict, "val"); + uint64_t addr =3D qdict_get_uint(qdict, "val"); Error *err =3D NULL; =20 qmp_pmemsave(addr, size, filename, &err); diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 2e1b499cb7..9484c82fe0 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -178,6 +178,11 @@ int ram_block_discard_guest_memfd_range(RAMBlock *rb, = uint64_t start, =20 #endif =20 +/* Returns: translated physical address on success, -1 on error. + * If the address is not valid, `*attrs` is left untouched. + */ +hwaddr cpu_memory_translate(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); + /* Returns: 0 on success, -1 on error */ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, void *ptr, size_t len, bool is_write); diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index ae116d9804..febccf13cc 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -47,6 +47,7 @@ void hmp_system_powerdown(Monitor *mon, const QDict *qdic= t); void hmp_exit_preconfig(Monitor *mon, const QDict *qdict); void hmp_announce_self(Monitor *mon, const QDict *qdict); void hmp_cpu(Monitor *mon, const QDict *qdict); +void hmp_memtranslate(Monitor *mon, const QDict *qdict); void hmp_memsave(Monitor *mon, const QDict *qdict); void hmp_pmemsave(Monitor *mon, const QDict *qdict); void hmp_ringbuf_write(Monitor *mon, const QDict *qdict); diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index 82e90fc072..38c310becb 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -52,17 +52,21 @@ const QDictEntry *qdict_next(const QDict *qdict, const = QDictEntry *entry); =20 void qdict_put_bool(QDict *qdict, const char *key, bool value); void qdict_put_int(QDict *qdict, const char *key, int64_t value); +void qdict_put_uint(QDict *qdict, const char *key, uint64_t value); void qdict_put_null(QDict *qdict, const char *key); void qdict_put_str(QDict *qdict, const char *key, const char *value); =20 double qdict_get_double(const QDict *qdict, const char *key); int64_t qdict_get_int(const QDict *qdict, const char *key); +uint64_t qdict_get_uint(const QDict *qdict, const char *key); bool qdict_get_bool(const QDict *qdict, const char *key); QList *qdict_get_qlist(const QDict *qdict, const char *key); QDict *qdict_get_qdict(const QDict *qdict, const char *key); const char *qdict_get_str(const QDict *qdict, const char *key); int64_t qdict_get_try_int(const QDict *qdict, const char *key, int64_t def_value); +uint64_t qdict_get_try_uint(const QDict *qdict, const char *key, + uint64_t def_value); bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_valu= e); const char *qdict_get_try_str(const QDict *qdict, const char *key); =20 diff --git a/monitor/hmp-expr.inc b/monitor/hmp-expr.inc new file mode 100644 index 0000000000..789a957ed2 --- /dev/null +++ b/monitor/hmp-expr.inc @@ -0,0 +1,200 @@ +#ifndef HMP_EXPR_INC_TY +#error "missing HMP_EXPR_INC_TY" +#endif + +#ifndef HMP_EXPR_INC_IDENT +#error "missing HMP_EXPR_INC_IDENT" +#endif + +static HMP_EXPR_INC_TY HMP_EXPR_INC_IDENT(expr_sum)(Monitor *mon); + +static HMP_EXPR_INC_TY HMP_EXPR_INC_IDENT(expr_unary)(Monitor *mon) +{ + HMP_EXPR_INC_TY n; + char *p; + int ret; + + switch (*pch) { + case '+': + next(); + n =3D HMP_EXPR_INC_IDENT(expr_unary)(mon); + break; + case '-': + next(); + n =3D -HMP_EXPR_INC_IDENT(expr_unary)(mon); + break; + case '~': + next(); + n =3D ~HMP_EXPR_INC_IDENT(expr_unary)(mon); + break; + case '(': + next(); + n =3D HMP_EXPR_INC_IDENT(expr_sum)(mon); + if (*pch !=3D ')') { + expr_error(mon, "')' expected"); + } + next(); + break; + case '\'': + pch++; + if (*pch =3D=3D '\0') { + expr_error(mon, "character constant expected"); + } + n =3D *pch; + pch++; + if (*pch !=3D '\'') { + expr_error(mon, "missing terminating \' character"); + } + next(); + break; + case '$': + { + char buf[128], *q; + int64_t reg =3D 0; + + pch++; + q =3D buf; + while ((*pch >=3D 'a' && *pch <=3D 'z') || + (*pch >=3D 'A' && *pch <=3D 'Z') || + (*pch >=3D '0' && *pch <=3D '9') || + *pch =3D=3D '_' || *pch =3D=3D '.') { + if ((q - buf) < sizeof(buf) - 1) { + *q++ =3D *pch; + } + pch++; + } + while (qemu_isspace(*pch)) { + pch++; + } + *q =3D 0; + ret =3D get_monitor_def(mon, ®, buf); + if (ret < 0) { + expr_error(mon, "unknown register"); + } + n =3D (HMP_EXPR_INC_TY)reg; + } + break; + case '\0': + expr_error(mon, "unexpected end of expression"); + n =3D 0; + break; + default: + errno =3D 0; + n =3D strtoull(pch, &p, 0); + if (errno =3D=3D ERANGE) { + expr_error(mon, "number too large"); + } + if (pch =3D=3D p) { + expr_error(mon, "invalid char '%c' in expression", *p); + } + pch =3D p; + while (qemu_isspace(*pch)) { + pch++; + } + break; + } + return n; +} + +static HMP_EXPR_INC_TY HMP_EXPR_INC_IDENT(expr_prod)(Monitor *mon) +{ + HMP_EXPR_INC_TY val, val2; + int op; + + val =3D HMP_EXPR_INC_IDENT(expr_unary)(mon); + for (;;) { + op =3D *pch; + if (op !=3D '*' && op !=3D '/' && op !=3D '%') { + break; + } + next(); + val2 =3D HMP_EXPR_INC_IDENT(expr_unary)(mon); + switch (op) { + default: + case '*': + val *=3D val2; + break; + case '/': + case '%': + if (val2 =3D=3D 0) { + expr_error(mon, "division by zero"); + } + if (op =3D=3D '/') { + val /=3D val2; + } else { + val %=3D val2; + } + break; + } + } + return val; +} + +static HMP_EXPR_INC_TY HMP_EXPR_INC_IDENT(expr_logic)(Monitor *mon) +{ + HMP_EXPR_INC_TY val, val2; + int op; + + val =3D HMP_EXPR_INC_IDENT(expr_prod)(mon); + for (;;) { + op =3D *pch; + if (op !=3D '&' && op !=3D '|' && op !=3D '^') { + break; + } + next(); + val2 =3D HMP_EXPR_INC_IDENT(expr_prod)(mon); + switch (op) { + default: + case '&': + val &=3D val2; + break; + case '|': + val |=3D val2; + break; + case '^': + val ^=3D val2; + break; + } + } + return val; +} + +static HMP_EXPR_INC_TY HMP_EXPR_INC_IDENT(expr_sum)(Monitor *mon) +{ + HMP_EXPR_INC_TY val, val2; + int op; + + val =3D HMP_EXPR_INC_IDENT(expr_logic)(mon); + for (;;) { + op =3D *pch; + if (op !=3D '+' && op !=3D '-') { + break; + } + next(); + val2 =3D HMP_EXPR_INC_IDENT(expr_logic)(mon); + if (op =3D=3D '+') { + val +=3D val2; + } else { + val -=3D val2; + } + } + return val; +} + +static int HMP_EXPR_INC_IDENT(get_expr)(Monitor *mon, HMP_EXPR_INC_TY *pva= l, const char **pp) +{ + pch =3D *pp; + if (sigsetjmp(expr_env, 0)) { + *pp =3D pch; + return -1; + } + while (qemu_isspace(*pch)) { + pch++; + } + *pval =3D HMP_EXPR_INC_IDENT(expr_sum)(mon); + *pp =3D pch; + return 0; +} + +#undef HMP_EXPR_INC_TY +#undef HMP_EXPR_INC_IDENT diff --git a/monitor/hmp.c b/monitor/hmp.c index 460e8832f6..95d965a20a 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -332,195 +332,13 @@ static void next(void) } } =20 -static int64_t expr_sum(Monitor *mon); +#define HMP_EXPR_INC_TY int64_t +#define HMP_EXPR_INC_IDENT(name) name ## _int64 +#include "monitor/hmp-expr.inc" =20 -static int64_t expr_unary(Monitor *mon) -{ - int64_t n; - char *p; - int ret; - - switch (*pch) { - case '+': - next(); - n =3D expr_unary(mon); - break; - case '-': - next(); - n =3D -expr_unary(mon); - break; - case '~': - next(); - n =3D ~expr_unary(mon); - break; - case '(': - next(); - n =3D expr_sum(mon); - if (*pch !=3D ')') { - expr_error(mon, "')' expected"); - } - next(); - break; - case '\'': - pch++; - if (*pch =3D=3D '\0') { - expr_error(mon, "character constant expected"); - } - n =3D *pch; - pch++; - if (*pch !=3D '\'') { - expr_error(mon, "missing terminating \' character"); - } - next(); - break; - case '$': - { - char buf[128], *q; - int64_t reg =3D 0; - - pch++; - q =3D buf; - while ((*pch >=3D 'a' && *pch <=3D 'z') || - (*pch >=3D 'A' && *pch <=3D 'Z') || - (*pch >=3D '0' && *pch <=3D '9') || - *pch =3D=3D '_' || *pch =3D=3D '.') { - if ((q - buf) < sizeof(buf) - 1) { - *q++ =3D *pch; - } - pch++; - } - while (qemu_isspace(*pch)) { - pch++; - } - *q =3D 0; - ret =3D get_monitor_def(mon, ®, buf); - if (ret < 0) { - expr_error(mon, "unknown register"); - } - n =3D reg; - } - break; - case '\0': - expr_error(mon, "unexpected end of expression"); - n =3D 0; - break; - default: - errno =3D 0; - n =3D strtoull(pch, &p, 0); - if (errno =3D=3D ERANGE) { - expr_error(mon, "number too large"); - } - if (pch =3D=3D p) { - expr_error(mon, "invalid char '%c' in expression", *p); - } - pch =3D p; - while (qemu_isspace(*pch)) { - pch++; - } - break; - } - return n; -} - -static int64_t expr_prod(Monitor *mon) -{ - int64_t val, val2; - int op; - - val =3D expr_unary(mon); - for (;;) { - op =3D *pch; - if (op !=3D '*' && op !=3D '/' && op !=3D '%') { - break; - } - next(); - val2 =3D expr_unary(mon); - switch (op) { - default: - case '*': - val *=3D val2; - break; - case '/': - case '%': - if (val2 =3D=3D 0) { - expr_error(mon, "division by zero"); - } - if (op =3D=3D '/') { - val /=3D val2; - } else { - val %=3D val2; - } - break; - } - } - return val; -} - -static int64_t expr_logic(Monitor *mon) -{ - int64_t val, val2; - int op; - - val =3D expr_prod(mon); - for (;;) { - op =3D *pch; - if (op !=3D '&' && op !=3D '|' && op !=3D '^') { - break; - } - next(); - val2 =3D expr_prod(mon); - switch (op) { - default: - case '&': - val &=3D val2; - break; - case '|': - val |=3D val2; - break; - case '^': - val ^=3D val2; - break; - } - } - return val; -} - -static int64_t expr_sum(Monitor *mon) -{ - int64_t val, val2; - int op; - - val =3D expr_logic(mon); - for (;;) { - op =3D *pch; - if (op !=3D '+' && op !=3D '-') { - break; - } - next(); - val2 =3D expr_logic(mon); - if (op =3D=3D '+') { - val +=3D val2; - } else { - val -=3D val2; - } - } - return val; -} - -static int get_expr(Monitor *mon, int64_t *pval, const char **pp) -{ - pch =3D *pp; - if (sigsetjmp(expr_env, 0)) { - *pp =3D pch; - return -1; - } - while (qemu_isspace(*pch)) { - pch++; - } - *pval =3D expr_sum(mon); - *pp =3D pch; - return 0; -} +#define HMP_EXPR_INC_TY uint64_t +#define HMP_EXPR_INC_IDENT(name) name ## _uint64 +#include "monitor/hmp-expr.inc" =20 static int get_double(Monitor *mon, double *pval, const char **pp) { @@ -882,7 +700,7 @@ static QDict *monitor_parse_arguments(Monitor *mon, } typestr++; } - if (get_expr(mon, &val, &p)) { + if (get_expr_int64(mon, &val, &p)) { goto fail; } /* Check if 'i' is greater than 32-bit */ @@ -900,6 +718,51 @@ static QDict *monitor_parse_arguments(Monitor *mon, qdict_put_int(qdict, key, val); } break; + case 'd': + case 'u': + case 'm': + { + uint64_t val; + + while (qemu_isspace(*p)) { + p++; + } + if (*typestr =3D=3D '?' || *typestr =3D=3D '.') { + if (*typestr =3D=3D '?') { + if (*p =3D=3D '\0') { + typestr++; + break; + } + } else { + if (*p =3D=3D '.') { + p++; + while (qemu_isspace(*p)) { + p++; + } + } else { + typestr++; + break; + } + } + typestr++; + } + + if (get_expr_uint64(mon, &val, &p)) { + goto fail; + } + + /* Check if 'd' is greater than 32-bit */ + if ((c =3D=3D 'd') && ((val >> 32) & 0xffffffff)) { + monitor_printf(mon, "\'%s\' has failed: ", cmd->name); + monitor_printf(mon, "integer is for 32-bit values\n"); + goto fail; + } else if (c =3D=3D 'm') { + val *=3D MiB; + } + + qdict_put_uint(qdict, key, val); + } + break; case 'o': { int ret; diff --git a/qapi/machine.json b/qapi/machine.json index fcfd249e2d..7c2627c3e2 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -825,6 +825,44 @@ 'policy': 'HmatCacheWritePolicy', 'line': 'uint16' }} =20 +## +# @MemTranslation: +# +# Result of a virtual-to-physical memory translation via @memtranslate. +# +# @phys: the physical address corresponding to the virtual address, +# or -1 if the translation failed +# +# Since: TBD +## +{ 'struct': 'MemTranslation', + 'data': { 'phys': 'uint64' } } + +## +# @memtranslate: +# +# Translate a guest virtual address to a physical address. +# +# @val: the virtual address of the guest to translate +# +# @cpu-index: the index of the virtual CPU to use for translating the +# virtual address (defaults to CPU 0) +# +# Returns: +# @MemTranslation +# +# Since: TBD +# +# .. qmp-example:: +# +# -> { "execute": "memtranslate", +# "arguments": { "val": 10 } } +# <- { "return": { "phys": 20 } } +## +{ 'command': 'memtranslate', + 'data': {'val': 'uint64', '*cpu-index': 'int'}, + 'returns': 'MemTranslation' } + ## # @memsave: # @@ -852,7 +890,7 @@ # <- { "return": {} } ## { 'command': 'memsave', - 'data': {'val': 'int', 'size': 'int', 'filename': 'str', '*cpu-index': '= int'} } + 'data': {'val': 'uint64', 'size': 'uint64', 'filename': 'str', '*cpu-ind= ex': 'int'} } =20 ## # @pmemsave: @@ -878,7 +916,7 @@ # <- { "return": {} } ## { 'command': 'pmemsave', - 'data': {'val': 'int', 'size': 'int', 'filename': 'str'} } + 'data': {'val': 'uint64', 'size': 'uint64', 'filename': 'str'} } =20 ## # @Memdev: diff --git a/qobject/qdict.c b/qobject/qdict.c index 8faff230d3..9696eee57d 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -136,6 +136,11 @@ void qdict_put_int(QDict *qdict, const char *key, int6= 4_t value) qdict_put(qdict, key, qnum_from_int(value)); } =20 +void qdict_put_uint(QDict *qdict, const char *key, uint64_t value) +{ + qdict_put(qdict, key, qnum_from_uint(value)); +} + void qdict_put_bool(QDict *qdict, const char *key, bool value) { qdict_put(qdict, key, qbool_from_bool(value)); @@ -209,6 +214,19 @@ int64_t qdict_get_int(const QDict *qdict, const char *= key) return qnum_get_int(qobject_to(QNum, qdict_get(qdict, key))); } =20 +/** + * qdict_get_int(): Get an unsigned integer mapped by 'key' + * + * This function assumes that 'key' exists and it stores a + * QNum representable as uint. + * + * Return integer mapped by 'key'. + */ +uint64_t qdict_get_uint(const QDict *qdict, const char *key) +{ + return qnum_get_uint(qobject_to(QNum, qdict_get(qdict, key))); +} + /** * qdict_get_bool(): Get a bool mapped by 'key' * @@ -272,6 +290,26 @@ int64_t qdict_get_try_int(const QDict *qdict, const ch= ar *key, return val; } =20 +/** + * qdict_get_try_uint(): Try to get unsigned integer mapped by 'key' + * + * Return integer mapped by 'key', if it is not present in the + * dictionary or if the stored object is not a QNum representing an + * unsigned integer, 'def_value' will be returned. + */ +uint64_t qdict_get_try_uint(const QDict *qdict, const char *key, + uint64_t def_value) +{ + QNum *qnum =3D qobject_to(QNum, qdict_get(qdict, key)); + uint64_t val; + + if (!qnum || !qnum_get_try_uint(qnum, &val)) { + return def_value; + } + + return val; +} + /** * qdict_get_try_bool(): Try to get a bool mapped by 'key' * diff --git a/system/cpus.c b/system/cpus.c index 5e3a988a0a..25d7d7c93f 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -792,7 +792,34 @@ int vm_stop_force_state(RunState state) } } =20 -void qmp_memsave(int64_t addr, int64_t size, const char *filename, +MemTranslation *qmp_memtranslate(uint64_t val, bool has_cpu_index, int64_t= cpu_index, Error **errp) +{ + CPUState *cpu; + hwaddr phys_addr; + MemTxAttrs attrs; + MemTranslation *translation; + + if (!has_cpu_index) { + cpu_index =3D 0; + } + + cpu =3D qemu_get_cpu(cpu_index); + if (cpu =3D=3D NULL) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); + return NULL; + } + + phys_addr =3D cpu_memory_translate(cpu, val, &attrs); + + translation =3D g_new0(MemTranslation, 1); + + translation->phys =3D phys_addr; + + return translation; +} + +void qmp_memsave(uint64_t addr, uint64_t size, const char *filename, bool has_cpu, int64_t cpu_index, Error **errp) { FILE *f; @@ -840,7 +867,7 @@ exit: fclose(f); } =20 -void qmp_pmemsave(int64_t addr, int64_t size, const char *filename, +void qmp_pmemsave(uint64_t addr, uint64_t size, const char *filename, Error **errp) { FILE *f; diff --git a/system/physmem.c b/system/physmem.c index 0e19186e1b..d2fab35e3a 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3524,6 +3524,24 @@ address_space_write_cached_slow(MemoryRegionCache *c= ache, hwaddr addr, #define RCU_READ_UNLOCK() ((void)0) #include "memory_ldst.c.inc" =20 +/* virtual memory translation */ +hwaddr cpu_memory_translate(CPUState *cpu, vaddr addr, MemTxAttrs *attrs) +{ + hwaddr phys_addr, phys_addr_rel; + vaddr page; + + page =3D addr & TARGET_PAGE_MASK; + phys_addr =3D cpu_get_phys_page_attrs_debug(cpu, page, attrs); + + if (phys_addr =3D=3D -1) { + return -1; + } + + phys_addr_rel =3D phys_addr + (addr & ~TARGET_PAGE_MASK); + + return phys_addr_rel; +} + /* virtual memory access for debug (includes writing to ROM) */ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, void *ptr, size_t len, bool is_write) diff --git a/tests/qtest/test-hmp.c b/tests/qtest/test-hmp.c index 1b2e07522f..13c45deb35 100644 --- a/tests/qtest/test-hmp.c +++ b/tests/qtest/test-hmp.c @@ -44,6 +44,7 @@ static const char *hmp_cmds[] =3D { "i /w 0", "log all", "log none", + "memtranslate 0", "memsave 0 4096 \"/dev/null\"", "migrate_set_parameter xbzrle-cache-size 64k", "migrate_set_parameter downtime-limit 1", diff --git a/tests/unit/check-qdict.c b/tests/unit/check-qdict.c index b5efa859b0..09ebe08900 100644 --- a/tests/unit/check-qdict.c +++ b/tests/unit/check-qdict.c @@ -99,6 +99,21 @@ static void qdict_get_int_test(void) qobject_unref(tests_dict); } =20 +static void qdict_get_uint_test(void) +{ + int ret; + const unsigned int value =3D 100; + const char *key =3D "int"; + QDict *tests_dict =3D qdict_new(); + + qdict_put_uint(tests_dict, key, value); + + ret =3D qdict_get_uint(tests_dict, key); + g_assert(ret =3D=3D value); + + qobject_unref(tests_dict); +} + static void qdict_get_try_int_test(void) { int ret; @@ -121,6 +136,28 @@ static void qdict_get_try_int_test(void) qobject_unref(tests_dict); } =20 +static void qdict_get_try_uint_test(void) +{ + int ret; + const unsigned int value =3D 100; + const char *key =3D "int"; + QDict *tests_dict =3D qdict_new(); + + qdict_put_uint(tests_dict, key, value); + qdict_put_str(tests_dict, "string", "test"); + + ret =3D qdict_get_try_uint(tests_dict, key, 0); + g_assert(ret =3D=3D value); + + ret =3D qdict_get_try_uint(tests_dict, "missing", -42); + g_assert_cmpuint(ret, =3D=3D, -42); + + ret =3D qdict_get_try_uint(tests_dict, "string", -42); + g_assert_cmpuint(ret, =3D=3D, -42); + + qobject_unref(tests_dict); +} + static void qdict_get_str_test(void) { const char *p; @@ -358,7 +395,9 @@ int main(int argc, char **argv) /* Continue, but now with fixtures */ g_test_add_func("/public/get", qdict_get_test); g_test_add_func("/public/get_int", qdict_get_int_test); + g_test_add_func("/public/get_uint", qdict_get_uint_test); g_test_add_func("/public/get_try_int", qdict_get_try_int_test); + g_test_add_func("/public/get_try_uint", qdict_get_try_uint_test); g_test_add_func("/public/get_str", qdict_get_str_test); g_test_add_func("/public/get_try_str", qdict_get_try_str_test); g_test_add_func("/public/haskey_not", qdict_haskey_not_test); --=20 2.34.1