All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/18] first version of mcdstub
@ 2023-12-20 16:25 Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 01/18] gdbstub, mcdstub: file and build structure adapted to accomodate for the mcdstub Nicolas Eder
                   ` (17 more replies)
  0 siblings, 18 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

SUMMARY
=======

This patch-set introduces the first version of the mcdstub.
The mcdstub is a debug interface, which enables debugging QEMU
using the MCD (Multi-Core Debug) API.
The mcdstub uses TCP to communicate with the host debug software. However,
because MCD is merely an API, the TCP communication is not part of
the MCD spec but specific to this project.

To translate between the MCD API and the TCP data stream coming from the mcdstub,
the host has to use a shared library (.dll/.so).
Such a shared library is available at: https://gitlab.com/lauterbach/mcdrefsrv
The MCD API itself can be downloaded here: https://repo.lauterbach.com/sprint_mcd_api_v1_0.zip

QUICK START
===========

Attention: MCD is currently only supported for qemu-system-arm !

Three components are required to Debug QEMU via MCD:

1. qemu-system-arm (built with this patch series applied).
2. MCD shared library (translates between the MCD API and TCP data).
3. Host debugging software with support for the MCD API (e.g. Lauterbach TRACE32).

To activate the mcdstub, just use the "mcd" launch option in combination with
a TCP port.

With the default TCP port 1235:

$ qemu-system-arm -M virt -cpu cortex-a15 -mcd default

With a custom TCP port:

$ qemu-system-arm -M virt -cpu cortex-a15 -mcd tcp::1235

QEMU will listen for an MCD host to connect to the specified port.

IMPORTANT CHANGES
=================

1. DebugClass:

This patch-set introduces the DebugClass to the QOM, which is used to abstract
GDB/MCD specific debugger details.
It is declared in include/qemu/debug.h, defined in debug/common/debug.c
It currently only offers one function: set_stop_cpu, which gets called
in cpu_handle_guest_debug in softmmu/cpus.c.
In the future, other functions could be moved from the mcd/gdbstub
to the DebugClass.

2. mcd launch option:

This patch-set introduces the mcd launch option to QEMU. The quick start
section describes how to use it.

3. MCD debugging features:

* Go, break, step
* Read/write memory (user space only)
* Read/write registers (GPR and CP)
* Set breakpoints and watchpoints.

=================

Signed-off-by: Nicolas Eder <nicolas.eder@lauterbach.com>

Nicolas Eder (18):
  gdbstub, mcdstub: file and build structure adapted to accomodate for
    the mcdstub
  gdbstub: hex conversion functions moved to cutils.h
  gdbstub: GDBRegisterState moved to gdbstub.h so it can be used outside
    of the gdbstub
  gdbstub: DebugClass added to system mode.
  mcdstub: memory helper functions added
  mcdstub: -mcd start option added, mcd specific defines added
  mcdstub: mcdserver initialization functions added
  cutils: qemu_strtou32 function added
  mcdstub: TCP packet plumbing added
  mcdstub: open and close server functions added
  mcdstub: system and core queries added
  mcdstub: all core specific queries added
  mcdstub: go, step and break added
  mcdstub: state query added
  mcdstub: skeleton for reset handling added
  mcdstub: register access added
  mcdstub: memory access added
  mcdstub: break/watchpoints added

 MAINTAINERS                              |   11 +-
 debug/common/debug.c                     |   51 +
 debug/common/meson.build                 |    1 +
 {gdbstub => debug/gdbstub}/gdbstub.c     |   27 +-
 {gdbstub => debug/gdbstub}/internals.h   |   26 -
 {gdbstub => debug/gdbstub}/meson.build   |    0
 {gdbstub => debug/gdbstub}/syscalls.c    |    0
 {gdbstub => debug/gdbstub}/system.c      |   18 +
 {gdbstub => debug/gdbstub}/trace-events  |    0
 debug/gdbstub/trace.h                    |    1 +
 {gdbstub => debug/gdbstub}/user-target.c |    0
 {gdbstub => debug/gdbstub}/user.c        |    0
 debug/mcdstub/arm_mcdstub.c              |  289 +++
 debug/mcdstub/mcdstub.c                  | 2481 ++++++++++++++++++++++
 debug/mcdstub/meson.build                |   12 +
 debug/meson.build                        |    3 +
 gdbstub/trace.h                          |    1 -
 include/exec/cpu-common.h                |    3 +
 include/exec/gdbstub.h                   |    8 +
 include/exec/memory.h                    |    9 +
 include/hw/boards.h                      |    1 +
 include/mcdstub/arm_mcdstub.h            |  118 +
 include/mcdstub/mcd_shared_defines.h     |  132 ++
 include/mcdstub/mcdstub.h                |  165 ++
 include/mcdstub/mcdstub_common.h         |   83 +
 include/qemu/cutils.h                    |   32 +
 include/qemu/debug.h                     |   38 +
 include/qemu/typedefs.h                  |    2 +
 meson.build                              |    4 +-
 qemu-options.hx                          |   18 +
 system/cpus.c                            |    9 +-
 system/memory.c                          |   11 +
 system/physmem.c                         |   26 +
 system/vl.c                              |   13 +
 util/cutils.c                            |   30 +
 35 files changed, 3575 insertions(+), 48 deletions(-)
 create mode 100644 debug/common/debug.c
 create mode 100644 debug/common/meson.build
 rename {gdbstub => debug/gdbstub}/gdbstub.c (98%)
 rename {gdbstub => debug/gdbstub}/internals.h (92%)
 rename {gdbstub => debug/gdbstub}/meson.build (100%)
 rename {gdbstub => debug/gdbstub}/syscalls.c (100%)
 rename {gdbstub => debug/gdbstub}/system.c (97%)
 rename {gdbstub => debug/gdbstub}/trace-events (100%)
 create mode 100644 debug/gdbstub/trace.h
 rename {gdbstub => debug/gdbstub}/user-target.c (100%)
 rename {gdbstub => debug/gdbstub}/user.c (100%)
 create mode 100644 debug/mcdstub/arm_mcdstub.c
 create mode 100644 debug/mcdstub/mcdstub.c
 create mode 100644 debug/mcdstub/meson.build
 create mode 100644 debug/meson.build
 delete mode 100644 gdbstub/trace.h
 create mode 100644 include/mcdstub/arm_mcdstub.h
 create mode 100644 include/mcdstub/mcd_shared_defines.h
 create mode 100644 include/mcdstub/mcdstub.h
 create mode 100644 include/mcdstub/mcdstub_common.h
 create mode 100644 include/qemu/debug.h

-- 
2.34.1



^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH v5 01/18] gdbstub, mcdstub: file and build structure adapted to accomodate for the mcdstub
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2024-02-29 15:33   ` Alex Bennée
  2023-12-20 16:25 ` [PATCH v5 02/18] gdbstub: hex conversion functions moved to cutils.h Nicolas Eder
                   ` (16 subsequent siblings)
  17 siblings, 1 reply; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

mcdstub files created including the shared header between the mcd shared library and the mcdstub.
MAINTAINERS file updated
---
 MAINTAINERS                              |  11 +-
 debug/common/debug.c                     |  18 ++++
 debug/common/meson.build                 |   0
 {gdbstub => debug/gdbstub}/gdbstub.c     |   0
 {gdbstub => debug/gdbstub}/internals.h   |   0
 {gdbstub => debug/gdbstub}/meson.build   |   0
 {gdbstub => debug/gdbstub}/syscalls.c    |   0
 {gdbstub => debug/gdbstub}/system.c      |   0
 {gdbstub => debug/gdbstub}/trace-events  |   0
 debug/gdbstub/trace.h                    |   1 +
 {gdbstub => debug/gdbstub}/user-target.c |   0
 {gdbstub => debug/gdbstub}/user.c        |   0
 debug/mcdstub/arm_mcdstub.c              |  18 ++++
 debug/mcdstub/mcdstub.c                  |  18 ++++
 debug/mcdstub/meson.build                |   0
 debug/meson.build                        |   1 +
 gdbstub/trace.h                          |   1 -
 include/mcdstub/arm_mcdstub.h            |  18 ++++
 include/mcdstub/mcd_shared_defines.h     | 132 +++++++++++++++++++++++
 include/mcdstub/mcdstub.h                |  18 ++++
 include/mcdstub/mcdstub_common.h         |  18 ++++
 include/qemu/debug.h                     |  18 ++++
 meson.build                              |   4 +-
 23 files changed, 272 insertions(+), 4 deletions(-)
 create mode 100644 debug/common/debug.c
 create mode 100644 debug/common/meson.build
 rename {gdbstub => debug/gdbstub}/gdbstub.c (100%)
 rename {gdbstub => debug/gdbstub}/internals.h (100%)
 rename {gdbstub => debug/gdbstub}/meson.build (100%)
 rename {gdbstub => debug/gdbstub}/syscalls.c (100%)
 rename {gdbstub => debug/gdbstub}/system.c (100%)
 rename {gdbstub => debug/gdbstub}/trace-events (100%)
 create mode 100644 debug/gdbstub/trace.h
 rename {gdbstub => debug/gdbstub}/user-target.c (100%)
 rename {gdbstub => debug/gdbstub}/user.c (100%)
 create mode 100644 debug/mcdstub/arm_mcdstub.c
 create mode 100644 debug/mcdstub/mcdstub.c
 create mode 100644 debug/mcdstub/meson.build
 create mode 100644 debug/meson.build
 delete mode 100644 gdbstub/trace.h
 create mode 100644 include/mcdstub/arm_mcdstub.h
 create mode 100644 include/mcdstub/mcd_shared_defines.h
 create mode 100644 include/mcdstub/mcdstub.h
 create mode 100644 include/mcdstub/mcdstub_common.h
 create mode 100644 include/qemu/debug.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 695e0bd34f..467da56a38 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2955,7 +2955,7 @@ M: Alex Bennée <alex.bennee@linaro.org>
 R: Philippe Mathieu-Daudé <philmd@linaro.org>
 S: Maintained
 F: docs/system/gdb.rst
-F: gdbstub/*
+F: debug/gdbstub/*
 F: include/exec/gdbstub.h
 F: include/gdbstub/*
 F: gdb-xml/
@@ -2963,6 +2963,15 @@ F: tests/tcg/multiarch/gdbstub/*
 F: scripts/feature_to_c.py
 F: scripts/probe-gdb-support.py
 
+MCD stub
+M: Nicolas Eder <nicolas.eder@lauterbach.com>
+R: Alex Bennée <alex.bennee@linaro.org>
+S: Maintained
+F: debug/mcdstub/*
+F: debug/common/*
+F: include/mcdstub/*
+F: include/qemu/debug.h
+
 Memory API
 M: Paolo Bonzini <pbonzini@redhat.com>
 M: Peter Xu <peterx@redhat.com>
diff --git a/debug/common/debug.c b/debug/common/debug.c
new file mode 100644
index 0000000000..c24aaf1202
--- /dev/null
+++ b/debug/common/debug.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2023 Nicolas Eder
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
diff --git a/debug/common/meson.build b/debug/common/meson.build
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/gdbstub/gdbstub.c b/debug/gdbstub/gdbstub.c
similarity index 100%
rename from gdbstub/gdbstub.c
rename to debug/gdbstub/gdbstub.c
diff --git a/gdbstub/internals.h b/debug/gdbstub/internals.h
similarity index 100%
rename from gdbstub/internals.h
rename to debug/gdbstub/internals.h
diff --git a/gdbstub/meson.build b/debug/gdbstub/meson.build
similarity index 100%
rename from gdbstub/meson.build
rename to debug/gdbstub/meson.build
diff --git a/gdbstub/syscalls.c b/debug/gdbstub/syscalls.c
similarity index 100%
rename from gdbstub/syscalls.c
rename to debug/gdbstub/syscalls.c
diff --git a/gdbstub/system.c b/debug/gdbstub/system.c
similarity index 100%
rename from gdbstub/system.c
rename to debug/gdbstub/system.c
diff --git a/gdbstub/trace-events b/debug/gdbstub/trace-events
similarity index 100%
rename from gdbstub/trace-events
rename to debug/gdbstub/trace-events
diff --git a/debug/gdbstub/trace.h b/debug/gdbstub/trace.h
new file mode 100644
index 0000000000..ca6f0e8d29
--- /dev/null
+++ b/debug/gdbstub/trace.h
@@ -0,0 +1 @@
+#include "trace/trace-debug_gdbstub.h"
diff --git a/gdbstub/user-target.c b/debug/gdbstub/user-target.c
similarity index 100%
rename from gdbstub/user-target.c
rename to debug/gdbstub/user-target.c
diff --git a/gdbstub/user.c b/debug/gdbstub/user.c
similarity index 100%
rename from gdbstub/user.c
rename to debug/gdbstub/user.c
diff --git a/debug/mcdstub/arm_mcdstub.c b/debug/mcdstub/arm_mcdstub.c
new file mode 100644
index 0000000000..c24aaf1202
--- /dev/null
+++ b/debug/mcdstub/arm_mcdstub.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2023 Nicolas Eder
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
new file mode 100644
index 0000000000..c24aaf1202
--- /dev/null
+++ b/debug/mcdstub/mcdstub.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2023 Nicolas Eder
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
diff --git a/debug/mcdstub/meson.build b/debug/mcdstub/meson.build
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/debug/meson.build b/debug/meson.build
new file mode 100644
index 0000000000..a5b093f31e
--- /dev/null
+++ b/debug/meson.build
@@ -0,0 +1 @@
+subdir('gdbstub')
diff --git a/gdbstub/trace.h b/gdbstub/trace.h
deleted file mode 100644
index dee87b1238..0000000000
--- a/gdbstub/trace.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "trace/trace-gdbstub.h"
diff --git a/include/mcdstub/arm_mcdstub.h b/include/mcdstub/arm_mcdstub.h
new file mode 100644
index 0000000000..c24aaf1202
--- /dev/null
+++ b/include/mcdstub/arm_mcdstub.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2023 Nicolas Eder
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
diff --git a/include/mcdstub/mcd_shared_defines.h b/include/mcdstub/mcd_shared_defines.h
new file mode 100644
index 0000000000..b6f2d81c34
--- /dev/null
+++ b/include/mcdstub/mcd_shared_defines.h
@@ -0,0 +1,132 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Lauterbach GmbH, Nicolas Eder
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * 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.
+ */
+
+/*
+ *this file is shared between the mcd dll and the mcd stub.
+ *it has to be kept exectly the same!
+ */
+
+#ifndef MCD_SHARED_DEFINES
+#define MCD_SHARED_DEFINES
+
+/* default tcp port */
+#define MCD_DEFAULT_TCP_PORT "1235"
+
+/* tcp data characters */
+#define TCP_CHAR_OPEN_SERVER 'I'
+#define TCP_CHAR_OPEN_CORE 'i'
+#define TCP_CHAR_GO 'C'
+#define TCP_CHAR_STEP 'c'
+#define TCP_CHAR_BREAK 'b'
+#define TCP_CHAR_QUERY 'q'
+#define TCP_CHAR_CLOSE_SERVER 'D'
+#define TCP_CHAR_CLOSE_CORE 'd'
+#define TCP_CHAR_KILLQEMU 'k'
+#define TCP_CHAR_RESET 'r'
+#define TCP_CHAR_READ_REGISTER 'p'
+#define TCP_CHAR_WRITE_REGISTER 'P'
+#define TCP_CHAR_READ_MEMORY 'm'
+#define TCP_CHAR_WRITE_MEMORY 'M'
+#define TCP_CHAR_BREAKPOINT_INSERT 't'
+#define TCP_CHAR_BREAKPOINT_REMOVE 'T'
+
+/* tcp protocol chars */
+#define TCP_ACKNOWLEDGED '+'
+#define TCP_NOT_ACKNOWLEDGED '-'
+#define TCP_COMMAND_START '$'
+#define TCP_COMMAND_END '#'
+#define TCP_WAS_LAST '|'
+#define TCP_WAS_NOT_LAST '~'
+#define TCP_HANDSHAKE_SUCCESS "shaking your hand"
+#define TCP_EXECUTION_SUCCESS "success"
+#define TCP_EXECUTION_ERROR "error"
+
+/* tcp query arguments */
+#define QUERY_FIRST "f"
+#define QUERY_CONSEQUTIVE "c"
+#define QUERY_END_INDEX "!"
+
+#define QUERY_ARG_SYSTEM "system"
+#define QUERY_ARG_CORES "cores"
+#define QUERY_ARG_RESET "reset"
+#define QUERY_ARG_TRIGGER "trigger"
+#define QUERY_ARG_MEMORY "memory"
+#define QUERY_ARG_REGGROUP "reggroup"
+#define QUERY_ARG_REG "reg"
+#define QUERY_ARG_STATE "state"
+
+/* tcp query packet argument list */
+#define TCP_ARGUMENT_NAME "name"
+#define TCP_ARGUMENT_DATA "data"
+#define TCP_ARGUMENT_ID "id"
+#define TCP_ARGUMENT_TYPE "type"
+#define TCP_ARGUMENT_BITS_PER_MAU "bpm"
+#define TCP_ARGUMENT_INVARIANCE "i"
+#define TCP_ARGUMENT_ENDIAN "e"
+#define TCP_ARGUMENT_MIN "min"
+#define TCP_ARGUMENT_MAX "max"
+#define TCP_ARGUMENT_SUPPORTED_ACCESS_OPTIONS "sao"
+#define TCP_ARGUMENT_REGGROUPID "reggroupid"
+#define TCP_ARGUMENT_MEMSPACEID "memspaceid"
+#define TCP_ARGUMENT_SIZE "size"
+#define TCP_ARGUMENT_THREAD "thread"
+#define TCP_ARGUMENT_ADDRESS "address"
+#define TCP_ARGUMENT_STOP_STRING "stop_str"
+#define TCP_ARGUMENT_INFO_STRING "info_str"
+#define TCP_ARGUMENT_STATE "state"
+#define TCP_ARGUMENT_EVENT "event"
+#define TCP_ARGUMENT_DEVICE "device"
+#define TCP_ARGUMENT_CORE "core"
+#define TCP_ARGUMENT_AMOUNT_CORE "nr_cores"
+#define TCP_ARGUMENT_AMOUNT_TRIGGER "nr_trigger"
+#define TCP_ARGUMENT_OPTION "option"
+#define TCP_ARGUMENT_ACTION "action"
+#define TCP_ARGUMENT_OPCODE "opcode"
+
+/* for packets sent to qemu */
+#define ARGUMENT_SEPARATOR ';'
+#define NEGATIVE_FLAG 0
+#define POSITIVE_FLAG 1
+
+/* core states */
+#define CORE_STATE_RUNNING "running"
+#define CORE_STATE_HALTED "halted"
+#define CORE_STATE_DEBUG "debug"
+#define CORE_STATE_UNKNOWN "unknown"
+
+/* breakpoint types */
+#define MCD_BREAKPOINT_HW 1
+#define MCD_BREAKPOINT_READ 2
+#define MCD_BREAKPOINT_WRITE 3
+#define MCD_BREAKPOINT_RW 4
+
+/* trigger data */
+#define MCD_TRIG_ACT_BREAK "check_data_value"
+#define MCD_TRIG_OPT_VALUE "break_on_trigger"
+
+/* register mem space key words */
+#define MCD_GRP_KEYWORD "GPR"
+#define MCD_CP_KEYWORD "CP"
+
+#endif
diff --git a/include/mcdstub/mcdstub.h b/include/mcdstub/mcdstub.h
new file mode 100644
index 0000000000..c24aaf1202
--- /dev/null
+++ b/include/mcdstub/mcdstub.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2023 Nicolas Eder
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
diff --git a/include/mcdstub/mcdstub_common.h b/include/mcdstub/mcdstub_common.h
new file mode 100644
index 0000000000..c24aaf1202
--- /dev/null
+++ b/include/mcdstub/mcdstub_common.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2023 Nicolas Eder
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
diff --git a/include/qemu/debug.h b/include/qemu/debug.h
new file mode 100644
index 0000000000..c24aaf1202
--- /dev/null
+++ b/include/qemu/debug.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2023 Nicolas Eder
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
diff --git a/meson.build b/meson.build
index d2c4c2adb3..2e1a7048e9 100644
--- a/meson.build
+++ b/meson.build
@@ -3263,7 +3263,7 @@ trace_events_subdirs = [
   'qom',
   'monitor',
   'util',
-  'gdbstub',
+  'debug/gdbstub',
 ]
 if have_linux_user
   trace_events_subdirs += [ 'linux-user' ]
@@ -3389,7 +3389,7 @@ subdir('authz')
 subdir('crypto')
 subdir('ui')
 subdir('hw')
-subdir('gdbstub')
+subdir('debug')
 
 if enable_modules
   libmodulecommon = static_library('module-common', files('module-common.c') + genh, pic: true, c_args: '-DBUILD_DSO')
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 02/18] gdbstub: hex conversion functions moved to cutils.h
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 01/18] gdbstub, mcdstub: file and build structure adapted to accomodate for the mcdstub Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2024-02-29 15:33   ` Alex Bennée
  2023-12-20 16:25 ` [PATCH v5 03/18] gdbstub: GDBRegisterState moved to gdbstub.h so it can be used outside of the gdbstub Nicolas Eder
                   ` (15 subsequent siblings)
  17 siblings, 1 reply; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/gdbstub/gdbstub.c   | 19 ++++++++++---------
 debug/gdbstub/internals.h | 26 --------------------------
 include/qemu/cutils.h     | 30 ++++++++++++++++++++++++++++++
 3 files changed, 40 insertions(+), 35 deletions(-)

diff --git a/debug/gdbstub/gdbstub.c b/debug/gdbstub/gdbstub.c
index 46d752bbc2..f43d4355c0 100644
--- a/debug/gdbstub/gdbstub.c
+++ b/debug/gdbstub/gdbstub.c
@@ -80,8 +80,8 @@ void gdb_memtohex(GString *buf, const uint8_t *mem, int len)
     int i, c;
     for(i = 0; i < len; i++) {
         c = mem[i];
-        g_string_append_c(buf, tohex(c >> 4));
-        g_string_append_c(buf, tohex(c & 0xf));
+        g_string_append_c(buf, nibble_to_hexchar(c >> 4));
+        g_string_append_c(buf, nibble_to_hexchar(c & 0xf));
     }
     g_string_append_c(buf, '\0');
 }
@@ -91,7 +91,8 @@ void gdb_hextomem(GByteArray *mem, const char *buf, int len)
     int i;
 
     for(i = 0; i < len; i++) {
-        guint8 byte = fromhex(buf[0]) << 4 | fromhex(buf[1]);
+        guint8 byte = hexchar_to_nibble(buf[0]) << 4 |
+                      hexchar_to_nibble(buf[1]);
         g_byte_array_append(mem, &byte, 1);
         buf += 2;
     }
@@ -118,8 +119,8 @@ static void hexdump(const char *buf, int len,
         if (i < len) {
             char value = buf[i];
 
-            line_buffer[hex_col + 0] = tohex((value >> 4) & 0xF);
-            line_buffer[hex_col + 1] = tohex((value >> 0) & 0xF);
+            line_buffer[hex_col + 0] = nibble_to_hexchar((value >> 4) & 0xF);
+            line_buffer[hex_col + 1] = nibble_to_hexchar((value >> 0) & 0xF);
             line_buffer[txt_col + 0] = (value >= ' ' && value < 127)
                     ? value
                     : '.';
@@ -151,8 +152,8 @@ int gdb_put_packet_binary(const char *buf, int len, bool dump)
             csum += buf[i];
         }
         footer[0] = '#';
-        footer[1] = tohex((csum >> 4) & 0xf);
-        footer[2] = tohex((csum) & 0xf);
+        footer[1] = nibble_to_hexchar((csum >> 4) & 0xf);
+        footer[2] = nibble_to_hexchar((csum) & 0xf);
         g_byte_array_append(gdbserver_state.last_packet, footer, 3);
 
         gdb_put_buffer(gdbserver_state.last_packet->data,
@@ -2267,7 +2268,7 @@ void gdb_read_byte(uint8_t ch)
                 break;
             }
             gdbserver_state.line_buf[gdbserver_state.line_buf_index] = '\0';
-            gdbserver_state.line_csum = fromhex(ch) << 4;
+            gdbserver_state.line_csum = hexchar_to_nibble(ch) << 4;
             gdbserver_state.state = RS_CHKSUM2;
             break;
         case RS_CHKSUM2:
@@ -2277,7 +2278,7 @@ void gdb_read_byte(uint8_t ch)
                 gdbserver_state.state = RS_GETLINE;
                 break;
             }
-            gdbserver_state.line_csum |= fromhex(ch);
+            gdbserver_state.line_csum |= hexchar_to_nibble(ch);
 
             if (gdbserver_state.line_csum != (gdbserver_state.line_sum & 0xff)) {
                 trace_gdbstub_err_checksum_incorrect(gdbserver_state.line_sum, gdbserver_state.line_csum);
diff --git a/debug/gdbstub/internals.h b/debug/gdbstub/internals.h
index 5c0c725e54..4b67adfeda 100644
--- a/debug/gdbstub/internals.h
+++ b/debug/gdbstub/internals.h
@@ -75,32 +75,6 @@ typedef struct GDBState {
 /* lives in main gdbstub.c */
 extern GDBState gdbserver_state;
 
-/*
- * Inline utility function, convert from int to hex and back
- */
-
-static inline int fromhex(int v)
-{
-    if (v >= '0' && v <= '9') {
-        return v - '0';
-    } else if (v >= 'A' && v <= 'F') {
-        return v - 'A' + 10;
-    } else if (v >= 'a' && v <= 'f') {
-        return v - 'a' + 10;
-    } else {
-        return 0;
-    }
-}
-
-static inline int tohex(int v)
-{
-    if (v < 10) {
-        return v + '0';
-    } else {
-        return v - 10 + 'a';
-    }
-}
-
 /*
  * Connection helpers for both system and user backends
  */
diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h
index 92c927a6a3..5ab1a4ffb0 100644
--- a/include/qemu/cutils.h
+++ b/include/qemu/cutils.h
@@ -267,4 +267,34 @@ void qemu_hexdump_line(char *line, unsigned int b, const void *bufptr,
 void qemu_hexdump(FILE *fp, const char *prefix,
                   const void *bufptr, size_t size);
 
+
+/**
+ * hexchar_to_nibble() - Converts hex character to nibble.
+ */
+static inline int hexchar_to_nibble(int v)
+{
+    if (v >= '0' && v <= '9') {
+        return v - '0';
+    } else if (v >= 'A' && v <= 'F') {
+        return v - 'A' + 10;
+    } else if (v >= 'a' && v <= 'f') {
+        return v - 'a' + 10;
+    } else {
+        g_assert_not_reached();
+    }
+}
+
+/**
+ * nibble_to_hexchar() - Converts nibble to hex character.
+ */
+static inline int nibble_to_hexchar(int v)
+{
+    g_assert(v <= 0xf);
+    if (v < 10) {
+        return v + '0';
+    } else {
+        return v - 10 + 'a';
+    }
+}
+
 #endif
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 03/18] gdbstub: GDBRegisterState moved to gdbstub.h so it can be used outside of the gdbstub
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 01/18] gdbstub, mcdstub: file and build structure adapted to accomodate for the mcdstub Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 02/18] gdbstub: hex conversion functions moved to cutils.h Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2024-02-29 15:23   ` Alex Bennée
  2023-12-20 16:25 ` [PATCH v5 04/18] gdbstub: DebugClass added to system mode Nicolas Eder
                   ` (14 subsequent siblings)
  17 siblings, 1 reply; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/gdbstub/gdbstub.c | 8 --------
 include/exec/gdbstub.h  | 8 ++++++++
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/debug/gdbstub/gdbstub.c b/debug/gdbstub/gdbstub.c
index f43d4355c0..5df7841878 100644
--- a/debug/gdbstub/gdbstub.c
+++ b/debug/gdbstub/gdbstub.c
@@ -45,14 +45,6 @@
 
 #include "internals.h"
 
-typedef struct GDBRegisterState {
-    int base_reg;
-    int num_regs;
-    gdb_get_reg_cb get_reg;
-    gdb_set_reg_cb set_reg;
-    const char *xml;
-} GDBRegisterState;
-
 GDBState gdbserver_state;
 
 void gdb_init_gdbserver_state(void)
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index d8a3c56fa2..cdbad65930 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -27,6 +27,14 @@ typedef struct GDBFeatureBuilder {
 typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg);
 typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg);
 
+typedef struct GDBRegisterState {
+    int base_reg;
+    int num_regs;
+    gdb_get_reg_cb get_reg;
+    gdb_set_reg_cb set_reg;
+    const char *xml;
+} GDBRegisterState;
+
 /**
  * gdb_register_coprocessor() - register a supplemental set of registers
  * @cpu - the CPU associated with registers
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 04/18] gdbstub: DebugClass added to system mode.
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (2 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 03/18] gdbstub: GDBRegisterState moved to gdbstub.h so it can be used outside of the gdbstub Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2024-02-29 16:35   ` Alex Bennée
  2023-12-20 16:25 ` [PATCH v5 05/18] mcdstub: memory helper functions added Nicolas Eder
                   ` (13 subsequent siblings)
  17 siblings, 1 reply; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

This class is used to abstract debug features between different debuggers
---
 debug/common/debug.c     | 33 +++++++++++++++++++++++++++++++++
 debug/common/meson.build |  1 +
 debug/gdbstub/system.c   | 18 ++++++++++++++++++
 debug/meson.build        |  1 +
 include/hw/boards.h      |  1 +
 include/qemu/debug.h     | 20 ++++++++++++++++++++
 include/qemu/typedefs.h  |  2 ++
 system/cpus.c            |  9 ++++++++-
 8 files changed, 84 insertions(+), 1 deletion(-)

diff --git a/debug/common/debug.c b/debug/common/debug.c
index c24aaf1202..476c969c98 100644
--- a/debug/common/debug.c
+++ b/debug/common/debug.c
@@ -16,3 +16,36 @@
  *
  * SPDX-License-Identifier: LGPL-2.0+
  */
+
+#include "qemu/osdep.h"
+#include "qemu/debug.h"
+#include "qom/object_interfaces.h"
+
+static void debug_instance_init(Object *obj)
+{
+}
+
+static void debug_finalize(Object *obj)
+{
+}
+
+static void debug_class_init(ObjectClass *oc, void *data)
+{
+}
+
+static const TypeInfo debug_info = {
+    .name = TYPE_DEBUG,
+    .parent = TYPE_OBJECT,
+    .instance_size = sizeof(DebugState),
+    .instance_init = debug_instance_init,
+    .instance_finalize = debug_finalize,
+    .class_size = sizeof(DebugClass),
+    .class_init = debug_class_init
+};
+
+static void debug_register_types(void)
+{
+    type_register_static(&debug_info);
+}
+
+type_init(debug_register_types);
diff --git a/debug/common/meson.build b/debug/common/meson.build
index e69de29bb2..815cb6f0fc 100644
--- a/debug/common/meson.build
+++ b/debug/common/meson.build
@@ -0,0 +1 @@
+system_ss.add(files('debug.c'))
diff --git a/debug/gdbstub/system.c b/debug/gdbstub/system.c
index 83fd452800..06bc580147 100644
--- a/debug/gdbstub/system.c
+++ b/debug/gdbstub/system.c
@@ -14,6 +14,7 @@
 #include "qapi/error.h"
 #include "qemu/error-report.h"
 #include "qemu/cutils.h"
+#include "qemu/debug.h"
 #include "exec/gdbstub.h"
 #include "gdbstub/syscalls.h"
 #include "exec/hwaddr.h"
@@ -46,6 +47,20 @@ static void reset_gdbserver_state(void)
     gdbserver_state.allow_stop_reply = false;
 }
 
+/**
+ * gdb_init_debug_class() - initialize gdb-specific DebugClass
+ */
+static void gdb_init_debug_class(void)
+{
+    Object *obj;
+    obj = object_new(TYPE_DEBUG);
+    DebugState *ds = DEBUG(obj);
+    DebugClass *dc = DEBUG_GET_CLASS(ds);
+    dc->set_stop_cpu = gdb_set_stop_cpu;
+    MachineState *ms = MACHINE(qdev_get_machine());
+    ms->debug_state = ds;
+}
+
 /*
  * Return the GDB index for a given vCPU state.
  *
@@ -405,6 +420,9 @@ int gdbserver_start(const char *device)
     gdbserver_system_state.mon_chr = mon_chr;
     gdb_syscall_reset();
 
+    /* create new debug object */
+    gdb_init_debug_class();
+
     return 0;
 }
 
diff --git a/debug/meson.build b/debug/meson.build
index a5b093f31e..f46ab14af9 100644
--- a/debug/meson.build
+++ b/debug/meson.build
@@ -1 +1,2 @@
+subdir('common')
 subdir('gdbstub')
diff --git a/include/hw/boards.h b/include/hw/boards.h
index da85f86efb..2e28913afc 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -400,6 +400,7 @@ struct MachineState {
     CpuTopology smp;
     struct NVDIMMState *nvdimms_state;
     struct NumaState *numa_state;
+    DebugState *debug_state;
 };
 
 #define DEFINE_MACHINE(namestr, machine_initfn) \
diff --git a/include/qemu/debug.h b/include/qemu/debug.h
index c24aaf1202..9526f9fb48 100644
--- a/include/qemu/debug.h
+++ b/include/qemu/debug.h
@@ -16,3 +16,23 @@
  *
  * SPDX-License-Identifier: LGPL-2.0+
  */
+
+#ifndef QEMU_DEBUG_H
+#define QEMU_DEBUG_H
+
+#include "qom/object.h"
+#include "qemu/typedefs.h"
+
+struct DebugClass {
+    ObjectClass parent_class;
+    void (*set_stop_cpu)(CPUState *cpu);
+};
+
+struct DebugState {
+    Object parent_obj;
+};
+
+#define TYPE_DEBUG "debug"
+OBJECT_DECLARE_TYPE(DebugState, DebugClass, DEBUG)
+
+#endif /* QEMU_DEBUG_H */
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index 5abdbc3874..e48b544173 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -46,6 +46,8 @@ typedef struct CpuInfoFast CpuInfoFast;
 typedef struct CPUJumpCache CPUJumpCache;
 typedef struct CPUState CPUState;
 typedef struct CPUTLBEntryFull CPUTLBEntryFull;
+typedef struct DebugClass DebugClass;
+typedef struct DebugState DebugState;
 typedef struct DeviceListener DeviceListener;
 typedef struct DeviceState DeviceState;
 typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot;
diff --git a/system/cpus.c b/system/cpus.c
index a444a747f0..7a7fff14bc 100644
--- a/system/cpus.c
+++ b/system/cpus.c
@@ -25,6 +25,7 @@
 #include "qemu/osdep.h"
 #include "monitor/monitor.h"
 #include "qemu/coroutine-tls.h"
+#include "qemu/debug.h"
 #include "qapi/error.h"
 #include "qapi/qapi-commands-machine.h"
 #include "qapi/qapi-commands-misc.h"
@@ -313,7 +314,13 @@ void cpu_handle_guest_debug(CPUState *cpu)
             cpu_single_step(cpu, 0);
         }
     } else {
-        gdb_set_stop_cpu(cpu);
+        MachineState *ms = MACHINE(qdev_get_machine());
+        DebugState *ds = ms->debug_state;
+        DebugClass *dc = DEBUG_GET_CLASS(ds);
+
+        if (dc->set_stop_cpu) {
+            dc->set_stop_cpu(cpu);
+        }
         qemu_system_debug_request();
         cpu->stopped = true;
     }
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 05/18] mcdstub: memory helper functions added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (3 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 04/18] gdbstub: DebugClass added to system mode Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2024-02-29 16:56   ` Alex Bennée
  2023-12-20 16:25 ` [PATCH v5 06/18] mcdstub: -mcd start option added, mcd specific defines added Nicolas Eder
                   ` (12 subsequent siblings)
  17 siblings, 1 reply; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 include/exec/cpu-common.h |  3 +++
 include/exec/memory.h     |  9 +++++++++
 system/memory.c           | 11 +++++++++++
 system/physmem.c          | 26 ++++++++++++++++++++++++++
 4 files changed, 49 insertions(+)

diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
index 41115d8919..dd989b5ab2 100644
--- a/include/exec/cpu-common.h
+++ b/include/exec/cpu-common.h
@@ -182,6 +182,9 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length);
 int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
                         void *ptr, size_t len, bool is_write);
 
+int cpu_memory_get_physical_address(CPUState *cpu, vaddr *addr, size_t *len);
+
+
 /* vl.c */
 void list_cpus(void);
 
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 831f7c996d..174de807d5 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -3142,6 +3142,15 @@ bool ram_block_discard_is_disabled(void);
  */
 bool ram_block_discard_is_required(void);
 
+/*
+ * mcd_find_address_space() - Find the address spaces with the corresponding
+ * name.
+ *
+ * Currently only used by the mcd debugger.
+ * @as_name: Name to look for.
+ */
+AddressSpace *mcd_find_address_space(const char *as_name);
+
 #endif
 
 #endif
diff --git a/system/memory.c b/system/memory.c
index 798b6c0a17..9a8fa79e0c 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -3562,6 +3562,17 @@ void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled)
     }
 }
 
+AddressSpace *mcd_find_address_space(const char *as_name)
+{
+    AddressSpace *as;
+    QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+        if (strcmp(as->name, as_name) == 0) {
+            return as;
+        }
+    }
+    return NULL;
+}
+
 void memory_region_init_ram(MemoryRegion *mr,
                             Object *owner,
                             const char *name,
diff --git a/system/physmem.c b/system/physmem.c
index a63853a7bc..70733c67c7 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -3422,6 +3422,32 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
     return 0;
 }
 
+int cpu_memory_get_physical_address(CPUState *cpu, vaddr *addr, size_t *len)
+{
+    hwaddr phys_addr;
+    vaddr l, page;
+
+    cpu_synchronize_state(cpu);
+    MemTxAttrs attrs;
+
+    page = *addr & TARGET_PAGE_MASK;
+    phys_addr = cpu_get_phys_page_attrs_debug(cpu, page, &attrs);
+    /* if no physical page mapped, return an error */
+    if (phys_addr == -1) {
+        return -1;
+    }
+    l = (page + TARGET_PAGE_SIZE) - *addr;
+    if (l > *len) {
+        l = *len;
+    }
+    phys_addr += (*addr & ~TARGET_PAGE_MASK);
+
+    /* set output values */
+    *addr = phys_addr;
+    *len = l;
+    return 0;
+}
+
 /*
  * Allows code that needs to deal with migration bitmaps etc to still be built
  * target independent.
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 06/18] mcdstub: -mcd start option added, mcd specific defines added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (4 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 05/18] mcdstub: memory helper functions added Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 07/18] mcdstub: mcdserver initialization functions added Nicolas Eder
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/mcdstub.c          | 203 +++++++++++++++++++++++++++++++
 debug/mcdstub/meson.build        |  12 ++
 debug/meson.build                |   1 +
 include/mcdstub/mcdstub.h        | 152 +++++++++++++++++++++++
 include/mcdstub/mcdstub_common.h |  46 +++++++
 qemu-options.hx                  |  18 +++
 system/vl.c                      |  13 ++
 7 files changed, 445 insertions(+)

diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index c24aaf1202..4d8d5d956a 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -16,3 +16,206 @@
  *
  * SPDX-License-Identifier: LGPL-2.0+
  */
+
+#include "qemu/osdep.h"
+#include "qemu/ctype.h"
+#include "qemu/cutils.h"
+#include "qemu/module.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "sysemu/cpus.h"
+#include "sysemu/hw_accel.h"
+#include "sysemu/runstate.h"
+
+#include "mcdstub/mcd_shared_defines.h"
+#include "mcdstub/mcdstub.h"
+
+typedef struct {
+    CharBackend chr;
+} MCDSystemState;
+
+MCDSystemState mcdserver_system_state;
+
+MCDState mcdserver_state;
+
+/**
+ * mcd_supports_guest_debug() - Returns true if debugging the selected
+ * accelerator is supported.
+ */
+static bool mcd_supports_guest_debug(void)
+{
+    const AccelOpsClass *ops = cpus_get_accel();
+    if (ops->supports_guest_debug) {
+        return ops->supports_guest_debug();
+    }
+    return false;
+}
+
+#ifndef _WIN32
+static void mcd_sigterm_handler(int signal)
+{
+    if (runstate_is_running()) {
+        vm_stop(RUN_STATE_PAUSED);
+    }
+}
+#endif
+
+/**
+ * mcd_vm_state_change() - Handles a state change of the QEMU VM.
+ *
+ * This function is called when the QEMU VM goes through a state transition.
+ * It stores the runstate the CPU is in to the cpu_state and when in
+ * RUN_STATE_DEBUG it collects additional data on what watchpoint was hit.
+ * This function also resets the singlestep behavior.
+ * @running: True if he VM is running.
+ * @state: The new (and active) VM run state.
+ */
+static void mcd_vm_state_change(void *opaque, bool running, RunState state)
+{
+}
+
+/**
+ * mcd_chr_can_receive() - Returns the maximum packet length of a TCP packet.
+ */
+static int mcd_chr_can_receive(void *opaque)
+{
+    return MAX_PACKET_LENGTH;
+}
+
+/**
+ * mcd_chr_receive() - Handles receiving a TCP packet.
+ *
+ * This function gets called by QEMU when a TCP packet is received.
+ * It iterates over that packet an calls :c:func:`mcd_read_byte` for each char
+ * of the packet.
+ * @buf: Content of the packet.
+ * @size: Length of the packet.
+ */
+static void mcd_chr_receive(void *opaque, const uint8_t *buf, int size)
+{
+}
+
+/**
+ * mcd_chr_event() - Handles a TCP client connect.
+ *
+ * This function gets called by QEMU when a TCP cliet connects to the opened
+ * TCP port. It attaches the first process. From here on TCP packets can be
+ * exchanged.
+ * @event: Type of event.
+ */
+static void mcd_chr_event(void *opaque, QEMUChrEvent event)
+{
+}
+
+/**
+ * mcd_init_mcdserver_state() - Initializes the mcdserver_state struct.
+ *
+ * This function allocates memory for the mcdserver_state struct and sets
+ * all of its members to their inital values. This includes setting the
+ * cpu_state to halted and initializing the query functions with
+ * :c:func:`init_query_cmds_table`.
+ */
+static void mcd_init_mcdserver_state(void)
+{
+}
+
+/**
+ * reset_mcdserver_state() - Resets the mcdserver_state struct.
+ *
+ * This function deletes all processes connected to the mcdserver_state.
+ */
+static void reset_mcdserver_state(void)
+{
+}
+
+/**
+ * create_processes() - Sorts all processes and calls
+ * :c:func:`mcd_create_default_process`.
+ *
+ * This function sorts all connected processes with the qsort function.
+ * Afterwards, it creates a new process with
+ * :c:func:`mcd_create_default_process`.
+ * @s: A MCDState object.
+ */
+static void create_processes(MCDState *s)
+{
+}
+
+int mcdserver_start(const char *device)
+{
+    char mcd_device_config[TCP_CONFIG_STRING_LENGTH];
+    char mcd_tcp_port[TCP_CONFIG_STRING_LENGTH];
+    Chardev *chr = NULL;
+
+    if (!first_cpu) {
+        error_report("mcdstub: meaningless to attach to a "
+                     "machine without any CPU.");
+        return -1;
+    }
+
+    if (!mcd_supports_guest_debug()) {
+        error_report("mcdstub: current accelerator doesn't "
+                     "support guest debugging");
+        return -1;
+    }
+
+    if (!device) {
+        return -1;
+    }
+
+    /* if device == default -> set tcp_port = tcp::<MCD_DEFAULT_TCP_PORT> */
+    if (strcmp(device, "default") == 0) {
+        snprintf(mcd_tcp_port, sizeof(mcd_tcp_port), "tcp::%s",
+            MCD_DEFAULT_TCP_PORT);
+        device = mcd_tcp_port;
+    }
+
+    if (strcmp(device, "none") != 0) {
+        if (strstart(device, "tcp:", NULL)) {
+            /* enforce required TCP attributes */
+            if (snprintf(mcd_device_config, sizeof(mcd_device_config),
+                     "%s,wait=off,nodelay=on,server=on", device) < 0) {
+                g_assert_not_reached();
+            }
+            device = mcd_device_config;
+        }
+#ifndef _WIN32
+        else if (strcmp(device, "stdio") == 0) {
+            struct sigaction act;
+
+            memset(&act, 0, sizeof(act));
+            act.sa_handler = mcd_sigterm_handler;
+            sigaction(SIGINT, &act, NULL);
+            strcpy(mcd_device_config, device);
+        }
+#endif
+        chr = qemu_chr_new_noreplay("mcd", device, true, NULL);
+        if (!chr) {
+            return -1;
+        }
+    }
+
+    if (!mcdserver_state.init) {
+        mcd_init_mcdserver_state();
+
+        qemu_add_vm_change_state_handler(mcd_vm_state_change, NULL);
+    } else {
+        qemu_chr_fe_deinit(&mcdserver_system_state.chr, true);
+        reset_mcdserver_state();
+    }
+
+    create_processes(&mcdserver_state);
+
+    if (chr) {
+        qemu_chr_fe_init(&mcdserver_system_state.chr, chr, &error_abort);
+        qemu_chr_fe_set_handlers(&mcdserver_system_state.chr,
+                                 mcd_chr_can_receive,
+                                 mcd_chr_receive, mcd_chr_event,
+                                 NULL, &mcdserver_state, NULL, true);
+    }
+    mcdserver_state.state = chr ? RS_IDLE : RS_INACTIVE;
+
+    return 0;
+}
diff --git a/debug/mcdstub/meson.build b/debug/mcdstub/meson.build
index e69de29bb2..7e5ae878b0 100644
--- a/debug/mcdstub/meson.build
+++ b/debug/mcdstub/meson.build
@@ -0,0 +1,12 @@
+# only system emulation is supported over mcd
+mcd_system_ss = ss.source_set()
+mcd_system_ss.add(files('mcdstub.c'))
+mcd_system_ss = mcd_system_ss.apply(config_host, strict: false)
+
+libmcd_system = static_library('mcd_system',
+                                mcd_system_ss.sources() + genh,
+                                name_suffix: 'fa',
+                                build_by_default: have_system)
+
+mcd_system = declare_dependency(link_whole: libmcd_system)
+system_ss.add(mcd_system)
diff --git a/debug/meson.build b/debug/meson.build
index f46ab14af9..97c80d7406 100644
--- a/debug/meson.build
+++ b/debug/meson.build
@@ -1,2 +1,3 @@
 subdir('common')
 subdir('gdbstub')
+subdir('mcdstub')
diff --git a/include/mcdstub/mcdstub.h b/include/mcdstub/mcdstub.h
index c24aaf1202..26aa33c0e3 100644
--- a/include/mcdstub/mcdstub.h
+++ b/include/mcdstub/mcdstub.h
@@ -16,3 +16,155 @@
  *
  * SPDX-License-Identifier: LGPL-2.0+
  */
+
+#ifndef MCDSTUB_H
+#define MCDSTUB_H
+
+#include "mcdstub_common.h"
+
+#define MAX_PACKET_LENGTH 1024
+
+/* trigger defines */
+#define MCD_TRIG_OPT_DATA_IS_CONDITION 0x00000008
+#define MCD_TRIG_ACTION_DBG_DEBUG 0x00000001
+
+/* schema defines */
+#define ARG_SCHEMA_QRYHANDLE 'q'
+#define ARG_SCHEMA_STRING 's'
+#define ARG_SCHEMA_INT 'd'
+#define ARG_SCHEMA_UINT64_T 'l'
+#define ARG_SCHEMA_CORENUM 'c'
+#define ARG_SCHEMA_HEXDATA 'h'
+
+/* resets */
+#define RESET_SYSTEM "full_system_reset"
+#define RESET_GPR "gpr_reset"
+#define RESET_MEMORY "memory_reset"
+
+/* misc */
+#define QUERY_TOTAL_NUMBER 12
+#define CMD_SCHEMA_LENGTH 6
+#define MCD_SYSTEM_NAME "qemu-system"
+
+/* supported architectures */
+#define MCDSTUB_ARCH_ARM "arm"
+
+/* tcp query packet values templates */
+#define DEVICE_NAME_TEMPLATE(s) "qemu-" #s "-device"
+
+/* state strings */
+#define STATE_STR_UNKNOWN(d) "cpu " #d " in unknown state"
+#define STATE_STR_DEBUG(d) "cpu " #d " in debug state"
+#define STATE_STR_RUNNING(d) "cpu " #d " running"
+#define STATE_STR_HALTED(d) "cpu " #d " currently halted"
+#define STATE_STR_INIT_HALTED "vm halted since boot"
+#define STATE_STR_INIT_RUNNING "vm running since boot"
+#define STATE_STR_BREAK_HW "stopped beacuse of HW breakpoint"
+#define STATE_STEP_PERFORMED "stopped beacuse of single step"
+#define STATE_STR_BREAK_READ(d) "stopped beacuse of read access at " #d
+#define STATE_STR_BREAK_WRITE(d) "stopped beacuse of write access at " #d
+#define STATE_STR_BREAK_RW(d) "stopped beacuse of read or write access at " #d
+#define STATE_STR_BREAK_UNKNOWN "stopped for unknown reason"
+
+typedef struct MCDProcess {
+    uint32_t pid;
+    bool attached;
+} MCDProcess;
+
+typedef void (*MCDCmdHandler)(GArray *params, void *user_ctx);
+typedef struct MCDCmdParseEntry {
+    MCDCmdHandler handler;
+    char cmd[ARGUMENT_STRING_LENGTH];
+    char schema[CMD_SCHEMA_LENGTH];
+} MCDCmdParseEntry;
+
+typedef union MCDCmdVariant {
+    const char *data;
+    uint32_t data_uint32_t;
+    uint64_t data_uint64_t;
+    uint32_t query_handle;
+    uint32_t cpu_id;
+} MCDCmdVariant;
+
+#define get_param(p, i)    (&g_array_index(p, MCDCmdVariant, i))
+
+enum RSState {
+    RS_INACTIVE,
+    RS_IDLE,
+    RS_GETLINE,
+    RS_DATAEND,
+};
+
+typedef struct breakpoint_st {
+    uint32_t type;
+    uint64_t address;
+    uint32_t id;
+} breakpoint_st;
+
+typedef struct mcd_trigger_into_st {
+    char type[ARGUMENT_STRING_LENGTH];
+    char option[ARGUMENT_STRING_LENGTH];
+    char action[ARGUMENT_STRING_LENGTH];
+    uint32_t nr_trigger;
+} mcd_trigger_into_st;
+
+typedef struct mcd_cpu_state_st {
+    const char *state;
+    bool memory_changed;
+    bool registers_changed;
+    bool target_was_stopped;
+    uint32_t bp_type;
+    uint64_t bp_address;
+    const char *stop_str;
+    const char *info_str;
+} mcd_cpu_state_st;
+
+typedef struct MCDState {
+    bool init;
+    CPUState *c_cpu;
+    enum RSState state;
+    char line_buf[MAX_PACKET_LENGTH];
+    int line_buf_index;
+    int line_sum;
+    int line_csum;
+    GByteArray *last_packet;
+    int signal;
+
+    MCDProcess *processes;
+    int process_num;
+    GString *str_buf;
+    GByteArray *mem_buf;
+    int sstep_flags;
+    int supported_sstep_flags;
+
+    uint32_t query_cpu_id;
+    GList *all_memspaces;
+    GList *all_reggroups;
+    GList *all_registers;
+    GList *all_breakpoints;
+    GArray *resets;
+    mcd_trigger_into_st trigger;
+    mcd_cpu_state_st cpu_state;
+    MCDCmdParseEntry mcd_query_cmds_table[QUERY_TOTAL_NUMBER];
+} MCDState;
+
+/* lives in mcdstub.c */
+extern MCDState mcdserver_state;
+
+typedef struct xml_attrib {
+    char argument[ARGUMENT_STRING_LENGTH];
+    char value[ARGUMENT_STRING_LENGTH];
+} xml_attrib;
+
+typedef struct mcd_reset_st {
+    const char *name;
+    uint8_t id;
+} mcd_reset_st;
+
+/**
+ * mcdserver_start() - initializes the mcdstub and opens a TCP port
+ * @device: TCP port (e.g. tcp::1235)
+ */
+int mcdserver_start(const char *device);
+
+#endif /* MCDSTUB_H */
diff --git a/include/mcdstub/mcdstub_common.h b/include/mcdstub/mcdstub_common.h
index c24aaf1202..b64748c080 100644
--- a/include/mcdstub/mcdstub_common.h
+++ b/include/mcdstub/mcdstub_common.h
@@ -16,3 +16,49 @@
  *
  * SPDX-License-Identifier: LGPL-2.0+
  */
+
+#ifndef MCDSTUB_COMMON_H
+#define MCDSTUB_COMMON_H
+
+#define ARGUMENT_STRING_LENGTH 64
+#define TCP_CONFIG_STRING_LENGTH 128
+
+typedef struct mcd_mem_space_st {
+    const char *name;
+    uint32_t id;
+    uint32_t type;
+    uint32_t bits_per_mau;
+    uint8_t invariance;
+    uint32_t endian;
+    uint64_t min_addr;
+    uint64_t max_addr;
+    uint32_t supported_access_options;
+    /* internal */
+    bool is_secure;
+    bool is_physical;
+} mcd_mem_space_st;
+
+typedef struct mcd_reg_st {
+    /* xml info */
+    char name[ARGUMENT_STRING_LENGTH];
+    char group[ARGUMENT_STRING_LENGTH];
+    char type[ARGUMENT_STRING_LENGTH];
+    uint32_t bitsize;
+    uint32_t id; /* id used by the mcd interface */
+    uint32_t internal_id; /* id inside reg type */
+    uint8_t reg_type;
+    /* mcd metadata */
+    uint32_t mcd_reg_group_id;
+    uint32_t mcd_mem_space_id;
+    uint32_t mcd_reg_type;
+    uint32_t mcd_hw_thread_id;
+    /* data for op-code */
+    uint32_t opcode;
+} mcd_reg_st;
+
+typedef struct mcd_reg_group_st {
+    const char *name;
+    uint32_t id;
+} mcd_reg_group_st;
+
+#endif /* MCDSTUB_COMMON_H */
diff --git a/qemu-options.hx b/qemu-options.hx
index 42fd09e4de..b60df3463c 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4444,6 +4444,24 @@ SRST
     (see the :ref:`GDB usage` chapter in the System Emulation Users Guide).
 ERST
 
+DEF("mcd", HAS_ARG, QEMU_OPTION_mcd, \
+    "-mcd dev        accept mcd connection on 'dev'. (QEMU defaults to starting\n"
+    "                the guest without waiting for a mcd client to connect; use -S too\n"
+    "                if you want it to not start execution.)\n"
+    "                To use the default Port write '-mcd default'\n",
+    QEMU_ARCH_ALL)
+SRST
+``-mcd dev``
+    Accept a mcd connection on device dev. Note that this option does not pause QEMU
+    execution -- if you want QEMU to not start the guest until you
+    connect with mcd and issue a ``run`` command, you will need to
+    also pass the ``-S`` option to QEMU.
+
+    The most usual configuration is to listen on a local TCP socket::
+
+        -mcd tcp::1235
+ERST
+
 DEF("d", HAS_ARG, QEMU_OPTION_d, \
     "-d item1,...    enable logging of specified items (use '-d help' for a list of log items)\n",
     QEMU_ARCH_ALL)
diff --git a/system/vl.c b/system/vl.c
index 2bcd9efb9a..2c4610c19f 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -68,6 +68,7 @@
 #include "sysemu/numa.h"
 #include "sysemu/hostmem.h"
 #include "exec/gdbstub.h"
+#include "mcdstub/mcdstub.h"
 #include "qemu/timer.h"
 #include "chardev/char.h"
 #include "qemu/bitmap.h"
@@ -1271,6 +1272,7 @@ struct device_config {
         DEV_PARALLEL,  /* -parallel      */
         DEV_DEBUGCON,  /* -debugcon */
         DEV_GDB,       /* -gdb, -s */
+        DEV_MCD,       /* -mcd */
         DEV_SCLP,      /* s390 sclp */
     } type;
     const char *cmdline;
@@ -2686,6 +2688,12 @@ static void qemu_machine_creation_done(void)
     if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
         exit(1);
     }
+    if (foreach_device_config(DEV_MCD, mcdserver_start) < 0) {
+        /*
+         * starts the mcdserver if the mcd option was set
+         */
+        exit(1);
+    }
     if (!vga_interface_created && !default_vga &&
         vga_interface_type != VGA_NONE) {
         warn_report("A -vga option was passed but this machine "
@@ -3041,6 +3049,11 @@ void qemu_init(int argc, char **argv)
             case QEMU_OPTION_gdb:
                 add_device_config(DEV_GDB, optarg);
                 break;
+#if !defined(CONFIG_USER_ONLY)
+            case QEMU_OPTION_mcd:
+                add_device_config(DEV_MCD, optarg);
+                break;
+#endif
             case QEMU_OPTION_L:
                 if (is_help_option(optarg)) {
                     list_data_dirs = true;
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 07/18] mcdstub: mcdserver initialization functions added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (5 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 06/18] mcdstub: -mcd start option added, mcd specific defines added Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 08/18] cutils: qemu_strtou32 function added Nicolas Eder
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/mcdstub.c | 154 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index 4d8d5d956a..0df436719e 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -22,9 +22,12 @@
 #include "qemu/cutils.h"
 #include "qemu/module.h"
 #include "qemu/error-report.h"
+#include "qemu/debug.h"
 #include "qapi/error.h"
 #include "chardev/char.h"
 #include "chardev/char-fe.h"
+#include "hw/cpu/cluster.h"
+#include "hw/boards.h"
 #include "sysemu/cpus.h"
 #include "sysemu/hw_accel.h"
 #include "sysemu/runstate.h"
@@ -109,6 +112,39 @@ static void mcd_chr_event(void *opaque, QEMUChrEvent event)
 {
 }
 
+/**
+ * init_query_cmds_table() - Initializes all query functions.
+ *
+ * This function adds all query functions to the mcd_query_cmds_table. This
+ * includes their command string, handler function and parameter schema.
+ * @mcd_query_cmds_table: Lookup table with all query commands.
+ */
+static void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table)
+{}
+
+/**
+ * mcd_set_stop_cpu() - Sets c_cpu to the just stopped CPU.
+ *
+ * @cpu: The CPU state.
+ */
+static void mcd_set_stop_cpu(CPUState *cpu)
+{
+    mcdserver_state.c_cpu = cpu;
+}
+
+/**
+ * mcd_init_debug_class() - initialize mcd-specific DebugClass
+ */
+static void mcd_init_debug_class(void){
+    Object *obj;
+    obj = object_new(TYPE_DEBUG);
+    DebugState *ds = DEBUG(obj);
+    DebugClass *dc = DEBUG_GET_CLASS(ds);
+    dc->set_stop_cpu = mcd_set_stop_cpu;
+    MachineState *ms = MACHINE(qdev_get_machine());
+    ms->debug_state = ds;
+}
+
 /**
  * mcd_init_mcdserver_state() - Initializes the mcdserver_state struct.
  *
@@ -119,6 +155,35 @@ static void mcd_chr_event(void *opaque, QEMUChrEvent event)
  */
 static void mcd_init_mcdserver_state(void)
 {
+    g_assert(!mcdserver_state.init);
+    memset(&mcdserver_state, 0, sizeof(MCDState));
+    mcdserver_state.init = true;
+    mcdserver_state.str_buf = g_string_new(NULL);
+    mcdserver_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH);
+    mcdserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4);
+
+    /*
+     * What single-step modes are supported is accelerator dependent.
+     * By default try to use no IRQs and no timers while single
+     * stepping so as to make single stepping like a typical ICE HW step.
+     */
+    mcdserver_state.supported_sstep_flags =
+        accel_supported_gdbstub_sstep_flags();
+    mcdserver_state.sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER;
+    mcdserver_state.sstep_flags &= mcdserver_state.supported_sstep_flags;
+
+    /* init query table */
+    init_query_cmds_table(mcdserver_state.mcd_query_cmds_table);
+
+    /* at this time the cpu hans't been started! -> set cpu_state */
+    mcd_cpu_state_st cpu_state =  {
+            .state = CORE_STATE_HALTED,
+            .info_str = STATE_STR_INIT_HALTED,
+    };
+    mcdserver_state.cpu_state = cpu_state;
+
+    /* create new debug object */
+    mcd_init_debug_class();
 }
 
 /**
@@ -128,6 +193,84 @@ static void mcd_init_mcdserver_state(void)
  */
 static void reset_mcdserver_state(void)
 {
+    g_free(mcdserver_state.processes);
+    mcdserver_state.processes = NULL;
+    mcdserver_state.process_num = 0;
+}
+
+/**
+ * mcd_create_default_process() - Creates a default process for debugging.
+ *
+ * This function creates a new, not yet attached, process with an ID one above
+ * the previous maximum ID.
+ * @s: A MCDState object.
+ */
+static void mcd_create_default_process(MCDState *s)
+{
+    MCDProcess *process;
+    int max_pid = 0;
+
+    if (mcdserver_state.process_num) {
+        max_pid = s->processes[s->process_num - 1].pid;
+    }
+
+    s->processes = g_renew(MCDProcess, s->processes, ++s->process_num);
+    process = &s->processes[s->process_num - 1];
+
+    /* We need an available PID slot for this process */
+    assert(max_pid < UINT32_MAX);
+
+    process->pid = max_pid + 1;
+    process->attached = false;
+}
+
+/**
+ * find_cpu_clusters() - Returns the CPU cluster of the child object.
+ *
+ * @param[in] child Object with unknown CPU cluster.
+ * @param[in] opaque Pointer to an MCDState object.
+ */
+static int find_cpu_clusters(Object *child, void *opaque)
+{
+    if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) {
+        MCDState *s = (MCDState *) opaque;
+        CPUClusterState *cluster = CPU_CLUSTER(child);
+        MCDProcess *process;
+
+        s->processes = g_renew(MCDProcess, s->processes, ++s->process_num);
+
+        process = &s->processes[s->process_num - 1];
+        assert(cluster->cluster_id != UINT32_MAX);
+        process->pid = cluster->cluster_id + 1;
+        process->attached = false;
+
+        return 0;
+    }
+
+    return object_child_foreach(child, find_cpu_clusters, opaque);
+}
+
+/**
+ * pid_order() - Compares process IDs.
+ *
+ * This function returns -1 if process "a" has a ower process ID than "b".
+ * If "b" has a lower ID than "a" 1 is returned and if they are qual 0 is
+ * returned.
+ * @a: Process a.
+ * @b: Process b.
+ */
+static int pid_order(const void *a, const void *b)
+{
+    MCDProcess *pa = (MCDProcess *) a;
+    MCDProcess *pb = (MCDProcess *) b;
+
+    if (pa->pid < pb->pid) {
+        return -1;
+    } else if (pa->pid > pb->pid) {
+        return 1;
+    } else {
+        return 0;
+    }
 }
 
 /**
@@ -141,6 +284,17 @@ static void reset_mcdserver_state(void)
  */
 static void create_processes(MCDState *s)
 {
+    object_child_foreach(object_get_root(), find_cpu_clusters, s);
+
+    if (mcdserver_state.processes) {
+        /* Sort by PID */
+        qsort(mcdserver_state.processes,
+              mcdserver_state.process_num,
+              sizeof(mcdserver_state.processes[0]),
+              pid_order);
+    }
+
+    mcd_create_default_process(s);
 }
 
 int mcdserver_start(const char *device)
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 08/18] cutils: qemu_strtou32 function added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (6 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 07/18] mcdstub: mcdserver initialization functions added Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 09/18] mcdstub: TCP packet plumbing added Nicolas Eder
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 include/qemu/cutils.h |  2 ++
 util/cutils.c         | 30 ++++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h
index 5ab1a4ffb0..14f492ba61 100644
--- a/include/qemu/cutils.h
+++ b/include/qemu/cutils.h
@@ -158,6 +158,8 @@ int qemu_strtoul(const char *nptr, const char **endptr, int base,
                  unsigned long *result);
 int qemu_strtoi64(const char *nptr, const char **endptr, int base,
                   int64_t *result);
+int qemu_strtou32(const char *nptr, const char **endptr, int base,
+                  uint32_t *result);
 int qemu_strtou64(const char *nptr, const char **endptr, int base,
                   uint64_t *result);
 int qemu_strtod(const char *nptr, const char **endptr, double *result);
diff --git a/util/cutils.c b/util/cutils.c
index 42364039a5..5e00a4ec14 100644
--- a/util/cutils.c
+++ b/util/cutils.c
@@ -665,6 +665,36 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base,
     return check_strtox_error(nptr, ep, endptr, *result == 0, errno);
 }
 
+/**
+ * Convert string @nptr to an uint32_t.
+ *
+ * Works like qemu_strtoul(), except it stores UINT32_MAX on overflow.
+ * (If you want to prohibit negative numbers that wrap around to
+ * positive, use parse_uint()).
+ */
+int qemu_strtou32(const char *nptr, const char **endptr, int base,
+                  uint32_t *result)
+{
+    char *ep;
+
+    assert((unsigned) base <= 36 && base != 1);
+    if (!nptr) {
+        *result = 0;
+        if (endptr) {
+            *endptr = nptr;
+        }
+        return -EINVAL;
+    }
+
+    errno = 0;
+    *result = strtoul(nptr, &ep, base);
+    /* Windows returns 1 for negative out-of-range values.  */
+    if (errno == ERANGE) {
+        *result = -1;
+    }
+    return check_strtox_error(nptr, ep, endptr, *result == 0, errno);
+}
+
 /**
  * Convert string @nptr to an uint64_t.
  *
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 09/18] mcdstub: TCP packet plumbing added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (7 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 08/18] cutils: qemu_strtou32 function added Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 10/18] mcdstub: open and close server functions added Nicolas Eder
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/mcdstub.c | 422 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 422 insertions(+)

diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index 0df436719e..642f3c2826 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -87,6 +87,320 @@ static int mcd_chr_can_receive(void *opaque)
     return MAX_PACKET_LENGTH;
 }
 
+/**
+ * mcd_put_buffer() - Sends the buf as TCP packet with qemu_chr_fe_write_all.
+ *
+ * @buf: TCP packet data.
+ * @len: TCP packet length.
+ */
+static void mcd_put_buffer(const uint8_t *buf, int len)
+{
+    qemu_chr_fe_write_all(&mcdserver_system_state.chr, buf, len);
+}
+
+/**
+ * mcd_put_packet_binary() - Adds footer and header to the TCP packet data in
+ * buf.
+ *
+ * Besides adding header and footer, this function also stores the complete TCP
+ * packet in the last_packet member of the mcdserver_state. Then the packet
+ * gets send with the :c:func:`mcd_put_buffer` function.
+ * @buf: TCP packet data.
+ * @len: TCP packet length.
+ */
+static int mcd_put_packet_binary(const char *buf, int len)
+{
+    g_byte_array_set_size(mcdserver_state.last_packet, 0);
+    g_byte_array_append(mcdserver_state.last_packet,
+        (const uint8_t *) (char[2]) { TCP_COMMAND_START, '\0' }, 1);
+    g_byte_array_append(mcdserver_state.last_packet,
+        (const uint8_t *) buf, len);
+    g_byte_array_append(mcdserver_state.last_packet,
+        (const uint8_t *) (char[2]) { TCP_COMMAND_END, '\0' }, 1);
+    g_byte_array_append(mcdserver_state.last_packet,
+        (const uint8_t *) (char[2]) { TCP_WAS_LAST, '\0' }, 1);
+
+    mcd_put_buffer(mcdserver_state.last_packet->data,
+        mcdserver_state.last_packet->len);
+    return 0;
+}
+
+/**
+ * mcd_put_packet() - Calls :c:func:`mcd_put_packet_binary` with buf and length
+ * of buf.
+ *
+ * @buf: TCP packet data.
+ */
+static int mcd_put_packet(const char *buf)
+{
+    return mcd_put_packet_binary(buf, strlen(buf));
+}
+
+/**
+ * cmd_parse_params() - Extracts all parameters from a TCP packet.
+ *
+ * This function uses the schema parameter to determine which type of parameter
+ * to expect. It then extracts that parameter from the data and stores it in
+ * the params GArray.
+ * @data: TCP packet data.
+ * @schema: List of expected parameters for the packet.
+ * @params: GArray with all extracted parameters.
+ */
+static int cmd_parse_params(const char *data, const char *schema,
+                            GArray *params)
+{
+    char data_buffer[64] = {0};
+    const char *remaining_data = data;
+
+    for (int i = 0; i < strlen(schema); i++) {
+        /* get correct part of data */
+        char *separator = strchr(remaining_data, ARGUMENT_SEPARATOR);
+
+        if (separator) {
+            /* multiple arguments */
+            int seperator_index = (int)(separator - remaining_data);
+            strncpy(data_buffer, remaining_data, seperator_index);
+            data_buffer[seperator_index] = 0;
+        } else {
+            strncpy(data_buffer, remaining_data, 63);
+            data_buffer[strlen(remaining_data)] = 0;
+        }
+
+        /* store right data */
+        MCDCmdVariant this_param;
+        switch (schema[i]) {
+        case ARG_SCHEMA_STRING:
+            /* this has to be the last argument */
+            this_param.data = remaining_data;
+            g_array_append_val(params, this_param);
+            break;
+        case ARG_SCHEMA_HEXDATA:
+            g_string_printf(mcdserver_state.str_buf, "%s", data_buffer);
+            break;
+        case ARG_SCHEMA_INT:
+            if (qemu_strtou32(remaining_data, &remaining_data, 10,
+                              (uint32_t *)&this_param.data_uint32_t)) {
+                                return -1;
+            }
+            g_array_append_val(params, this_param);
+            break;
+        case ARG_SCHEMA_UINT64_T:
+            if (qemu_strtou64(remaining_data, &remaining_data, 10,
+                              (uint64_t *)&this_param.data_uint64_t)) {
+                                return -1;
+            }
+            g_array_append_val(params, this_param);
+            break;
+        case ARG_SCHEMA_QRYHANDLE:
+            if (qemu_strtou32(remaining_data, &remaining_data, 10,
+                              (uint32_t *)&this_param.query_handle)) {
+                                return -1;
+            }
+            g_array_append_val(params, this_param);
+            break;
+        case ARG_SCHEMA_CORENUM:
+            if (qemu_strtou32(remaining_data, &remaining_data, 10,
+                              (uint32_t *)&this_param.cpu_id)) {
+                                return -1;
+            }
+            g_array_append_val(params, this_param);
+            break;
+        default:
+            return -1;
+        }
+        /* update remaining data for the next run */
+        remaining_data = &(remaining_data[1]);
+    }
+    return 0;
+}
+
+/**
+ * process_string_cmd() - Collects all parameters from the data and calls the
+ * correct handler.
+ *
+ * The parameters are extracted with the :c:func:`cmd_parse_params function.
+ * This function selects the command in the cmds array, which fits the start of
+ * the data string. This way the correct commands is selected.
+ * @data: TCP packet data.
+ * @cmds: Array of possible commands.
+ * @num_cmds: Number of commands in the cmds array.
+ */
+static int process_string_cmd(void *user_ctx, const char *data,
+    const MCDCmdParseEntry *cmds, int num_cmds)
+{
+    int i;
+    g_autoptr(GArray) params = g_array_new(false, true, sizeof(MCDCmdVariant));
+
+    if (!cmds) {
+        return -1;
+    }
+
+    for (i = 0; i < num_cmds; i++) {
+        const MCDCmdParseEntry *cmd = &cmds[i];
+        g_assert(cmd->handler && cmd->cmd);
+
+        /* continue if data and command are different */
+        if (strncmp(data, cmd->cmd, strlen(cmd->cmd))) {
+            continue;
+        }
+
+        if (strlen(cmd->schema)) {
+            /* extract data for parameters */
+            if (cmd_parse_params(&data[strlen(cmd->cmd)], cmd->schema, params))
+            {
+                return -1;
+            }
+        }
+
+        /* call handler */
+        cmd->handler(params, user_ctx);
+        return 0;
+    }
+
+    return -1;
+}
+
+/**
+ * run_cmd_parser() - Prepares the mcdserver_state before executing TCP packet
+ * functions.
+ *
+ * This function empties the str_buf and mem_buf of the mcdserver_state and
+ * then calls :c:func:`process_string_cmd`. In case this function fails, an
+ * empty TCP packet is sent back the MCD Shared Library.
+ * @data: TCP packet data.
+ * @cmd: Handler function (can be an array of functions).
+ */
+static void run_cmd_parser(const char *data, const MCDCmdParseEntry *cmd)
+{
+    if (!data) {
+        return;
+    }
+
+    g_string_set_size(mcdserver_state.str_buf, 0);
+    g_byte_array_set_size(mcdserver_state.mem_buf, 0);
+
+    if (process_string_cmd(NULL, data, cmd, 1)) {
+        mcd_put_packet("");
+    }
+}
+
+/**
+ * mcd_handle_packet() - Evaluates the type of received packet and chooses the
+ * correct handler.
+ *
+ * This function takes the first character of the line_buf to determine the
+ * type of packet. Then it selects the correct handler function and parameter
+ * schema. With this info it calls :c:func:`run_cmd_parser`.
+ * @line_buf: TCP packet data.
+ */
+static int mcd_handle_packet(const char *line_buf)
+{
+    /*
+     * decides what function (handler) to call depending on
+     * the first character in the line_buf
+     */
+    const MCDCmdParseEntry *cmd_parser = NULL;
+
+    switch (line_buf[0]) {
+    default:
+        /* command not supported */
+        mcd_put_packet("");
+        break;
+    }
+
+    if (cmd_parser) {
+        /* parse commands and run the selected handler function */
+        run_cmd_parser(line_buf, cmd_parser);
+    }
+
+    return RS_IDLE;
+}
+
+/**
+ * mcd_read_byte() - Resends the last packet if not acknowledged and extracts
+ * the data from a received TCP packet.
+ *
+ * In case the last sent packet was not acknowledged from the mcdstub,
+ * this function resends it.
+ * If it was acknowledged this function parses the incoming packet
+ * byte by byte.
+ * It extracts the data in the packet and sends an
+ * acknowledging response when finished. Then :c:func:`mcd_handle_packet` gets
+ * called.
+ * @ch: Character of the received TCP packet, which should be parsed.
+ */
+static void mcd_read_byte(uint8_t ch)
+{
+     uint8_t reply;
+
+    if (mcdserver_state.last_packet->len) {
+        if (ch == TCP_NOT_ACKNOWLEDGED) {
+            /* the previous packet was not akcnowledged */
+            mcd_put_buffer(mcdserver_state.last_packet->data,
+                mcdserver_state.last_packet->len);
+        } else if (ch == TCP_ACKNOWLEDGED) {
+            /* the previous packet was acknowledged */
+        }
+
+        if (ch == TCP_ACKNOWLEDGED || ch == TCP_COMMAND_START) {
+            /*
+             * either acknowledged or a new communication starts
+             * -> discard previous packet
+             */
+            g_byte_array_set_size(mcdserver_state.last_packet, 0);
+        }
+        if (ch != TCP_COMMAND_START) {
+            /* skip to the next char */
+            return;
+        }
+    }
+
+    switch (mcdserver_state.state) {
+    case RS_IDLE:
+        if (ch == TCP_COMMAND_START) {
+            /* start of command packet */
+            mcdserver_state.line_buf_index = 0;
+            mcdserver_state.line_sum = 0;
+            mcdserver_state.state = RS_GETLINE;
+        }
+        break;
+    case RS_GETLINE:
+        if (ch == TCP_COMMAND_END) {
+            /* end of command */
+            mcdserver_state.line_buf[mcdserver_state.line_buf_index++] = 0;
+            mcdserver_state.state = RS_DATAEND;
+        } else if (mcdserver_state.line_buf_index >=
+            sizeof(mcdserver_state.line_buf) - 1) {
+            /* the input string is too long for the linebuffer! */
+            mcdserver_state.state = RS_IDLE;
+        } else {
+            /* copy the content to the line_buf */
+            mcdserver_state.line_buf[mcdserver_state.line_buf_index++] = ch;
+            mcdserver_state.line_sum += ch;
+        }
+        break;
+    case RS_DATAEND:
+        if (ch == TCP_WAS_NOT_LAST) {
+            reply = TCP_ACKNOWLEDGED;
+            mcd_put_buffer(&reply, 1);
+            mcdserver_state.state = mcd_handle_packet(mcdserver_state.line_buf);
+        } else if (ch == TCP_WAS_LAST) {
+            reply = TCP_ACKNOWLEDGED;
+            mcd_put_buffer(&reply, 1);
+            mcdserver_state.state = mcd_handle_packet(mcdserver_state.line_buf);
+        } else {
+            /* not acknowledged! */
+            reply = TCP_NOT_ACKNOWLEDGED;
+            mcd_put_buffer(&reply, 1);
+            /* waiting for package to get resent */
+            mcdserver_state.state = RS_IDLE;
+        }
+        break;
+    default:
+        abort();
+    }
+}
+
 /**
  * mcd_chr_receive() - Handles receiving a TCP packet.
  *
@@ -98,6 +412,99 @@ static int mcd_chr_can_receive(void *opaque)
  */
 static void mcd_chr_receive(void *opaque, const uint8_t *buf, int size)
 {
+    int i;
+
+    for (i = 0; i < size; i++) {
+        mcd_read_byte(buf[i]);
+        if (buf[i] == 0) {
+            break;
+        }
+    }
+}
+
+/**
+ * mcd_get_process() - Returns the process of the provided pid.
+ *
+ * @pid: The process ID.
+ */
+static MCDProcess *mcd_get_process(uint32_t pid)
+{
+    int i;
+
+    if (!pid) {
+        /* 0 means any process, we take the first one */
+        return &mcdserver_state.processes[0];
+    }
+
+    for (i = 0; i < mcdserver_state.process_num; i++) {
+        if (mcdserver_state.processes[i].pid == pid) {
+            return &mcdserver_state.processes[i];
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * mcd_get_cpu_pid() - Returns the process ID of the provided CPU.
+ *
+ * @cpu: The CPU state.
+ */
+static uint32_t mcd_get_cpu_pid(CPUState *cpu)
+{
+    if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
+        /* Return the default process' PID */
+        int index = mcdserver_state.process_num - 1;
+        return mcdserver_state.processes[index].pid;
+    }
+    return cpu->cluster_index + 1;
+}
+
+/**
+ * mcd_get_cpu_process() - Returns the process of the provided CPU.
+ *
+ * @cpu: The CPU state.
+ */
+static MCDProcess *mcd_get_cpu_process(CPUState *cpu)
+{
+    return mcd_get_process(mcd_get_cpu_pid(cpu));
+}
+
+/**
+ * mcd_next_attached_cpu() - Returns the first CPU with an attached process
+ * starting after the
+ * provided cpu.
+ *
+ * @cpu: The CPU to start from.
+ */
+static CPUState *mcd_next_attached_cpu(CPUState *cpu)
+{
+    cpu = CPU_NEXT(cpu);
+
+    while (cpu) {
+        if (mcd_get_cpu_process(cpu)->attached) {
+            break;
+        }
+
+        cpu = CPU_NEXT(cpu);
+    }
+
+    return cpu;
+}
+
+/**
+ * mcd_first_attached_cpu() - Returns the first CPU with an attached process.
+ */
+static CPUState *mcd_first_attached_cpu(void)
+{
+    CPUState *cpu = first_cpu;
+    MCDProcess *process = mcd_get_cpu_process(cpu);
+
+    if (!process->attached) {
+        return mcd_next_attached_cpu(cpu);
+    }
+
+    return cpu;
 }
 
 /**
@@ -110,6 +517,21 @@ static void mcd_chr_receive(void *opaque, const uint8_t *buf, int size)
  */
 static void mcd_chr_event(void *opaque, QEMUChrEvent event)
 {
+    int i;
+    MCDState *s = (MCDState *) opaque;
+
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        /* Start with first process attached, others detached */
+        for (i = 0; i < s->process_num; i++) {
+            s->processes[i].attached = !i;
+        }
+
+        s->c_cpu = mcd_first_attached_cpu();
+        break;
+    default:
+        break;
+    }
 }
 
 /**
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 10/18] mcdstub: open and close server functions added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (8 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 09/18] mcdstub: TCP packet plumbing added Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 11/18] mcdstub: system and core queries added Nicolas Eder
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/mcdstub.c | 299 ++++++++++++++++++++++++++++------------
 1 file changed, 214 insertions(+), 85 deletions(-)

diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index 642f3c2826..45daa38689 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -65,6 +65,91 @@ static void mcd_sigterm_handler(int signal)
 }
 #endif
 
+/**
+ * mcd_get_process() - Returns the process of the provided pid.
+ *
+ * @pid: The process ID.
+ */
+static MCDProcess *mcd_get_process(uint32_t pid)
+{
+    int i;
+
+    if (!pid) {
+        /* 0 means any process, we take the first one */
+        return &mcdserver_state.processes[0];
+    }
+
+    for (i = 0; i < mcdserver_state.process_num; i++) {
+        if (mcdserver_state.processes[i].pid == pid) {
+            return &mcdserver_state.processes[i];
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * mcd_get_cpu_pid() - Returns the process ID of the provided CPU.
+ *
+ * @cpu: The CPU state.
+ */
+static uint32_t mcd_get_cpu_pid(CPUState *cpu)
+{
+    if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
+        /* Return the default process' PID */
+        int index = mcdserver_state.process_num - 1;
+        return mcdserver_state.processes[index].pid;
+    }
+    return cpu->cluster_index + 1;
+}
+
+/**
+ * mcd_get_cpu_process() - Returns the process of the provided CPU.
+ *
+ * @cpu: The CPU state.
+ */
+static MCDProcess *mcd_get_cpu_process(CPUState *cpu)
+{
+    return mcd_get_process(mcd_get_cpu_pid(cpu));
+}
+
+/**
+ * mcd_next_attached_cpu() - Returns the first CPU with an attached process
+ * starting after the
+ * provided cpu.
+ *
+ * @cpu: The CPU to start from.
+ */
+static CPUState *mcd_next_attached_cpu(CPUState *cpu)
+{
+    cpu = CPU_NEXT(cpu);
+
+    while (cpu) {
+        if (mcd_get_cpu_process(cpu)->attached) {
+            break;
+        }
+
+        cpu = CPU_NEXT(cpu);
+    }
+
+    return cpu;
+}
+
+/**
+ * mcd_first_attached_cpu() - Returns the first CPU with an attached process.
+ */
+static CPUState *mcd_first_attached_cpu(void)
+{
+    CPUState *cpu = first_cpu;
+    MCDProcess *process = mcd_get_cpu_process(cpu);
+
+    if (!process->attached) {
+        return mcd_next_attached_cpu(cpu);
+    }
+
+    return cpu;
+}
+
 /**
  * mcd_vm_state_change() - Handles a state change of the QEMU VM.
  *
@@ -284,6 +369,117 @@ static void run_cmd_parser(const char *data, const MCDCmdParseEntry *cmd)
     }
 }
 
+/**
+ * init_resets() - Initializes the resets info.
+ *
+ * This function currently only adds all theoretical possible resets to the
+ * resets GArray. None of the resets work at the moment. The resets are:
+ * "full_system_reset", "gpr_reset" and "memory_reset".
+ * @resets: GArray with possible resets.
+ */
+static int init_resets(GArray *resets)
+{
+    mcd_reset_st system_reset = { .id = 0, .name = RESET_SYSTEM};
+    mcd_reset_st gpr_reset = { .id = 1, .name = RESET_GPR};
+    mcd_reset_st memory_reset = { .id = 2, .name = RESET_MEMORY};
+    g_array_append_vals(resets, (gconstpointer)&system_reset, 1);
+    g_array_append_vals(resets, (gconstpointer)&gpr_reset, 1);
+    g_array_append_vals(resets, (gconstpointer)&memory_reset, 1);
+    return 0;
+}
+
+/**
+ * init_trigger() - Initializes the trigger info.
+ *
+ * This function adds the types of trigger, their possible options and actions
+ * to the trigger struct.
+ * @trigger: Struct with all trigger info.
+ */
+static int init_trigger(mcd_trigger_into_st *trigger)
+{
+    snprintf(trigger->type, sizeof(trigger->type),
+        "%d,%d,%d,%d", MCD_BREAKPOINT_HW, MCD_BREAKPOINT_READ,
+        MCD_BREAKPOINT_WRITE, MCD_BREAKPOINT_RW);
+    snprintf(trigger->option, sizeof(trigger->option),
+        "%s", MCD_TRIG_OPT_VALUE);
+    snprintf(trigger->action, sizeof(trigger->action),
+        "%s", MCD_TRIG_ACT_BREAK);
+    /* there can be 16 breakpoints and 16 watchpoints each */
+    trigger->nr_trigger = 16;
+    return 0;
+}
+
+/**
+ * handle_open_server() - Handler for opening the MCD server.
+ *
+ * This is the first function that gets called from the MCD Shared Library.
+ * It initializes core indepent data with the :c:func:`init_resets` and
+ * \reg init_trigger functions. It also send the TCP_HANDSHAKE_SUCCESS
+ * packet back to the library to confirm the mcdstub is ready for further
+ * communication.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_open_server(GArray *params, void *user_ctx)
+{
+    /* initialize core-independent data */
+    int return_value = 0;
+    mcdserver_state.resets = g_array_new(false, true, sizeof(mcd_reset_st));
+    return_value = init_resets(mcdserver_state.resets);
+    if (return_value != 0) {
+        g_assert_not_reached();
+    }
+    return_value = init_trigger(&mcdserver_state.trigger);
+    if (return_value != 0) {
+        g_assert_not_reached();
+    }
+
+    mcd_put_packet(TCP_HANDSHAKE_SUCCESS);
+}
+
+/**
+ * mcd_vm_start() - Starts all CPUs with the vm_start function.
+ */
+static void mcd_vm_start(void)
+{
+    if (!runstate_needs_reset() && !runstate_is_running()) {
+        vm_start();
+    }
+}
+
+/**
+ * handle_close_server() - Handler for closing the MCD server.
+ *
+ * This function detaches the debugger (process) and frees up memory.
+ * Then it start the QEMU VM with :c:func:`mcd_vm_start`.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_close_server(GArray *params, void *user_ctx)
+{
+    uint32_t pid = 1;
+    MCDProcess *process = mcd_get_process(pid);
+
+    /*
+     * 1. free memory
+     * TODO: do this only if there are no processes attached anymore!
+     */
+    g_list_free(mcdserver_state.all_memspaces);
+    g_list_free(mcdserver_state.all_reggroups);
+    g_list_free(mcdserver_state.all_registers);
+    g_array_free(mcdserver_state.resets, TRUE);
+
+    /* 2. detach */
+    process->attached = false;
+
+    /* 3. reset process */
+    if (pid == mcd_get_cpu_pid(mcdserver_state.c_cpu)) {
+        mcdserver_state.c_cpu = mcd_first_attached_cpu();
+    }
+    if (!mcdserver_state.c_cpu) {
+        /* no more processes attached */
+        mcd_vm_start();
+    }
+}
+
 /**
  * mcd_handle_packet() - Evaluates the type of received packet and chooses the
  * correct handler.
@@ -302,6 +498,24 @@ static int mcd_handle_packet(const char *line_buf)
     const MCDCmdParseEntry *cmd_parser = NULL;
 
     switch (line_buf[0]) {
+    case TCP_CHAR_OPEN_SERVER:
+        {
+            static MCDCmdParseEntry open_server_cmd_desc = {
+                .handler = handle_open_server,
+                .cmd = {TCP_CHAR_OPEN_SERVER, '\0'},
+            };
+            cmd_parser = &open_server_cmd_desc;
+        }
+        break;
+    case TCP_CHAR_CLOSE_SERVER:
+        {
+            static MCDCmdParseEntry close_server_cmd_desc = {
+                .handler = handle_close_server,
+                .cmd = {TCP_CHAR_CLOSE_SERVER, '\0'},
+            };
+            cmd_parser = &close_server_cmd_desc;
+        }
+        break;
     default:
         /* command not supported */
         mcd_put_packet("");
@@ -422,91 +636,6 @@ static void mcd_chr_receive(void *opaque, const uint8_t *buf, int size)
     }
 }
 
-/**
- * mcd_get_process() - Returns the process of the provided pid.
- *
- * @pid: The process ID.
- */
-static MCDProcess *mcd_get_process(uint32_t pid)
-{
-    int i;
-
-    if (!pid) {
-        /* 0 means any process, we take the first one */
-        return &mcdserver_state.processes[0];
-    }
-
-    for (i = 0; i < mcdserver_state.process_num; i++) {
-        if (mcdserver_state.processes[i].pid == pid) {
-            return &mcdserver_state.processes[i];
-        }
-    }
-
-    return NULL;
-}
-
-/**
- * mcd_get_cpu_pid() - Returns the process ID of the provided CPU.
- *
- * @cpu: The CPU state.
- */
-static uint32_t mcd_get_cpu_pid(CPUState *cpu)
-{
-    if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
-        /* Return the default process' PID */
-        int index = mcdserver_state.process_num - 1;
-        return mcdserver_state.processes[index].pid;
-    }
-    return cpu->cluster_index + 1;
-}
-
-/**
- * mcd_get_cpu_process() - Returns the process of the provided CPU.
- *
- * @cpu: The CPU state.
- */
-static MCDProcess *mcd_get_cpu_process(CPUState *cpu)
-{
-    return mcd_get_process(mcd_get_cpu_pid(cpu));
-}
-
-/**
- * mcd_next_attached_cpu() - Returns the first CPU with an attached process
- * starting after the
- * provided cpu.
- *
- * @cpu: The CPU to start from.
- */
-static CPUState *mcd_next_attached_cpu(CPUState *cpu)
-{
-    cpu = CPU_NEXT(cpu);
-
-    while (cpu) {
-        if (mcd_get_cpu_process(cpu)->attached) {
-            break;
-        }
-
-        cpu = CPU_NEXT(cpu);
-    }
-
-    return cpu;
-}
-
-/**
- * mcd_first_attached_cpu() - Returns the first CPU with an attached process.
- */
-static CPUState *mcd_first_attached_cpu(void)
-{
-    CPUState *cpu = first_cpu;
-    MCDProcess *process = mcd_get_cpu_process(cpu);
-
-    if (!process->attached) {
-        return mcd_next_attached_cpu(cpu);
-    }
-
-    return cpu;
-}
-
 /**
  * mcd_chr_event() - Handles a TCP client connect.
  *
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 11/18] mcdstub: system and core queries added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (9 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 10/18] mcdstub: open and close server functions added Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2024-02-29 15:11   ` Alex Bennée
  2023-12-20 16:25 ` [PATCH v5 12/18] mcdstub: all core specific " Nicolas Eder
                   ` (6 subsequent siblings)
  17 siblings, 1 reply; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/arm_mcdstub.c      | 243 ++++++++++++++++++++
 debug/mcdstub/mcdstub.c          | 370 ++++++++++++++++++++++++++++++-
 debug/mcdstub/meson.build        |   2 +-
 include/mcdstub/arm_mcdstub.h    |  85 +++++++
 include/mcdstub/mcdstub.h        |   5 -
 include/mcdstub/mcdstub_common.h |  19 ++
 6 files changed, 717 insertions(+), 7 deletions(-)

diff --git a/debug/mcdstub/arm_mcdstub.c b/debug/mcdstub/arm_mcdstub.c
index c24aaf1202..ce5264a617 100644
--- a/debug/mcdstub/arm_mcdstub.c
+++ b/debug/mcdstub/arm_mcdstub.c
@@ -16,3 +16,246 @@
  *
  * SPDX-License-Identifier: LGPL-2.0+
  */
+
+#include "qemu/osdep.h"
+#include "mcdstub/arm_mcdstub.h"
+
+int arm_mcd_store_mem_spaces(CPUState *cpu, GArray *memspaces)
+{
+    int nr_address_spaces = cpu->num_ases;
+    uint32_t mem_space_id = 0;
+
+    mem_space_id++;
+    mcd_mem_space_st non_secure = {
+        .name = "Non Secure",
+        .id = mem_space_id,
+        .type = 34,
+        .bits_per_mau = 8,
+        .invariance = 1,
+        .endian = 1,
+        .min_addr = 0,
+        .max_addr = -1,
+        .supported_access_options = 0,
+        .is_secure = false,
+        .is_physical = false,
+    };
+    g_array_append_vals(memspaces, (gconstpointer)&non_secure, 1);
+    mem_space_id++;
+    mcd_mem_space_st phys_non_secure = {
+        .name = "Physical (Non Secure)",
+        .id = mem_space_id,
+        .type = 18,
+        .bits_per_mau = 8,
+        .invariance = 1,
+        .endian = 1,
+        .min_addr = 0,
+        .max_addr = -1,
+        .supported_access_options = 0,
+        .is_secure = false,
+        .is_physical = true,
+    };
+    g_array_append_vals(memspaces, (gconstpointer)&phys_non_secure, 1);
+    if (nr_address_spaces > 1) {
+        mem_space_id++;
+        mcd_mem_space_st secure = {
+            .name = "Secure",
+            .id = mem_space_id,
+            .type = 34,
+            .bits_per_mau = 8,
+            .invariance = 1,
+            .endian = 1,
+            .min_addr = 0,
+            .max_addr = -1,
+            .supported_access_options = 0,
+            .is_secure = true,
+            .is_physical = false,
+        };
+        g_array_append_vals(memspaces, (gconstpointer)&secure, 1);
+        mem_space_id++;
+        mcd_mem_space_st phys_secure = {
+            .name = "Physical (Secure)",
+            .id = mem_space_id,
+            .type = 18,
+            .bits_per_mau = 8,
+            .invariance = 1,
+            .endian = 1,
+            .min_addr = 0,
+            .max_addr = -1,
+            .supported_access_options = 0,
+            .is_secure = true,
+            .is_physical = true,
+        };
+        g_array_append_vals(memspaces, (gconstpointer)&phys_secure, 1);
+    }
+    mem_space_id++;
+    mcd_mem_space_st gpr = {
+        .name = "GPR Registers",
+        .id = mem_space_id,
+        .type = 1,
+        .bits_per_mau = 8,
+        .invariance = 1,
+        .endian = 1,
+        .min_addr = 0,
+        .max_addr = -1,
+        .supported_access_options = 0,
+    };
+    g_array_append_vals(memspaces, (gconstpointer)&gpr, 1);
+    mem_space_id++;
+    mcd_mem_space_st cpr = {
+        .name = "CP15 Registers",
+        .id = mem_space_id,
+        .type = 1,
+        .bits_per_mau = 8,
+        .invariance = 1,
+        .endian = 1,
+        .min_addr = 0,
+        .max_addr = -1,
+        .supported_access_options = 0,
+    };
+    g_array_append_vals(memspaces, (gconstpointer)&cpr, 1);
+    return 0;
+}
+
+int arm_mcd_parse_core_xml_file(CPUClass *cc, GArray *reggroups,
+    GArray *registers, int *current_group_id)
+{
+    const char *xml_filename = NULL;
+    const char *current_xml_filename = NULL;
+    const char *xml_content = NULL;
+    int i = 0;
+
+    /* 1. get correct file */
+    xml_filename = cc->gdb_core_xml_file;
+    for (i = 0; ; i++) {
+        current_xml_filename = gdb_static_features[i].xmlname;
+        if (!current_xml_filename || (strncmp(current_xml_filename,
+            xml_filename, strlen(xml_filename)) == 0
+            && strlen(current_xml_filename) == strlen(xml_filename)))
+            break;
+    }
+    /* without gpr registers we can do nothing */
+    if (!current_xml_filename) {
+        return -1;
+    }
+
+    /* 2. add group for gpr registers */
+    mcd_reg_group_st gprregs = {
+        .name = "GPR Registers",
+        .id = *current_group_id
+    };
+    g_array_append_vals(reggroups, (gconstpointer)&gprregs, 1);
+    *current_group_id = *current_group_id + 1;
+
+    /* 3. parse xml */
+    /* the offset for gpr is always zero */
+    xml_content = gdb_static_features[i].xml;
+    parse_reg_xml(xml_content, strlen(xml_content), registers,
+        MCD_ARM_REG_TYPE_GPR, 0);
+    return 0;
+}
+
+int arm_mcd_parse_general_xml_files(CPUState *cpu, GArray *reggroups,
+    GArray *registers, int *current_group_id) {
+    const char *xml_filename = NULL;
+    const char *current_xml_filename = NULL;
+    const char *xml_content = NULL;
+    uint8_t reg_type = 0;
+    CPUClass *cc = CPU_GET_CLASS(cpu);
+
+    /* iterate over all gdb xml files*/
+    GDBRegisterState *r;
+    for (guint i = 0; i < cpu->gdb_regs->len; i++) {
+        r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
+
+        xml_filename = r->xml;
+        xml_content = NULL;
+
+        /* 1. get xml content */
+        if (cc->gdb_get_dynamic_xml) {
+            xml_content = cc->gdb_get_dynamic_xml(cpu, xml_filename);
+        }
+        if (xml_content) {
+            if (strcmp(xml_filename, "system-registers.xml") == 0) {
+                /* these are the coprocessor register */
+                mcd_reg_group_st corprocessorregs = {
+                    .name = "CP15 Registers",
+                    .id = *current_group_id
+                };
+                g_array_append_vals(reggroups,
+                    (gconstpointer)&corprocessorregs, 1);
+                *current_group_id = *current_group_id + 1;
+                reg_type = MCD_ARM_REG_TYPE_CPR;
+            }
+        } else {
+            /* its not a coprocessor xml -> it is a static xml file */
+            int j = 0;
+            for (j = 0; ; j++) {
+                current_xml_filename = gdb_static_features[j].xmlname;
+                if (!current_xml_filename || (strncmp(current_xml_filename,
+                    xml_filename, strlen(xml_filename)) == 0
+                    && strlen(current_xml_filename) == strlen(xml_filename)))
+                    break;
+            }
+            if (current_xml_filename) {
+                xml_content = gdb_static_features[j].xml;
+                /* select correct reg_type */
+                if (strcmp(current_xml_filename, "arm-vfp.xml") == 0) {
+                    reg_type = MCD_ARM_REG_TYPE_VFP;
+                } else if (strcmp(current_xml_filename, "arm-vfp3.xml") == 0) {
+                    reg_type = MCD_ARM_REG_TYPE_VFP;
+                } else if (strcmp(current_xml_filename,
+                    "arm-vfp-sysregs.xml") == 0) {
+                    reg_type = MCD_ARM_REG_TYPE_VFP_SYS;
+                } else if (strcmp(current_xml_filename,
+                    "arm-neon.xml") == 0) {
+                    reg_type = MCD_ARM_REG_TYPE_VFP;
+                } else if (strcmp(current_xml_filename,
+                    "arm-m-profile-mve.xml") == 0) {
+                    reg_type = MCD_ARM_REG_TYPE_MVE;
+                }
+            } else {
+                continue;
+            }
+        }
+        /* 2. parse xml */
+        parse_reg_xml(xml_content, strlen(xml_content), registers, reg_type,
+            r->base_reg);
+    }
+    return 0;
+}
+
+int arm_mcd_get_additional_register_info(GArray *reggroups, GArray *registers,
+    CPUState *cpu)
+{
+    mcd_reg_st *current_register;
+    uint32_t i = 0;
+
+    /* iterate over all registers */
+    for (i = 0; i < registers->len; i++) {
+        current_register = &(g_array_index(registers, mcd_reg_st, i));
+        /* add mcd_reg_group_id and mcd_mem_space_id */
+        if (strcmp(current_register->group, "cp_regs") == 0) {
+            /* coprocessor registers */
+            current_register->mcd_reg_group_id = 2;
+            current_register->mcd_mem_space_id = 6;
+            /*
+             * get info for opcode
+             * for 32bit the opcode is only 16 bit long
+             * for 64bit it is 32 bit long
+             */
+            current_register->opcode |=
+                arm_mcd_get_opcode(cpu, current_register->internal_id);
+        } else {
+            /* gpr register */
+            current_register->mcd_reg_group_id = 1;
+            current_register->mcd_mem_space_id = 5;
+        }
+    }
+    return 0;
+}
+
+uint16_t arm_mcd_get_opcode(CPUState *cs, uint32_t n)
+{
+    /* TODO: not working with current build structure */
+    return 0;
+}
diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index 45daa38689..4095b3f8ce 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -34,6 +34,7 @@
 
 #include "mcdstub/mcd_shared_defines.h"
 #include "mcdstub/mcdstub.h"
+#include "mcdstub/arm_mcdstub.h"
 
 typedef struct {
     CharBackend chr;
@@ -150,6 +151,25 @@ static CPUState *mcd_first_attached_cpu(void)
     return cpu;
 }
 
+/**
+ * mcd_get_cpu() - Returns the CPU the index i_cpu_index.
+ *
+ * @cpu_index: Index of the desired CPU.
+ */
+static CPUState *mcd_get_cpu(uint32_t cpu_index)
+{
+    CPUState *cpu = first_cpu;
+
+    while (cpu) {
+        if (cpu->cpu_index == cpu_index) {
+            return cpu;
+        }
+        cpu = mcd_next_attached_cpu(cpu);
+    }
+
+    return cpu;
+}
+
 /**
  * mcd_vm_state_change() - Handles a state change of the QEMU VM.
  *
@@ -221,6 +241,15 @@ static int mcd_put_packet(const char *buf)
     return mcd_put_packet_binary(buf, strlen(buf));
 }
 
+/**
+ * mcd_put_strbuf() - Calls :c:func:`mcd_put_packet` with the str_buf of the
+ * mcdserver_state.
+ */
+static void mcd_put_strbuf(void)
+{
+    mcd_put_packet(mcdserver_state.str_buf->str);
+}
+
 /**
  * cmd_parse_params() - Extracts all parameters from a TCP packet.
  *
@@ -480,6 +509,134 @@ static void handle_close_server(GArray *params, void *user_ctx)
     }
 }
 
+/**
+ * handle_gen_query() - Handler for all TCP query packets.
+ *
+ * Calls :c:func:`process_string_cmd` with all query functions in the
+ * mcd_query_cmds_table. :c:func:`process_string_cmd` then selects the correct
+ * one. This function just passes on the TCP packet data string from the
+ * parameters.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_gen_query(GArray *params, void *user_ctx)
+{
+    if (!params->len) {
+        return;
+    }
+    /* iterate over all possible query functions and execute the right one */
+    if (process_string_cmd(NULL, get_param(params, 0)->data,
+                           mcdserver_state.mcd_query_cmds_table,
+                           ARRAY_SIZE(mcdserver_state.mcd_query_cmds_table))) {
+        mcd_put_packet("");
+    }
+}
+
+/**
+ * handle_open_core() - Handler for opening a core.
+ *
+ * This function initializes all data for the core with the ID provided in
+ * the first parameter. In has a swtich case for different architectures.
+ * Currently only 32-Bit ARM is supported. The data includes memory spaces,
+ * register groups and registers themselves. They get stored into GLists where
+ * every entry in the list corresponds to one opened core.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_open_core(GArray *params, void *user_ctx)
+{
+    uint32_t cpu_id = get_param(params, 0)->cpu_id;
+    CPUState *cpu = mcd_get_cpu(cpu_id);
+    mcdserver_state.c_cpu = cpu;
+    CPUClass *cc = CPU_GET_CLASS(cpu);
+    const gchar *arch = cc->gdb_arch_name(cpu);
+    int return_value = 0;
+
+    /* prepare data strucutures */
+    GArray *memspaces = g_array_new(false, true, sizeof(mcd_mem_space_st));
+    GArray *reggroups = g_array_new(false, true, sizeof(mcd_reg_group_st));
+    GArray *registers = g_array_new(false, true, sizeof(mcd_reg_st));
+
+    if (strcmp(arch, MCDSTUB_ARCH_ARM) == 0) {
+        /* TODO: make group and memspace ids dynamic */
+        int current_group_id = 1;
+        /* 1. store mem spaces */
+        return_value = arm_mcd_store_mem_spaces(cpu, memspaces);
+        if (return_value != 0) {
+            g_assert_not_reached();
+        }
+        /* 2. parse core xml */
+        return_value = arm_mcd_parse_core_xml_file(cc, reggroups,
+            registers, &current_group_id);
+        if (return_value != 0) {
+            g_assert_not_reached();
+        }
+        /* 3. parse other xmls */
+        return_value = arm_mcd_parse_general_xml_files(cpu, reggroups,
+            registers, &current_group_id);
+        if (return_value != 0) {
+            g_assert_not_reached();
+        }
+        /* 4. add additional data the the regs from the xmls */
+        return_value = arm_mcd_get_additional_register_info(reggroups,
+            registers, cpu);
+        if (return_value != 0) {
+            g_assert_not_reached();
+        }
+        /* 5. store all found data */
+        if (g_list_nth(mcdserver_state.all_memspaces, cpu_id)) {
+            GList *memspaces_ptr =
+                g_list_nth(mcdserver_state.all_memspaces, cpu_id);
+            memspaces_ptr->data = memspaces;
+        } else {
+            mcdserver_state.all_memspaces =
+                g_list_insert(mcdserver_state.all_memspaces, memspaces, cpu_id);
+        }
+        if (g_list_nth(mcdserver_state.all_reggroups, cpu_id)) {
+            GList *reggroups_ptr =
+                g_list_nth(mcdserver_state.all_reggroups, cpu_id);
+            reggroups_ptr->data = reggroups;
+        } else {
+            mcdserver_state.all_reggroups =
+                g_list_insert(mcdserver_state.all_reggroups, reggroups, cpu_id);
+        }
+        if (g_list_nth(mcdserver_state.all_registers, cpu_id)) {
+            GList *registers_ptr =
+                g_list_nth(mcdserver_state.all_registers, cpu_id);
+            registers_ptr->data = registers;
+        } else {
+            mcdserver_state.all_registers =
+                g_list_insert(mcdserver_state.all_registers, registers, cpu_id);
+        }
+    } else {
+        /* we don't support other architectures */
+        g_assert_not_reached();
+    }
+}
+
+/**
+ * handle_close_core() - Handler for closing a core.
+ *
+ * Frees all memory allocated for core specific information. This includes
+ * memory spaces, register groups and registers.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_close_core(GArray *params, void *user_ctx)
+{
+    /* free memory for correct core */
+    uint32_t cpu_id = get_param(params, 0)->cpu_id;
+    GArray *memspaces = g_list_nth_data(mcdserver_state.all_memspaces, cpu_id);
+    mcdserver_state.all_memspaces =
+        g_list_remove(mcdserver_state.all_memspaces, memspaces);
+    g_array_free(memspaces, TRUE);
+    GArray *reggroups = g_list_nth_data(mcdserver_state.all_reggroups, cpu_id);
+    mcdserver_state.all_reggroups =
+        g_list_remove(mcdserver_state.all_reggroups, reggroups);
+    g_array_free(reggroups, TRUE);
+    GArray *registers = g_list_nth_data(mcdserver_state.all_registers, cpu_id);
+    mcdserver_state.all_registers =
+        g_list_remove(mcdserver_state.all_registers, registers);
+    g_array_free(registers, TRUE);
+}
+
 /**
  * mcd_handle_packet() - Evaluates the type of received packet and chooses the
  * correct handler.
@@ -516,6 +673,36 @@ static int mcd_handle_packet(const char *line_buf)
             cmd_parser = &close_server_cmd_desc;
         }
         break;
+    case TCP_CHAR_QUERY:
+        {
+            static MCDCmdParseEntry query_cmd_desc = {
+                .handler = handle_gen_query,
+                .cmd = {TCP_CHAR_QUERY, '\0'},
+                .schema = {ARG_SCHEMA_STRING, '\0'},
+            };
+            cmd_parser = &query_cmd_desc;
+        }
+        break;
+    case TCP_CHAR_OPEN_CORE:
+        {
+            static MCDCmdParseEntry open_core_cmd_desc = {
+                .handler = handle_open_core,
+                .cmd = {TCP_CHAR_OPEN_CORE, '\0'},
+                .schema = {ARG_SCHEMA_CORENUM, '\0'},
+            };
+            cmd_parser = &open_core_cmd_desc;
+        }
+        break;
+    case TCP_CHAR_CLOSE_CORE:
+        {
+            static MCDCmdParseEntry close_core_cmd_desc = {
+                .handler = handle_close_core,
+                .cmd = {TCP_CHAR_CLOSE_CORE, '\0'},
+                .schema = {ARG_SCHEMA_CORENUM, '\0'},
+            };
+            cmd_parser = &close_core_cmd_desc;
+        }
+        break;
     default:
         /* command not supported */
         mcd_put_packet("");
@@ -663,6 +850,49 @@ static void mcd_chr_event(void *opaque, QEMUChrEvent event)
     }
 }
 
+/**
+ * handle_query_system() - Handler for the system query.
+ *
+ * Sends the system name, which is "qemu-system".
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_system(GArray *params, void *user_ctx)
+{
+    mcd_put_packet(MCD_SYSTEM_NAME);
+}
+
+/**
+ * handle_query_cores() - Handler for the core query.
+ *
+ * This function sends the type of core and number of cores currently
+ * simulated by QEMU. It also sends a device name for the MCD data structure.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_cores(GArray *params, void *user_ctx)
+{
+    /* get first cpu */
+    CPUState *cpu = mcd_first_attached_cpu();
+    if (!cpu) {
+        return;
+    }
+
+    ObjectClass *oc = object_get_class(OBJECT(cpu));
+    const char *cpu_model = object_class_get_name(oc);
+
+    CPUClass *cc = CPU_GET_CLASS(cpu);
+    const gchar *arch = cc->gdb_arch_name(cpu);
+
+    uint32_t nr_cores = cpu->nr_cores;
+    char device_name[ARGUMENT_STRING_LENGTH] = {0};
+    if (arch) {
+        snprintf(device_name, sizeof(device_name), "qemu-%s-device", arch);
+    }
+    g_string_printf(mcdserver_state.str_buf, "%s=%s.%s=%s.%s=%u.",
+        TCP_ARGUMENT_DEVICE, device_name, TCP_ARGUMENT_CORE, cpu_model,
+        TCP_ARGUMENT_AMOUNT_CORE, nr_cores);
+    mcd_put_strbuf();
+}
+
 /**
  * init_query_cmds_table() - Initializes all query functions.
  *
@@ -671,7 +901,24 @@ static void mcd_chr_event(void *opaque, QEMUChrEvent event)
  * @mcd_query_cmds_table: Lookup table with all query commands.
  */
 static void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table)
-{}
+{
+    /* initalizes a list of all query commands */
+    int cmd_number = 0;
+
+    MCDCmdParseEntry query_system = {
+        .handler = handle_query_system,
+        .cmd = QUERY_ARG_SYSTEM,
+    };
+    mcd_query_cmds_table[cmd_number] = query_system;
+    cmd_number++;
+
+    MCDCmdParseEntry query_cores = {
+        .handler = handle_query_cores,
+        .cmd = QUERY_ARG_CORES,
+    };
+    mcd_query_cmds_table[cmd_number] = query_cores;
+    cmd_number++;
+}
 
 /**
  * mcd_set_stop_cpu() - Sets c_cpu to the just stopped CPU.
@@ -924,3 +1171,124 @@ int mcdserver_start(const char *device)
 
     return 0;
 }
+
+void parse_reg_xml(const char *xml, int size, GArray* registers,
+    uint8_t reg_type, uint32_t reg_id_offset)
+{
+    /* iterates over the complete xml file */
+    int i, j;
+    uint32_t current_reg_id = reg_id_offset;
+    uint32_t internal_id = 0;
+    int still_to_skip = 0;
+    char argument[64] = {0};
+    char value[64] = {0};
+    bool is_reg = false;
+    bool is_argument = false;
+    bool is_value = false;
+    GArray *reg_data = NULL;
+
+    char c;
+    char *c_ptr;
+
+    xml_attrib attribute_j;
+    const char *argument_j;
+    const char *value_j;
+
+    for (i = 0; i < size; i++) {
+        c = xml[i];
+        c_ptr = &c;
+
+        if (still_to_skip > 0) {
+            /* skip unwanted chars */
+            still_to_skip--;
+            continue;
+        }
+
+        if (strncmp(&xml[i], "<reg", 4) == 0) {
+            /* start of a register */
+            still_to_skip = 3;
+            is_reg = true;
+            reg_data = g_array_new(false, true, sizeof(xml_attrib));
+        } else if (is_reg) {
+            if (strncmp(&xml[i], "/>", 2) == 0) {
+                /* end of register info */
+                still_to_skip = 1;
+                is_reg = false;
+
+                /* create empty register */
+                mcd_reg_st my_register = (const struct mcd_reg_st){ 0 };
+
+                /* add found attribtues */
+                for (j = 0; j < reg_data->len; j++) {
+                    attribute_j = g_array_index(reg_data, xml_attrib, j);
+
+                    argument_j = attribute_j.argument;
+                    value_j = attribute_j.value;
+
+                    if (strcmp(argument_j, "name") == 0) {
+                        strcpy(my_register.name, value_j);
+                    } else if (strcmp(argument_j, "regnum") == 0) {
+                        my_register.id = atoi(value_j);
+                    } else if (strcmp(argument_j, "bitsize") == 0) {
+                        my_register.bitsize = atoi(value_j);
+                    } else if (strcmp(argument_j, "type") == 0) {
+                        strcpy(my_register.type, value_j);
+                    } else if (strcmp(argument_j, "group") == 0) {
+                        strcpy(my_register.group, value_j);
+                    }
+                }
+                /* add reg_type, internal_id and id*/
+                my_register.reg_type = reg_type;
+                my_register.internal_id = internal_id;
+                internal_id++;
+                if (!my_register.id) {
+                    my_register.id = current_reg_id;
+                    current_reg_id++;
+                } else {
+                    /* set correct ID for the next register */
+                    current_reg_id = my_register.id + 1;
+                }
+                /* store register */
+                g_array_append_vals(registers, (gconstpointer)&my_register, 1);
+                /* free memory */
+                g_array_free(reg_data, false);
+            } else {
+                /* store info for register */
+                switch (c) {
+                case ' ':
+                    break;
+                case '=':
+                    is_argument = false;
+                    break;
+                case '"':
+                    if (is_value) {
+                        /* end of value reached */
+                        is_value = false;
+                        /* store arg-val combo */
+                        xml_attrib current_attribute;
+                        strcpy(current_attribute.argument, argument);
+                        strcpy(current_attribute.value, value);
+                        g_array_append_vals(reg_data,
+                        (gconstpointer)&current_attribute, 1);
+                        memset(argument, 0, sizeof(argument));
+                        memset(value, 0, sizeof(value));
+                    } else {
+                        /*start of value */
+                        is_value = true;
+                    }
+                    break;
+                default:
+                    if (is_argument) {
+                        strncat(argument, c_ptr, 1);
+                    } else if (is_value) {
+                        strncat(value, c_ptr, 1);
+                    } else {
+                        is_argument = true;
+                        strncat(argument, c_ptr, 1);
+                    }
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/debug/mcdstub/meson.build b/debug/mcdstub/meson.build
index 7e5ae878b0..3051a4e731 100644
--- a/debug/mcdstub/meson.build
+++ b/debug/mcdstub/meson.build
@@ -1,6 +1,6 @@
 # only system emulation is supported over mcd
 mcd_system_ss = ss.source_set()
-mcd_system_ss.add(files('mcdstub.c'))
+mcd_system_ss.add(files('mcdstub.c', 'arm_mcdstub.c'))
 mcd_system_ss = mcd_system_ss.apply(config_host, strict: false)
 
 libmcd_system = static_library('mcd_system',
diff --git a/include/mcdstub/arm_mcdstub.h b/include/mcdstub/arm_mcdstub.h
index c24aaf1202..9961145f07 100644
--- a/include/mcdstub/arm_mcdstub.h
+++ b/include/mcdstub/arm_mcdstub.h
@@ -16,3 +16,88 @@
  *
  * SPDX-License-Identifier: LGPL-2.0+
  */
+
+#ifndef ARM_MCDSTUB_H
+#define ARM_MCDSTUB_H
+
+#include "hw/core/cpu.h"
+#include "mcdstub_common.h"
+/* just used for the register xml files */
+#include "exec/gdbstub.h"
+
+/* ids for each different type of register */
+enum {
+    MCD_ARM_REG_TYPE_GPR,
+    MCD_ARM_REG_TYPE_VFP,
+    MCD_ARM_REG_TYPE_VFP_SYS,
+    MCD_ARM_REG_TYPE_MVE,
+    MCD_ARM_REG_TYPE_CPR,
+};
+
+/**
+ * arm_mcd_store_mem_spaces() - Stores all 32-Bit ARM specific memory spaces.
+ *
+ * This function stores the memory spaces into the memspaces GArray.
+ * It only stores secure memory spaces if the CPU has more than one address
+ * space. It also stores a GPR and a CP15 register memory space.
+ * @memspaces: GArray of memory spaces.
+ * @cpu: CPU state.
+ */
+int arm_mcd_store_mem_spaces(CPUState *cpu, GArray *memspaces);
+
+/**
+ * arm_mcd_parse_core_xml_file() - Parses the GPR registers.
+ *
+ * This function parses the core XML file, which includes the GPR registers.
+ * The regsters get stored in a GArray and a GPR register group is stored in a
+ * second GArray.
+ * @reggroups: GArray of register groups.
+ * @registers: GArray of registers.
+ * @cc: The CPU class.
+ * @current_group_id: The current group ID. It increases after
+ * each group.
+ */
+int arm_mcd_parse_core_xml_file(CPUClass *cc, GArray *reggroups,
+    GArray *registers, int *current_group_id);
+
+/**
+ * arm_mcd_parse_general_xml_files() - Parses all but the GPR registers.
+ *
+ * This function parses all XML files except for the core XML file.
+ * The regsters get stored in a GArray and if the system-registers.xml file is
+ * parsed, it also adds a CP15 register group.
+ * @reggroups: GArray of register groups.
+ * @registers: GArray of registers.
+ * @cpu: The CPU state.
+ * @current_group_id: The current group ID. It increases after
+ * each added group.
+ */
+int arm_mcd_parse_general_xml_files(CPUState *cpu, GArray* reggroups,
+    GArray *registers, int *current_group_id);
+
+/**
+ * arm_mcd_get_additional_register_info() - Adds additional data to parsed
+ * registers.
+ *
+ * This function is called, after :c:func:`arm_mcd_parse_core_xml_file` and
+ * :c:func:`arm_mcd_parse_general_xml_files`. It adds additional data for all
+ * already parsed registers. The registers get a correct ID, group, memory
+ * space and opcode, if they are CP15 registers.
+ * @reggroups: GArray of register groups.
+ * @registers: GArray of registers.
+ * @cpu: The CPU state.
+ */
+int arm_mcd_get_additional_register_info(GArray *reggroups, GArray *registers,
+    CPUState *cpu);
+
+/**
+ * arm_mcd_get_opcode() - Returns the opcode for a coprocessor register.
+ *
+ * This function uses the opc1, opc2, crm and crn members of the register to
+ * create the opcode. The formular for creating the opcode is determined by ARM.
+ * @n: The register ID of the CP register.
+ * @cs: CPU state.
+ */
+uint16_t arm_mcd_get_opcode(CPUState *cs, uint32_t n);
+
+#endif /* ARM_MCDSTUB_H */
diff --git a/include/mcdstub/mcdstub.h b/include/mcdstub/mcdstub.h
index 26aa33c0e3..ac14b2cda8 100644
--- a/include/mcdstub/mcdstub.h
+++ b/include/mcdstub/mcdstub.h
@@ -151,11 +151,6 @@ typedef struct MCDState {
 /* lives in mcdstub.c */
 extern MCDState mcdserver_state;
 
-typedef struct xml_attrib {
-    char argument[ARGUMENT_STRING_LENGTH];
-    char value[ARGUMENT_STRING_LENGTH];
-} xml_attrib;
-
 typedef struct mcd_reset_st {
     const char *name;
     uint8_t id;
diff --git a/include/mcdstub/mcdstub_common.h b/include/mcdstub/mcdstub_common.h
index b64748c080..d6ff55005e 100644
--- a/include/mcdstub/mcdstub_common.h
+++ b/include/mcdstub/mcdstub_common.h
@@ -61,4 +61,23 @@ typedef struct mcd_reg_group_st {
     uint32_t id;
 } mcd_reg_group_st;
 
+typedef struct xml_attrib {
+    char argument[ARGUMENT_STRING_LENGTH];
+    char value[ARGUMENT_STRING_LENGTH];
+} xml_attrib;
+
+/**
+ * parse_reg_xml() -  Parses a GDB register XML file
+ *
+ * This fuction extracts all registers from the provided xml file and stores
+ * them into the registers GArray. It extracts the register name, bitsize, type
+ * and group if they are set.
+ * @xml: String with contents of the XML file.
+ * @registers: GArray with stored registers.
+ * @reg_type: Register type (depending on file).
+ * @size: Number of characters in the xml string.
+ */
+void parse_reg_xml(const char *xml, int size, GArray* registers,
+    uint8_t reg_type, uint32_t reg_id_offset);
+
 #endif /* MCDSTUB_COMMON_H */
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 12/18] mcdstub: all core specific queries added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (10 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 11/18] mcdstub: system and core queries added Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 13/18] mcdstub: go, step and break added Nicolas Eder
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/mcdstub.c | 365 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 365 insertions(+)

diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index 4095b3f8ce..e90fc81814 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -893,6 +893,301 @@ static void handle_query_cores(GArray *params, void *user_ctx)
     mcd_put_strbuf();
 }
 
+/**
+ * handle_query_reset_f() - Handler for the first reset query.
+ *
+ * This function sends the first reset name and ID.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_reset_f(GArray *params, void *user_ctx)
+{
+    /* 1. check length */
+    int nb_resets = mcdserver_state.resets->len;
+    if (nb_resets == 1) {
+        /* indicates this is the last packet */
+        g_string_printf(mcdserver_state.str_buf, "0%s", QUERY_END_INDEX);
+    } else {
+        g_string_printf(mcdserver_state.str_buf, "1%s", QUERY_END_INDEX);
+    }
+    /* 2. send data */
+    mcd_reset_st reset = g_array_index(mcdserver_state.resets, mcd_reset_st, 0);
+    g_string_append_printf(mcdserver_state.str_buf, "%s=%s.%s=%u.",
+        TCP_ARGUMENT_NAME, reset.name, TCP_ARGUMENT_ID, reset.id);
+    mcd_put_strbuf();
+}
+
+/**
+ * handle_query_reset_c() - Handler for all consecutive reset queries.
+ *
+ * This functions sends all consecutive reset names and IDs. It uses the
+ * query_index parameter to determine which reset is queried next.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_reset_c(GArray *params, void *user_ctx)
+{
+    /* reset options are the same for every cpu! */
+    uint32_t query_index = get_param(params, 0)->query_handle;
+
+    /* 1. check weather this was the last mem space */
+    int nb_groups = mcdserver_state.resets->len;
+    if (query_index + 1 == nb_groups) {
+        /* indicates this is the last packet */
+        g_string_printf(mcdserver_state.str_buf, "0%s", QUERY_END_INDEX);
+    } else {
+        g_string_printf(mcdserver_state.str_buf, "%u!", query_index + 1);
+    }
+
+    /* 2. send data */
+    mcd_reset_st reset = g_array_index(mcdserver_state.resets,
+        mcd_reset_st, query_index);
+    g_string_append_printf(mcdserver_state.str_buf, "%s=%s.%s=%u.",
+        TCP_ARGUMENT_NAME, reset.name, TCP_ARGUMENT_ID, reset.id);
+    mcd_put_strbuf();
+}
+
+/**
+ * handle_query_trigger() - Handler for trigger query.
+ *
+ * Sends data on the different types of trigger and their options and actions.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_trigger(GArray *params, void *user_ctx)
+{
+    mcd_trigger_into_st trigger = mcdserver_state.trigger;
+    g_string_printf(mcdserver_state.str_buf, "%s=%u.%s=%s.%s=%s.%s=%s.",
+        TCP_ARGUMENT_AMOUNT_TRIGGER, trigger.nr_trigger,
+        TCP_ARGUMENT_TYPE, trigger.type,
+        TCP_ARGUMENT_OPTION, trigger.option,
+        TCP_ARGUMENT_ACTION, trigger.action);
+    mcd_put_strbuf();
+}
+
+/**
+ * handle_query_mem_spaces_f() Handler for the first memory space query.
+ *
+ * This function sends the first memory space name, ID, type and accessing
+ * options.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_mem_spaces_f(GArray *params, void *user_ctx)
+{
+    /* 1. get correct memspaces and set the query_cpu */
+    uint32_t cpu_id = get_param(params, 0)->cpu_id;
+    mcdserver_state.query_cpu_id = cpu_id;
+    GArray *memspaces = g_list_nth_data(mcdserver_state.all_memspaces, cpu_id);
+
+    /* 2. check length */
+    int nb_groups = memspaces->len;
+    if (nb_groups == 1) {
+        /* indicates this is the last packet */
+        g_string_printf(mcdserver_state.str_buf, "0%s", QUERY_END_INDEX);
+    } else {
+        g_string_printf(mcdserver_state.str_buf, "1%s", QUERY_END_INDEX);
+    }
+
+    /* 3. send data */
+    mcd_mem_space_st space = g_array_index(memspaces, mcd_mem_space_st, 0);
+    g_string_append_printf(mcdserver_state.str_buf,
+        "%s=%s.%s=%u.%s=%u.%s=%u.%s=%u.%s=%u.%s=%ld.%s=%ld.%s=%u.",
+        TCP_ARGUMENT_NAME, space.name,
+        TCP_ARGUMENT_ID, space.id,
+        TCP_ARGUMENT_TYPE, space.type,
+        TCP_ARGUMENT_BITS_PER_MAU, space.bits_per_mau,
+        TCP_ARGUMENT_INVARIANCE, space.invariance,
+        TCP_ARGUMENT_ENDIAN, space.endian,
+        TCP_ARGUMENT_MIN, space.min_addr,
+        TCP_ARGUMENT_MAX, space.max_addr,
+        TCP_ARGUMENT_SUPPORTED_ACCESS_OPTIONS, space.supported_access_options);
+    mcd_put_strbuf();
+}
+
+/**
+ * handle_query_mem_spaces_c() - Handler for all consecutive memory space
+ * queries.
+ *
+ * This function sends all consecutive memory space names, IDs, types and
+ * accessing options.
+ * It uses the query_index parameter to determine
+ * which memory space is queried next.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_mem_spaces_c(GArray *params, void *user_ctx)
+{
+    /*
+     * this funcitons send all mem spaces except for the first
+     * 1. get parameter and memspace
+     */
+    uint32_t query_index = get_param(params, 0)->query_handle;
+    uint32_t cpu_id = mcdserver_state.query_cpu_id;
+    GArray *memspaces = g_list_nth_data(mcdserver_state.all_memspaces, cpu_id);
+
+    /* 2. check weather this was the last mem space */
+    int nb_groups = memspaces->len;
+    if (query_index + 1 == nb_groups) {
+        /* indicates this is the last packet */
+        g_string_printf(mcdserver_state.str_buf, "0%s", QUERY_END_INDEX);
+    } else {
+        g_string_printf(mcdserver_state.str_buf, "%u!", query_index + 1);
+    }
+
+    /* 3. send the correct memspace */
+    mcd_mem_space_st space = g_array_index(memspaces,
+        mcd_mem_space_st, query_index);
+    g_string_append_printf(mcdserver_state.str_buf,
+        "%s=%s.%s=%u.%s=%u.%s=%u.%s=%u.%s=%u.%s=%ld.%s=%ld.%s=%u.",
+        TCP_ARGUMENT_NAME, space.name,
+        TCP_ARGUMENT_ID, space.id,
+        TCP_ARGUMENT_TYPE, space.type,
+        TCP_ARGUMENT_BITS_PER_MAU, space.bits_per_mau,
+        TCP_ARGUMENT_INVARIANCE, space.invariance,
+        TCP_ARGUMENT_ENDIAN, space.endian,
+        TCP_ARGUMENT_MIN, space.min_addr,
+        TCP_ARGUMENT_MAX, space.max_addr,
+        TCP_ARGUMENT_SUPPORTED_ACCESS_OPTIONS, space.supported_access_options);
+    mcd_put_strbuf();
+}
+
+/**
+ * handle_query_reg_groups_f() - Handler for the first register group query.
+ *
+ * This function sends the first register group name and ID.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_reg_groups_f(GArray *params, void *user_ctx)
+{
+    /* 1. get correct reggroups and set the query_cpu */
+    uint32_t cpu_id = get_param(params, 0)->cpu_id;
+    mcdserver_state.query_cpu_id = cpu_id;
+    GArray *reggroups = g_list_nth_data(mcdserver_state.all_reggroups, cpu_id);
+
+    /* 2. check length */
+    int nb_groups = reggroups->len;
+    if (nb_groups == 1) {
+        /* indicates this is the last packet */
+        g_string_printf(mcdserver_state.str_buf, "0%s", QUERY_END_INDEX);
+    } else {
+        g_string_printf(mcdserver_state.str_buf, "1%s", QUERY_END_INDEX);
+    }
+    /* 3. send data */
+    mcd_reg_group_st group = g_array_index(reggroups, mcd_reg_group_st, 0);
+    g_string_append_printf(mcdserver_state.str_buf, "%s=%u.%s=%s.",
+        TCP_ARGUMENT_ID, group.id, TCP_ARGUMENT_NAME, group.name);
+    mcd_put_strbuf();
+}
+
+/**
+ * handle_query_reg_groups_c() - Handler for all consecutive register group
+ * queries.
+ *
+ * This function sends all consecutive register group names and IDs. It uses
+ * the query_index parameter to determine which register group is queried next.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_reg_groups_c(GArray *params, void *user_ctx)
+{
+    /*
+     * this funcitons send all reg groups except for the first
+     * 1. get parameter and memspace
+     */
+    uint32_t query_index = get_param(params, 0)->query_handle;
+    uint32_t cpu_id = mcdserver_state.query_cpu_id;
+    GArray *reggroups = g_list_nth_data(mcdserver_state.all_reggroups, cpu_id);
+
+    /* 2. check weather this was the last reg group */
+    int nb_groups = reggroups->len;
+    if (query_index + 1 == nb_groups) {
+        /* indicates this is the last packet */
+        g_string_printf(mcdserver_state.str_buf, "0%s", QUERY_END_INDEX);
+    } else {
+        g_string_printf(mcdserver_state.str_buf, "%u!", query_index + 1);
+    }
+
+    /* 3. send the correct reggroup */
+    mcd_reg_group_st group = g_array_index(reggroups, mcd_reg_group_st,
+        query_index);
+    g_string_append_printf(mcdserver_state.str_buf, "%s=%u.%s=%s.",
+        TCP_ARGUMENT_ID, group.id, TCP_ARGUMENT_NAME, group.name);
+    mcd_put_strbuf();
+}
+
+/**
+ * handle_query_regs_f() - Handler for the first register query.
+ *
+ * This function sends the first register with all its information.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_regs_f(GArray *params, void *user_ctx)
+{
+    /* 1. get correct registers and set the query_cpu */
+    uint32_t cpu_id = get_param(params, 0)->cpu_id;
+    mcdserver_state.query_cpu_id = cpu_id;
+    GArray *registers = g_list_nth_data(mcdserver_state.all_registers, cpu_id);
+
+    /* 2. check length */
+    int nb_regs = registers->len;
+    if (nb_regs == 1) {
+        /* indicates this is the last packet */
+        g_string_printf(mcdserver_state.str_buf, "0%s", QUERY_END_INDEX);
+    } else {
+        g_string_printf(mcdserver_state.str_buf, "1%s", QUERY_END_INDEX);
+    }
+    /* 3. send data */
+    mcd_reg_st my_register = g_array_index(registers, mcd_reg_st, 0);
+    g_string_append_printf(mcdserver_state.str_buf,
+        "%s=%u.%s=%s.%s=%u.%s=%u.%s=%u.%s=%u.%s=%u.%s=%u.",
+        TCP_ARGUMENT_ID, my_register.id,
+        TCP_ARGUMENT_NAME, my_register.name,
+        TCP_ARGUMENT_SIZE, my_register.bitsize,
+        TCP_ARGUMENT_REGGROUPID, my_register.mcd_reg_group_id,
+        TCP_ARGUMENT_MEMSPACEID, my_register.mcd_mem_space_id,
+        TCP_ARGUMENT_TYPE, my_register.mcd_reg_type,
+        TCP_ARGUMENT_THREAD, my_register.mcd_hw_thread_id,
+        TCP_ARGUMENT_OPCODE, my_register.opcode);
+    mcd_put_strbuf();
+}
+
+/**
+ * handle_query_regs_c() - Handler for all consecutive register queries.
+ *
+ * This function sends all consecutive registers with all their information.
+ * It uses the query_index parameter to determine
+ * which register is queried next.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_regs_c(GArray *params, void *user_ctx)
+{
+    /*
+     * this funcitons send all regs except for the first
+     * 1. get parameter and registers
+     */
+    uint32_t query_index = get_param(params, 0)->query_handle;
+    uint32_t cpu_id = mcdserver_state.query_cpu_id;
+    GArray *registers = g_list_nth_data(mcdserver_state.all_registers, cpu_id);
+
+    /* 2. check weather this was the last register */
+    int nb_regs = registers->len;
+    if (query_index + 1 == nb_regs) {
+        /* indicates this is the last packet */
+        g_string_printf(mcdserver_state.str_buf, "0%s", QUERY_END_INDEX);
+    } else {
+        g_string_printf(mcdserver_state.str_buf, "%u!", query_index + 1);
+    }
+
+    /* 3. send the correct register */
+    mcd_reg_st my_register = g_array_index(registers, mcd_reg_st, query_index);
+    g_string_append_printf(mcdserver_state.str_buf,
+        "%s=%u.%s=%s.%s=%u.%s=%u.%s=%u.%s=%u.%s=%u.%s=%u.",
+        TCP_ARGUMENT_ID, my_register.id,
+        TCP_ARGUMENT_NAME, my_register.name,
+        TCP_ARGUMENT_SIZE, my_register.bitsize,
+        TCP_ARGUMENT_REGGROUPID, my_register.mcd_reg_group_id,
+        TCP_ARGUMENT_MEMSPACEID, my_register.mcd_mem_space_id,
+        TCP_ARGUMENT_TYPE, my_register.mcd_reg_type,
+        TCP_ARGUMENT_THREAD, my_register.mcd_hw_thread_id,
+        TCP_ARGUMENT_OPCODE, my_register.opcode);
+    mcd_put_strbuf();
+}
+
 /**
  * init_query_cmds_table() - Initializes all query functions.
  *
@@ -918,6 +1213,76 @@ static void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table)
     };
     mcd_query_cmds_table[cmd_number] = query_cores;
     cmd_number++;
+
+    MCDCmdParseEntry query_reset_f = {
+        .handler = handle_query_reset_f,
+        .cmd = QUERY_ARG_RESET QUERY_FIRST,
+    };
+    mcd_query_cmds_table[cmd_number] = query_reset_f;
+    cmd_number++;
+
+    MCDCmdParseEntry query_reset_c = {
+        .handler = handle_query_reset_c,
+        .cmd = QUERY_ARG_RESET QUERY_CONSEQUTIVE,
+    };
+    strcpy(query_reset_c.schema, (char[2]) { ARG_SCHEMA_QRYHANDLE, '\0' });
+    mcd_query_cmds_table[cmd_number] = query_reset_c;
+    cmd_number++;
+
+    MCDCmdParseEntry query_trigger = {
+        .handler = handle_query_trigger,
+        .cmd = QUERY_ARG_TRIGGER,
+    };
+    mcd_query_cmds_table[cmd_number] = query_trigger;
+    cmd_number++;
+
+    MCDCmdParseEntry query_mem_spaces_f = {
+        .handler = handle_query_mem_spaces_f,
+        .cmd = QUERY_ARG_MEMORY QUERY_FIRST,
+    };
+    strcpy(query_mem_spaces_f.schema, (char[2]) { ARG_SCHEMA_CORENUM, '\0' });
+    mcd_query_cmds_table[cmd_number] = query_mem_spaces_f;
+    cmd_number++;
+
+    MCDCmdParseEntry query_mem_spaces_c = {
+        .handler = handle_query_mem_spaces_c,
+        .cmd = QUERY_ARG_MEMORY QUERY_CONSEQUTIVE,
+    };
+    strcpy(query_mem_spaces_c.schema, (char[2]) { ARG_SCHEMA_QRYHANDLE, '\0' });
+    mcd_query_cmds_table[cmd_number] = query_mem_spaces_c;
+    cmd_number++;
+
+    MCDCmdParseEntry query_reg_groups_f = {
+        .handler = handle_query_reg_groups_f,
+        .cmd = QUERY_ARG_REGGROUP QUERY_FIRST,
+    };
+    strcpy(query_reg_groups_f.schema, (char[2]) { ARG_SCHEMA_CORENUM, '\0' });
+    mcd_query_cmds_table[cmd_number] = query_reg_groups_f;
+    cmd_number++;
+
+    MCDCmdParseEntry query_reg_groups_c = {
+        .handler = handle_query_reg_groups_c,
+        .cmd = QUERY_ARG_REGGROUP QUERY_CONSEQUTIVE,
+    };
+    strcpy(query_reg_groups_c.schema, (char[2]) { ARG_SCHEMA_QRYHANDLE, '\0' });
+    mcd_query_cmds_table[cmd_number] = query_reg_groups_c;
+    cmd_number++;
+
+    MCDCmdParseEntry query_regs_f = {
+        .handler = handle_query_regs_f,
+        .cmd = QUERY_ARG_REG QUERY_FIRST,
+    };
+    strcpy(query_regs_f.schema, (char[2]) { ARG_SCHEMA_CORENUM, '\0' });
+    mcd_query_cmds_table[cmd_number] = query_regs_f;
+    cmd_number++;
+
+    MCDCmdParseEntry query_regs_c = {
+        .handler = handle_query_regs_c,
+        .cmd = QUERY_ARG_REG QUERY_CONSEQUTIVE,
+    };
+    strcpy(query_regs_c.schema, (char[2]) { ARG_SCHEMA_QRYHANDLE, '\0' });
+    mcd_query_cmds_table[cmd_number] = query_regs_c;
+    cmd_number++;
 }
 
 /**
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 13/18] mcdstub: go, step and break added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (11 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 12/18] mcdstub: all core specific " Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 14/18] mcdstub: state query added Nicolas Eder
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/mcdstub.c | 218 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 218 insertions(+)

diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index e90fc81814..ee52830a2c 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -28,6 +28,7 @@
 #include "chardev/char-fe.h"
 #include "hw/cpu/cluster.h"
 #include "hw/boards.h"
+#include "exec/tb-flush.h"
 #include "sysemu/cpus.h"
 #include "sysemu/hw_accel.h"
 #include "sysemu/runstate.h"
@@ -182,6 +183,96 @@ static CPUState *mcd_get_cpu(uint32_t cpu_index)
  */
 static void mcd_vm_state_change(void *opaque, bool running, RunState state)
 {
+    CPUState *cpu = mcdserver_state.c_cpu;
+
+    if (mcdserver_state.state == RS_INACTIVE) {
+        return;
+    }
+
+    if (cpu == NULL) {
+        if (running) {
+            /*
+             * this is the case if qemu starts the vm
+             * before a mcd client is connected
+             */
+            const char *mcd_state;
+            mcd_state = CORE_STATE_RUNNING;
+            const char *info_str;
+            info_str = STATE_STR_INIT_RUNNING;
+            mcdserver_state.cpu_state.state = mcd_state;
+            mcdserver_state.cpu_state.info_str = info_str;
+        }
+        return;
+    }
+
+    const char *mcd_state;
+    const char *stop_str;
+    const char *info_str;
+    uint32_t bp_type = 0;
+    uint64_t bp_address = 0;
+    switch (state) {
+    case RUN_STATE_RUNNING:
+        mcd_state = CORE_STATE_RUNNING;
+        info_str = STATE_STR_RUNNING(cpu->cpu_index);
+        stop_str = "";
+        break;
+    case RUN_STATE_DEBUG:
+        mcd_state = CORE_STATE_DEBUG;
+        info_str = STATE_STR_DEBUG(cpu->cpu_index);
+        if (cpu->watchpoint_hit) {
+            switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) {
+            case BP_MEM_READ:
+                bp_type = MCD_BREAKPOINT_READ;
+                stop_str = STATE_STR_BREAK_READ(cpu->watchpoint_hit->hitaddr);
+                break;
+            case BP_MEM_WRITE:
+                bp_type = MCD_BREAKPOINT_WRITE;
+                stop_str = STATE_STR_BREAK_WRITE(cpu->watchpoint_hit->hitaddr);
+                break;
+            case BP_MEM_ACCESS:
+                bp_type = MCD_BREAKPOINT_RW;
+                stop_str = STATE_STR_BREAK_RW(cpu->watchpoint_hit->hitaddr);
+                break;
+            default:
+                stop_str = STATE_STR_BREAK_UNKNOWN;
+                break;
+            }
+            bp_address = cpu->watchpoint_hit->hitaddr;
+            cpu->watchpoint_hit = NULL;
+        } else if (cpu->singlestep_enabled) {
+            /* we land here when a single step is performed */
+            stop_str = STATE_STEP_PERFORMED;
+        } else {
+            bp_type = MCD_BREAKPOINT_HW;
+            stop_str = STATE_STR_BREAK_HW;
+            tb_flush(cpu);
+        }
+        /* deactivate single step */
+        cpu_single_step(cpu, 0);
+        break;
+    case RUN_STATE_PAUSED:
+        info_str = STATE_STR_HALTED(cpu->cpu_index);
+        mcd_state = CORE_STATE_HALTED;
+        stop_str = "";
+        break;
+    case RUN_STATE_WATCHDOG:
+        info_str = STATE_STR_UNKNOWN(cpu->cpu_index);
+        mcd_state = CORE_STATE_UNKNOWN;
+        stop_str = "";
+        break;
+    default:
+        info_str = STATE_STR_UNKNOWN(cpu->cpu_index);
+        mcd_state = CORE_STATE_UNKNOWN;
+        stop_str = "";
+        break;
+    }
+
+    /* set state for c_cpu */
+    mcdserver_state.cpu_state.state = mcd_state;
+    mcdserver_state.cpu_state.bp_type = bp_type;
+    mcdserver_state.cpu_state.bp_address = bp_address;
+    mcdserver_state.cpu_state.stop_str = stop_str;
+    mcdserver_state.cpu_state.info_str = info_str;
 }
 
 /**
@@ -637,6 +728,104 @@ static void handle_close_core(GArray *params, void *user_ctx)
     g_array_free(registers, TRUE);
 }
 
+/**
+ * mcd_cpu_start() - Starts the selected CPU with the cpu_resume function.
+ *
+ * @cpu: The CPU about to be started.
+ */
+static void mcd_cpu_start(CPUState *cpu)
+{
+    if (!runstate_needs_reset() && !runstate_is_running() &&
+        !vm_prepare_start(false)) {
+        mcdserver_state.c_cpu = cpu;
+        qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true);
+        cpu_resume(cpu);
+    }
+}
+
+/**
+ * mcd_cpu_sstep() - Performes a step on the selected CPU.
+ *
+ * This function first sets the correct single step flags for the CPU with
+ * cpu_single_step and then starts the CPU with cpu_resume.
+ * @cpu: The CPU about to be stepped.
+ */
+static int mcd_cpu_sstep(CPUState *cpu)
+{
+    mcdserver_state.c_cpu = cpu;
+    cpu_single_step(cpu, mcdserver_state.sstep_flags);
+    if (!runstate_needs_reset() && !runstate_is_running() &&
+        !vm_prepare_start(true)) {
+        qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true);
+        cpu_resume(cpu);
+    }
+    return 0;
+}
+
+/**
+ * mcd_vm_stop() - Brings all CPUs in debug state with the vm_stop function.
+ */
+static void mcd_vm_stop(void)
+{
+    if (runstate_is_running()) {
+        vm_stop(RUN_STATE_DEBUG);
+    }
+}
+
+/**
+ * handle_vm_start() - Handler for the VM start TCP packet.
+ *
+ * Evaluates whether all cores or just a perticular core should get started and
+ * calls :c:func:`mcd_vm_start` or :c:func:`mcd_cpu_start` respectively.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_vm_start(GArray *params, void *user_ctx)
+{
+    uint32_t global = get_param(params, 0)->data_uint32_t;
+    if (global == 1) {
+        mcd_vm_start();
+    } else{
+        uint32_t cpu_id = get_param(params, 1)->cpu_id;
+        CPUState *cpu = mcd_get_cpu(cpu_id);
+        mcd_cpu_start(cpu);
+    }
+}
+
+/**
+ * handle_vm_step() - Handler for the VM step TCP packet.
+ *
+ * Calls :c:func:`mcd_cpu_sstep` for the CPU which sould be stepped.
+ * Stepping all CPUs is currently not supported.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_vm_step(GArray *params, void *user_ctx)
+{
+    uint32_t global = get_param(params, 0)->data_uint32_t;
+    if (global == 1) {
+        /* TODO: add multicore support */
+    } else{
+        uint32_t cpu_id = get_param(params, 1)->cpu_id;
+        CPUState *cpu = mcd_get_cpu(cpu_id);
+        int return_value = mcd_cpu_sstep(cpu);
+        if (return_value != 0) {
+            g_assert_not_reached();
+        }
+    }
+}
+
+/**
+ * handle_vm_stop() - Handler for the VM stop TCP packet.
+ *
+ * Always calls :c:func:`mcd_vm_stop` and stops all cores. Stopping individual
+ * cores is currently not supported.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_vm_stop(GArray *params, void *user_ctx)
+{
+    /* TODO: add core dependant break option */
+    mcd_vm_stop();
+}
+
 /**
  * mcd_handle_packet() - Evaluates the type of received packet and chooses the
  * correct handler.
@@ -703,6 +892,35 @@ static int mcd_handle_packet(const char *line_buf)
             cmd_parser = &close_core_cmd_desc;
         }
         break;
+    case TCP_CHAR_GO:
+        {
+            static MCDCmdParseEntry go_cmd_desc = {
+                .handler = handle_vm_start,
+                .cmd = {TCP_CHAR_GO, '\0'},
+                .schema = {ARG_SCHEMA_INT, ARG_SCHEMA_CORENUM, '\0'},
+            };
+            cmd_parser = &go_cmd_desc;
+        }
+        break;
+    case TCP_CHAR_STEP:
+        {
+            static MCDCmdParseEntry step_cmd_desc = {
+                .handler = handle_vm_step,
+                .cmd = {TCP_CHAR_STEP, '\0'},
+                .schema = {ARG_SCHEMA_INT, ARG_SCHEMA_CORENUM, '\0'},
+            };
+            cmd_parser = &step_cmd_desc;
+        }
+        break;
+    case TCP_CHAR_BREAK:
+        {
+            static MCDCmdParseEntry break_cmd_desc = {
+                .handler = handle_vm_stop,
+                .cmd = {TCP_CHAR_BREAK, '\0'},
+            };
+            cmd_parser = &break_cmd_desc;
+        }
+        break;
     default:
         /* command not supported */
         mcd_put_packet("");
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 14/18] mcdstub: state query added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (12 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 13/18] mcdstub: go, step and break added Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 15/18] mcdstub: skeleton for reset handling added Nicolas Eder
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/mcdstub.c | 44 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index ee52830a2c..fb13958108 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -1406,6 +1406,43 @@ static void handle_query_regs_c(GArray *params, void *user_ctx)
     mcd_put_strbuf();
 }
 
+/**
+ * handle_query_state() - Handler for the state query.
+ *
+ * This function collects all data stored in the
+ * cpu_state member of the mcdserver_state and formats and sends it to the
+ * library.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_query_state(GArray *params, void *user_ctx)
+{
+    /*
+     * TODO: multicore support
+     * get state info
+     */
+    mcd_cpu_state_st state_info = mcdserver_state.cpu_state;
+    /* TODO: add event information */
+    uint32_t event = 0;
+    /* send data */
+    g_string_printf(mcdserver_state.str_buf,
+        "%s=%s.%s=%u.%s=%u.%s=%u.%s=%lu.%s=%s.%s=%s.",
+        TCP_ARGUMENT_STATE, state_info.state,
+        TCP_ARGUMENT_EVENT, event, TCP_ARGUMENT_THREAD, 0,
+        TCP_ARGUMENT_TYPE, state_info.bp_type,
+        TCP_ARGUMENT_ADDRESS, state_info.bp_address,
+        TCP_ARGUMENT_STOP_STRING, state_info.stop_str,
+        TCP_ARGUMENT_INFO_STRING, state_info.info_str);
+    mcd_put_strbuf();
+
+    /* reset debug info after first query */
+    if (strcmp(state_info.state, CORE_STATE_DEBUG) == 0) {
+        mcdserver_state.cpu_state.stop_str = "";
+        mcdserver_state.cpu_state.info_str = "";
+        mcdserver_state.cpu_state.bp_type = 0;
+        mcdserver_state.cpu_state.bp_address = 0;
+    }
+}
+
 /**
  * init_query_cmds_table() - Initializes all query functions.
  *
@@ -1501,6 +1538,13 @@ static void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table)
     strcpy(query_regs_c.schema, (char[2]) { ARG_SCHEMA_QRYHANDLE, '\0' });
     mcd_query_cmds_table[cmd_number] = query_regs_c;
     cmd_number++;
+
+    MCDCmdParseEntry query_state = {
+        .handler = handle_query_state,
+        .cmd = QUERY_ARG_STATE,
+    };
+    strcpy(query_state.schema, (char[2]) { ARG_SCHEMA_CORENUM, '\0' });
+    mcd_query_cmds_table[cmd_number] = query_state;
 }
 
 /**
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 15/18] mcdstub: skeleton for reset handling added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (13 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 14/18] mcdstub: state query added Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 16/18] mcdstub: register access added Nicolas Eder
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/mcdstub.c | 46 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index fb13958108..df97eca65b 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -826,6 +826,37 @@ static void handle_vm_stop(GArray *params, void *user_ctx)
     mcd_vm_stop();
 }
 
+/**
+ * mcd_exit() - Terminates QEMU.
+ *
+ * If the mcdserver_state has not been initialized the function exits before
+ * terminating QEMU. Terminting is done with the qemu_chr_fe_deinit function.
+ * @code: An exitcode, which can be used in the future.
+ */
+static void mcd_exit(int code)
+{
+    /* terminate qemu */
+    if (!mcdserver_state.init) {
+        return;
+    }
+
+    qemu_chr_fe_deinit(&mcdserver_system_state.chr, true);
+}
+
+/**
+ * handle_reset() - Handler for performing resets.
+ *
+ * This function is currently not in use.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_reset(GArray *params, void *user_ctx)
+{
+    /*
+     * int reset_id = get_param(params, 0)->data_int;
+     * TODO: implement resets
+     */
+}
+
 /**
  * mcd_handle_packet() - Evaluates the type of received packet and chooses the
  * correct handler.
@@ -921,6 +952,21 @@ static int mcd_handle_packet(const char *line_buf)
             cmd_parser = &break_cmd_desc;
         }
         break;
+    case TCP_CHAR_KILLQEMU:
+        /* kill qemu completely */
+        error_report("QEMU: Terminated via MCDstub");
+        mcd_exit(0);
+        exit(0);
+    case TCP_CHAR_RESET:
+        {
+            static MCDCmdParseEntry reset_cmd_desc = {
+                .handler = handle_reset,
+                .cmd = {TCP_CHAR_RESET, '\0'},
+                .schema = {ARG_SCHEMA_INT, '\0'},
+            };
+            cmd_parser = &reset_cmd_desc;
+        }
+        break;
     default:
         /* command not supported */
         mcd_put_packet("");
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 16/18] mcdstub: register access added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (14 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 15/18] mcdstub: skeleton for reset handling added Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 17/18] mcdstub: memory " Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 18/18] mcdstub: break/watchpoints added Nicolas Eder
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/mcdstub.c | 161 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 161 insertions(+)

diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index df97eca65b..df98453558 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -857,6 +857,146 @@ static void handle_reset(GArray *params, void *user_ctx)
      */
 }
 
+/**
+ * mcd_memtohex() - Converts a byte array into a hex string.
+ *
+ * @mem: Pointer to byte array.
+ * @buf: Pointer to hex string.
+ * @len: Number of bytes.
+ */
+static void mcd_memtohex(GString *buf, const uint8_t *mem, int len)
+{
+    int i, c;
+    for (i = 0; i < len; i++) {
+        c = mem[i];
+        g_string_append_c(buf, nibble_to_hexchar(c >> 4));
+        g_string_append_c(buf, nibble_to_hexchar(c & 0xf));
+    }
+    g_string_append_c(buf, '\0');
+}
+
+/**
+ * mcd_hextomem() - Converts a hex string into a byte array.
+ *
+ * @mem: Pointer to byte array.
+ * @buf: Pointer to hex string.
+ * @len: Number of bytes.
+ */
+static void mcd_hextomem(GByteArray *mem, const char *buf, int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++) {
+        guint8 byte = hexchar_to_nibble(buf[0]) << 4 |
+                      hexchar_to_nibble(buf[1]);
+        g_byte_array_append(mem, &byte, 1);
+        buf += 2;
+    }
+}
+
+/**
+ * mcd_read_register() - Reads a registers data and stores it into the buf.
+ *
+ * This function collects the register type and internal ID
+ * (depending on the XML file). Then it calls the architecture specific
+ * read function. For ARM this is :c:func:`arm_mcd_read_register`.
+ * @cpu: CPU to which the register belongs.
+ * @buf: Byte array with register data.
+ * @reg: General ID of the register.
+ */
+static int mcd_read_register(CPUState *cpu, GByteArray *buf, int reg)
+{
+    CPUClass *cc = CPU_GET_CLASS(cpu);
+    CPUArchState *env = cpu_env(cpu);
+    GDBRegisterState *r;
+
+    if (reg < cc->gdb_num_core_regs) {
+        return cc->gdb_read_register(cpu, buf, reg);
+    }
+
+    for (guint i = 0; i < cpu->gdb_regs->len; i++) {
+        r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
+        if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) {
+            return r->get_reg(env, buf, reg - r->base_reg);
+        }
+    }
+    return 0;
+}
+
+/**
+ * mcd_write_register() - Writes data from the buf to a register.
+ *
+ * This function collects the register type and internal ID
+ * (depending on the XML file). Then it calls the architecture specific
+ * write function. For ARM this is :c:func:`arm_mcd_write_register`.
+ * @cpu: CPU to which the register belongs.
+ * @mem_buf: Byte array with register data.
+ * @reg: General ID of the register.
+ */
+static int mcd_write_register(CPUState *cpu, uint8_t *mem_buf, int reg)
+{
+    CPUClass *cc = CPU_GET_CLASS(cpu);
+    CPUArchState *env = cpu_env(cpu);
+    GDBRegisterState *r;
+
+    if (reg < cc->gdb_num_core_regs) {
+        return cc->gdb_write_register(cpu, mem_buf, reg);
+    }
+
+    for (guint i = 0; i < cpu->gdb_regs->len; i++) {
+        r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
+        if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) {
+            return r->set_reg(env, mem_buf, reg - r->base_reg);
+        }
+    }
+    return 0;
+}
+
+/**
+ * handle_read_register() - Handler for reading a register.
+ *
+ * This function calls :c:func:`mcd_read_register` to read a register. The
+ * register data gets stored in the mem_buf byte array. The data then gets
+ * converted into a hex string with :c:func:`mcd_memtohex` and then send.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_read_register(GArray *params, void *user_ctx)
+{
+    uint32_t cpu_id = get_param(params, 0)->cpu_id;
+    uint64_t reg_num = get_param(params, 1)->data_uint64_t;
+    int reg_size;
+
+    CPUState *cpu = mcd_get_cpu(cpu_id);
+    reg_size = mcd_read_register(cpu, mcdserver_state.mem_buf, reg_num);
+    mcd_memtohex(mcdserver_state.str_buf,
+        mcdserver_state.mem_buf->data, reg_size);
+    mcd_put_strbuf();
+}
+
+/**
+ * handle_write_register() - Handler for writing a register.
+ *
+ * This function converts the incoming hex string data into a byte array with
+ * :c:func:`mcd_hextomem`. Then it calls :c:func:`mcd_write_register` to write
+ * to the register.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_write_register(GArray *params, void *user_ctx)
+{
+    uint32_t cpu_id = get_param(params, 0)->cpu_id;
+    uint64_t reg_num = get_param(params, 1)->data_uint64_t;
+    uint32_t reg_size = get_param(params, 2)->data_uint32_t;
+
+    CPUState *cpu = mcd_get_cpu(cpu_id);
+    mcd_hextomem(mcdserver_state.mem_buf,
+        mcdserver_state.str_buf->str, reg_size);
+    if (mcd_write_register(cpu, mcdserver_state.mem_buf->data, reg_num) == 0) {
+        mcd_put_packet(TCP_EXECUTION_ERROR);
+    } else {
+        mcd_put_packet(TCP_EXECUTION_SUCCESS);
+    }
+}
+
 /**
  * mcd_handle_packet() - Evaluates the type of received packet and chooses the
  * correct handler.
@@ -967,6 +1107,27 @@ static int mcd_handle_packet(const char *line_buf)
             cmd_parser = &reset_cmd_desc;
         }
         break;
+    case TCP_CHAR_READ_REGISTER:
+        {
+            static MCDCmdParseEntry read_reg_cmd_desc = {
+                .handler = handle_read_register,
+                .cmd = {TCP_CHAR_READ_REGISTER, '\0'},
+                .schema = {ARG_SCHEMA_CORENUM, ARG_SCHEMA_UINT64_T, '\0'},
+            };
+            cmd_parser = &read_reg_cmd_desc;
+        }
+        break;
+    case TCP_CHAR_WRITE_REGISTER:
+        {
+            static MCDCmdParseEntry write_reg_cmd_desc = {
+                .handler = handle_write_register,
+                .cmd = {TCP_CHAR_WRITE_REGISTER, '\0'},
+                .schema = {ARG_SCHEMA_CORENUM, ARG_SCHEMA_UINT64_T,
+                           ARG_SCHEMA_INT, ARG_SCHEMA_HEXDATA, '\0'},
+            };
+            cmd_parser = &write_reg_cmd_desc;
+        }
+        break;
     default:
         /* command not supported */
         mcd_put_packet("");
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 17/18] mcdstub: memory access added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (15 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 16/18] mcdstub: register access added Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  2023-12-20 16:25 ` [PATCH v5 18/18] mcdstub: break/watchpoints added Nicolas Eder
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/arm_mcdstub.c   |  28 +++++
 debug/mcdstub/mcdstub.c       | 203 ++++++++++++++++++++++++++++++++++
 include/mcdstub/arm_mcdstub.h |  15 +++
 3 files changed, 246 insertions(+)

diff --git a/debug/mcdstub/arm_mcdstub.c b/debug/mcdstub/arm_mcdstub.c
index ce5264a617..6eaf2d754f 100644
--- a/debug/mcdstub/arm_mcdstub.c
+++ b/debug/mcdstub/arm_mcdstub.c
@@ -18,6 +18,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "exec/memory.h"
 #include "mcdstub/arm_mcdstub.h"
 
 int arm_mcd_store_mem_spaces(CPUState *cpu, GArray *memspaces)
@@ -259,3 +260,30 @@ uint16_t arm_mcd_get_opcode(CPUState *cs, uint32_t n)
     /* TODO: not working with current build structure */
     return 0;
 }
+
+AddressSpace *arm_mcd_get_address_space(uint32_t cpu_id,
+    mcd_mem_space_st mem_space)
+{
+    /* get correct address space name */
+    char as_name[ARGUMENT_STRING_LENGTH] = {0};
+    if (mem_space.is_secure) {
+        sprintf(as_name, "cpu-secure-memory-%u", cpu_id);
+    } else {
+        sprintf(as_name, "cpu-memory-%u", cpu_id);
+    }
+    /* return correct address space */
+    AddressSpace *as = mcd_find_address_space(as_name);
+    return as;
+}
+
+MemTxAttrs arm_mcd_get_memtxattrs(mcd_mem_space_st mem_space)
+{
+    MemTxAttrs attributes = {0};
+    if (mem_space.is_secure) {
+        attributes.secure = 1;
+        attributes.space = 2;
+    } else {
+        attributes.space = 1;
+    }
+    return attributes;
+}
diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index df98453558..954d06c0b7 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -997,6 +997,186 @@ static void handle_write_register(GArray *params, void *user_ctx)
     }
 }
 
+/**
+ * mcd_read_write_physical_memory() - Reades or writes from/to a logical
+ * memory address.
+ * @address_space: Desired QEMU address space (e.g. secure/non-secure)
+ * @attributes: Access attributes
+ * @addr: (physical) memory address
+ * @buf: Buffer for memory data
+ * @len: Length of the memory access
+ * @is_write: True for writing and false for reading
+ */
+static int mcd_read_write_physical_memory(AddressSpace *address_space,
+    MemTxAttrs attributes, hwaddr addr, uint8_t *buf, int len, bool is_write)
+{
+    if (is_write) {
+        return address_space_write_rom(address_space, addr, attributes, buf,
+            len);
+    } else {
+        return address_space_read_full(address_space, addr, attributes, buf,
+            len);
+    }
+}
+
+/**
+ * mcd_read_write_memory() - Reades or writes from/to a logical memory address.
+ * @cpu: CPUState
+ * @address_space: Desired QEMU address space (e.g. secure/non-secure)
+ * @attributes: Access attributes
+ * @addr: (logical) memory address
+ * @buf: Buffer for memory data
+ * @len: Length of the memory access
+ * @is_write: True for writing and false for reading
+ */
+static int mcd_read_write_memory(CPUState *cpu, AddressSpace *address_space,
+    MemTxAttrs attributes, vaddr addr, uint8_t *buf, uint64_t len,
+    bool is_write)
+{
+    /* get physical address */
+    if (cpu_memory_get_physical_address(cpu, &addr, &len) != 0) {
+        return -1;
+    }
+    /* read memory */
+    return mcd_read_write_physical_memory(address_space, attributes, addr, buf,
+        len, is_write);
+}
+
+/**
+ * mcd_get_address_space() - Returnes the correct QEMU address space name
+ * @cpu: CPUState
+ * @cpu_id: Correct CPU ID
+ * @mem_space: Desired mcd specific memory space.
+ */
+static AddressSpace *mcd_get_address_space(CPUState *cpu, uint32_t cpu_id,
+    mcd_mem_space_st mem_space)
+{
+    CPUClass *cc = CPU_GET_CLASS(cpu);
+    const gchar *arch = cc->gdb_arch_name(cpu);
+    if (strcmp(arch, MCDSTUB_ARCH_ARM) == 0) {
+        return arm_mcd_get_address_space(cpu_id, mem_space);
+    } else {
+        g_assert_not_reached();
+    }
+}
+
+/**
+ * mcd_get_memtxattrs() - Returnes the correct QEMU address space access
+ * attributes
+ * @cpu: CPUState
+ * @mem_space: Desired mcd specific memory space.
+ */
+static MemTxAttrs mcd_get_memtxattrs(CPUState *cpu, mcd_mem_space_st mem_space)
+{
+    CPUClass *cc = CPU_GET_CLASS(cpu);
+    const gchar *arch = cc->gdb_arch_name(cpu);
+    if (strcmp(arch, MCDSTUB_ARCH_ARM) == 0) {
+        return arm_mcd_get_memtxattrs(mem_space);
+    } else {
+        g_assert_not_reached();
+    }
+}
+
+/**
+ * handle_read_memory() - Handler for reading memory.
+ *
+ * First, this function checks whether reading a secure memory space is
+ * requested and changes the access mode with :c:func:`arm_mcd_set_scr`.
+ * Then it calls :c:func:`mcd_read_memory` to read memory. The collected
+ * data gets stored in the mem_buf byte array. The data then gets converted
+ * into a hex string with :c:func:`mcd_memtohex` and then send.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_read_memory(GArray *params, void *user_ctx)
+{
+    /* read input parameters */
+    uint32_t cpu_id = get_param(params, 0)->cpu_id;
+    uint32_t mem_space_id = get_param(params, 1)->data_uint32_t;
+    uint64_t mem_address = get_param(params, 2)->data_uint64_t;
+    uint32_t len = get_param(params, 3)->data_uint32_t;
+    /* check which memory space was requested */
+    CPUState *cpu = mcd_get_cpu(cpu_id);
+    GArray *memspaces =
+        g_list_nth_data(mcdserver_state.all_memspaces, cpu_id);
+    mcd_mem_space_st space = g_array_index(memspaces, mcd_mem_space_st,
+        mem_space_id - 1);
+    /* get data in the QEMU address space and access attributes */
+    AddressSpace *address_space = mcd_get_address_space(cpu, cpu_id, space);
+    MemTxAttrs attributes = mcd_get_memtxattrs(cpu, space);
+    /* read memory data */
+    g_byte_array_set_size(mcdserver_state.mem_buf, len);
+    if (space.is_physical) {
+        /* physical memory */
+        if (mcd_read_write_physical_memory(address_space, attributes,
+            mem_address, mcdserver_state.mem_buf->data,
+            mcdserver_state.mem_buf->len, false) != 0) {
+            mcd_put_packet(TCP_EXECUTION_ERROR);
+            return;
+        }
+    } else {
+        /* user space memory */
+        if (mcd_read_write_memory(cpu, address_space, attributes, mem_address,
+            mcdserver_state.mem_buf->data, mcdserver_state.mem_buf->len,
+            false) != 0) {
+            mcd_put_packet(TCP_EXECUTION_ERROR);
+            return;
+        }
+    }
+    /* send data */
+    mcd_memtohex(mcdserver_state.str_buf, mcdserver_state.mem_buf->data,
+        mcdserver_state.mem_buf->len);
+    mcd_put_strbuf();
+}
+
+/**
+ * handle_write_memory() - Handler for writing memory.
+ *
+ * First, this function checks whether reading a secure memory space is
+ * requested and changes the access mode with :c:func:`arm_mcd_set_scr`.
+ * Then it converts the incoming hex string data into a byte array with
+ * :c:func:`mcd_hextomem`. Then it calls :c:func:`mcd_write_memory` to write to
+ * the register.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_write_memory(GArray *params, void *user_ctx)
+{
+    /* read input parameters */
+    uint32_t cpu_id = get_param(params, 0)->cpu_id;
+    uint32_t mem_space_id = get_param(params, 1)->data_uint32_t;
+    uint64_t mem_address = get_param(params, 2)->data_uint64_t;
+    uint32_t len = get_param(params, 3)->data_uint32_t;
+    /* check which memory space was requested */
+    CPUState *cpu = mcd_get_cpu(cpu_id);
+    GArray *memspaces =
+        g_list_nth_data(mcdserver_state.all_memspaces, cpu_id);
+    mcd_mem_space_st space = g_array_index(memspaces, mcd_mem_space_st,
+        mem_space_id - 1);
+    /* get data in the QEMU address space and access attributes */
+    AddressSpace *address_space = mcd_get_address_space(cpu, cpu_id, space);
+    MemTxAttrs attributes = mcd_get_memtxattrs(cpu, space);
+    /* write memory data */
+    mcd_hextomem(mcdserver_state.mem_buf, mcdserver_state.str_buf->str, len);
+    if (space.is_physical) {
+        /* physical memory */
+        if (mcd_read_write_physical_memory(address_space, attributes,
+            mem_address, mcdserver_state.mem_buf->data,
+            mcdserver_state.mem_buf->len, true) != 0) {
+            mcd_put_packet(TCP_EXECUTION_ERROR);
+            return;
+        }
+    } else {
+        /* user space memory */
+        if (mcd_read_write_memory(cpu, address_space, attributes, mem_address,
+            mcdserver_state.mem_buf->data, mcdserver_state.mem_buf->len,
+            true) != 0) {
+            mcd_put_packet(TCP_EXECUTION_ERROR);
+            return;
+        }
+    }
+    /* send acknowledge */
+    mcd_put_packet(TCP_EXECUTION_SUCCESS);
+}
+
 /**
  * mcd_handle_packet() - Evaluates the type of received packet and chooses the
  * correct handler.
@@ -1128,6 +1308,29 @@ static int mcd_handle_packet(const char *line_buf)
             cmd_parser = &write_reg_cmd_desc;
         }
         break;
+    case TCP_CHAR_READ_MEMORY:
+        {
+            static MCDCmdParseEntry read_mem_cmd_desc = {
+                .handler = handle_read_memory,
+                .cmd = {TCP_CHAR_READ_MEMORY, '\0'},
+                .schema = {ARG_SCHEMA_CORENUM, ARG_SCHEMA_INT,
+                           ARG_SCHEMA_UINT64_T, ARG_SCHEMA_INT, '\0'},
+            };
+            cmd_parser = &read_mem_cmd_desc;
+        }
+        break;
+    case TCP_CHAR_WRITE_MEMORY:
+        {
+            static MCDCmdParseEntry write_mem_cmd_desc = {
+                .handler = handle_write_memory,
+                .cmd = {TCP_CHAR_WRITE_MEMORY, '\0'},
+                .schema = {ARG_SCHEMA_CORENUM, ARG_SCHEMA_INT,
+                           ARG_SCHEMA_UINT64_T, ARG_SCHEMA_INT,
+                           ARG_SCHEMA_HEXDATA, '\0'},
+            };
+            cmd_parser = &write_mem_cmd_desc;
+        }
+        break;
     default:
         /* command not supported */
         mcd_put_packet("");
diff --git a/include/mcdstub/arm_mcdstub.h b/include/mcdstub/arm_mcdstub.h
index 9961145f07..69f6e2d487 100644
--- a/include/mcdstub/arm_mcdstub.h
+++ b/include/mcdstub/arm_mcdstub.h
@@ -100,4 +100,19 @@ int arm_mcd_get_additional_register_info(GArray *reggroups, GArray *registers,
  */
 uint16_t arm_mcd_get_opcode(CPUState *cs, uint32_t n);
 
+/**
+ * arm_mcd_get_address_space() - Returnes the correct QEMU address space name
+ * @cpu_id: Correct CPU ID
+ * @mem_space: Desired mcd specific memory space.
+ */
+AddressSpace *arm_mcd_get_address_space(uint32_t cpu_id,
+    mcd_mem_space_st mem_space);
+
+/**
+ * arm_mcd_get_memtxattrs() - Returnes the correct QEMU address space access
+ * attributes
+ * @mem_space: Desired mcd specific memory space.
+ */
+MemTxAttrs arm_mcd_get_memtxattrs(mcd_mem_space_st mem_space);
+
 #endif /* ARM_MCDSTUB_H */
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v5 18/18] mcdstub: break/watchpoints added
  2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
                   ` (16 preceding siblings ...)
  2023-12-20 16:25 ` [PATCH v5 17/18] mcdstub: memory " Nicolas Eder
@ 2023-12-20 16:25 ` Nicolas Eder
  17 siblings, 0 replies; 28+ messages in thread
From: Nicolas Eder @ 2023-12-20 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Philippe Mathieu-Daudé,
	Christian Boenig, Nicolas Eder

---
 debug/mcdstub/mcdstub.c | 150 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 150 insertions(+)

diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
index 954d06c0b7..d2c505c0d6 100644
--- a/debug/mcdstub/mcdstub.c
+++ b/debug/mcdstub/mcdstub.c
@@ -1177,6 +1177,134 @@ static void handle_write_memory(GArray *params, void *user_ctx)
     mcd_put_packet(TCP_EXECUTION_SUCCESS);
 }
 
+/**
+ * mcd_breakpoint_insert() - Inserts a break- or watchpoint.
+ *
+ * This function evaluates the received breakpoint type and translates it
+ * to a known GDB breakpoint type.
+ * Then it calls cpu_breakpoint_insert or cpu_watchpoint_insert depending on
+ * the type.
+ * @cpu: CPU to which the breakpoint should be added.
+ * @addr: Address of the breakpoint.
+ * @type: Breakpoint type.
+ */
+static int mcd_breakpoint_insert(CPUState *cpu, int type, vaddr addr)
+{
+    /* translate the type to known gdb types and function call*/
+    int bp_type = 0;
+    CPUClass *cc = CPU_GET_CLASS(cpu);
+    if (cc->gdb_stop_before_watchpoint) {
+        /* bp_type |= BP_STOP_BEFORE_ACCESS; */
+    }
+    int return_value = 0;
+    switch (type) {
+    case MCD_BREAKPOINT_HW:
+        return_value = cpu_breakpoint_insert(cpu, addr, BP_GDB, NULL);
+        return return_value;
+    case MCD_BREAKPOINT_READ:
+        bp_type |= BP_GDB | BP_MEM_READ;
+        return_value = cpu_watchpoint_insert(cpu, addr, 4, bp_type, NULL);
+        return return_value;
+    case MCD_BREAKPOINT_WRITE:
+        bp_type |= BP_GDB | BP_MEM_WRITE;
+        return_value = cpu_watchpoint_insert(cpu, addr, 4, bp_type, NULL);
+        return return_value;
+    case MCD_BREAKPOINT_RW:
+        bp_type |= BP_GDB | BP_MEM_ACCESS;
+        return_value = cpu_watchpoint_insert(cpu, addr, 4, bp_type, NULL);
+        return return_value;
+    default:
+        return -ENOSYS;
+    }
+}
+
+/**
+ * mcd_breakpoint_remove() - Removes a break- or watchpoint.
+ *
+ * This function evaluates the received breakpoint type and translates it
+ * to a known GDB breakpoint type.
+ * Then it calls cpu_breakpoint_remove or cpu_watchpoint_remove depending on
+ * the type.
+ * @cpu: CPU from which the breakpoint should be removed.
+ * @addr: Address of the breakpoint.
+ * @type: Breakpoint type.
+ */
+static int mcd_breakpoint_remove(CPUState *cpu, int type, vaddr addr)
+{
+    /* translate the type to known gdb types and function call*/
+    int bp_type = 0;
+    CPUClass *cc = CPU_GET_CLASS(cpu);
+    if (cc->gdb_stop_before_watchpoint) {
+        /* bp_type |= BP_STOP_BEFORE_ACCESS; */
+    }
+    int return_value = 0;
+    switch (type) {
+    case MCD_BREAKPOINT_HW:
+        return_value = cpu_breakpoint_remove(cpu, addr, BP_GDB);
+        return return_value;
+    case MCD_BREAKPOINT_READ:
+        bp_type |= BP_GDB | BP_MEM_READ;
+        return_value = cpu_watchpoint_remove(cpu, addr, 4, bp_type);
+        return return_value;
+    case MCD_BREAKPOINT_WRITE:
+        bp_type |= BP_GDB | BP_MEM_WRITE;
+        return_value = cpu_watchpoint_remove(cpu, addr, 4, bp_type);
+        return return_value;
+    case MCD_BREAKPOINT_RW:
+        bp_type |= BP_GDB | BP_MEM_ACCESS;
+        return_value = cpu_watchpoint_remove(cpu, addr, 4, bp_type);
+        return return_value;
+    default:
+        return -ENOSYS;
+    }
+}
+
+/**
+ * handle_breakpoint_insert() - Handler for inserting a break- or watchpoint.
+ *
+ * This function extracts the CPU, breakpoint type and address from the
+ * parameters and calls :c:func:`mcd_breakpoint_insert` to insert the
+ * breakpoint.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_breakpoint_insert(GArray *params, void *user_ctx)
+{
+    /* 1. get parameter data */
+    uint32_t cpu_id = get_param(params, 0)->cpu_id;
+    uint32_t type = get_param(params, 1)->data_uint32_t;
+    uint64_t address = get_param(params, 2)->data_uint64_t;
+    /* 2. insert breakpoint and send reply */
+    CPUState *cpu = mcd_get_cpu(cpu_id);
+    if (mcd_breakpoint_insert(cpu, type, address) != 0) {
+        mcd_put_packet(TCP_EXECUTION_ERROR);
+    } else {
+        mcd_put_packet(TCP_EXECUTION_SUCCESS);
+    }
+}
+
+/**
+ * handle_breakpoint_remove() - Handler for inserting a break- or watchpoint.
+ *
+ * This function extracts the CPU, breakpoint type and address from the
+ * parameters and calls :c:func:`mcd_breakpoint_remove` to insert the
+ * breakpoint.
+ * @params: GArray with all TCP packet parameters.
+ */
+static void handle_breakpoint_remove(GArray *params, void *user_ctx)
+{
+    /* 1. get parameter data */
+    uint32_t cpu_id = get_param(params, 0)->cpu_id;
+    uint32_t type = get_param(params, 1)->data_uint32_t;
+    uint64_t address = get_param(params, 2)->data_uint64_t;
+    /* 2. remove breakpoint and send reply */
+    CPUState *cpu = mcd_get_cpu(cpu_id);
+    if (mcd_breakpoint_remove(cpu, type, address) != 0) {
+        mcd_put_packet(TCP_EXECUTION_ERROR);
+    } else {
+        mcd_put_packet(TCP_EXECUTION_SUCCESS);
+    }
+}
+
 /**
  * mcd_handle_packet() - Evaluates the type of received packet and chooses the
  * correct handler.
@@ -1331,6 +1459,28 @@ static int mcd_handle_packet(const char *line_buf)
             cmd_parser = &write_mem_cmd_desc;
         }
         break;
+    case TCP_CHAR_BREAKPOINT_INSERT:
+        {
+            static MCDCmdParseEntry handle_breakpoint_insert_cmd_desc = {
+                .handler = handle_breakpoint_insert,
+                .cmd = {TCP_CHAR_BREAKPOINT_INSERT, '\0'},
+                .schema = {ARG_SCHEMA_CORENUM, ARG_SCHEMA_INT,
+                           ARG_SCHEMA_UINT64_T, '\0'},
+            };
+            cmd_parser = &handle_breakpoint_insert_cmd_desc;
+        }
+        break;
+    case TCP_CHAR_BREAKPOINT_REMOVE:
+        {
+            static MCDCmdParseEntry handle_breakpoint_remove_cmd_desc = {
+                .handler = handle_breakpoint_remove,
+                .cmd = {TCP_CHAR_BREAKPOINT_REMOVE, '\0'},
+                .schema = {ARG_SCHEMA_CORENUM, ARG_SCHEMA_INT,
+                           ARG_SCHEMA_UINT64_T, '\0'},
+            };
+            cmd_parser = &handle_breakpoint_remove_cmd_desc;
+        }
+        break;
     default:
         /* command not supported */
         mcd_put_packet("");
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH v5 11/18] mcdstub: system and core queries added
  2023-12-20 16:25 ` [PATCH v5 11/18] mcdstub: system and core queries added Nicolas Eder
@ 2024-02-29 15:11   ` Alex Bennée
  2024-03-05 11:08     ` nicolas.eder
  0 siblings, 1 reply; 28+ messages in thread
From: Alex Bennée @ 2024-02-29 15:11 UTC (permalink / raw)
  To: Nicolas Eder; +Cc: qemu-devel, Philippe Mathieu-Daudé, Christian Boenig

Nicolas Eder <nicolas.eder@lauterbach.com> writes:

> ---
>  debug/mcdstub/arm_mcdstub.c      | 243 ++++++++++++++++++++
>  debug/mcdstub/mcdstub.c          | 370 ++++++++++++++++++++++++++++++-
>  debug/mcdstub/meson.build        |   2 +-
>  include/mcdstub/arm_mcdstub.h    |  85 +++++++
>  include/mcdstub/mcdstub.h        |   5 -
>  include/mcdstub/mcdstub_common.h |  19 ++
>  6 files changed, 717 insertions(+), 7 deletions(-)
>
> diff --git a/debug/mcdstub/arm_mcdstub.c b/debug/mcdstub/arm_mcdstub.c
> index c24aaf1202..ce5264a617 100644
> --- a/debug/mcdstub/arm_mcdstub.c
> +++ b/debug/mcdstub/arm_mcdstub.c
> @@ -16,3 +16,246 @@
>   *
>   * SPDX-License-Identifier: LGPL-2.0+
>   */
> +
> +#include "qemu/osdep.h"
> +#include "mcdstub/arm_mcdstub.h"
> +
> +int arm_mcd_store_mem_spaces(CPUState *cpu, GArray *memspaces)
> +{
> +    int nr_address_spaces = cpu->num_ases;
> +    uint32_t mem_space_id = 0;
> +
> +    mem_space_id++;
> +    mcd_mem_space_st non_secure = {
> +        .name = "Non Secure",
> +        .id = mem_space_id,
> +        .type = 34,
> +        .bits_per_mau = 8,
> +        .invariance = 1,
> +        .endian = 1,
> +        .min_addr = 0,
> +        .max_addr = -1,
> +        .supported_access_options = 0,
> +        .is_secure = false,
> +        .is_physical = false,
> +    };
> +    g_array_append_vals(memspaces, (gconstpointer)&non_secure, 1);
> +    mem_space_id++;
> +    mcd_mem_space_st phys_non_secure = {
> +        .name = "Physical (Non Secure)",
> +        .id = mem_space_id,
> +        .type = 18,
> +        .bits_per_mau = 8,
> +        .invariance = 1,
> +        .endian = 1,
> +        .min_addr = 0,
> +        .max_addr = -1,
> +        .supported_access_options = 0,
> +        .is_secure = false,
> +        .is_physical = true,
> +    };
> +    g_array_append_vals(memspaces, (gconstpointer)&phys_non_secure, 1);
> +    if (nr_address_spaces > 1) {
> +        mem_space_id++;
> +        mcd_mem_space_st secure = {
> +            .name = "Secure",
> +            .id = mem_space_id,
> +            .type = 34,
> +            .bits_per_mau = 8,
> +            .invariance = 1,
> +            .endian = 1,
> +            .min_addr = 0,
> +            .max_addr = -1,
> +            .supported_access_options = 0,
> +            .is_secure = true,
> +            .is_physical = false,
> +        };
> +        g_array_append_vals(memspaces, (gconstpointer)&secure, 1);
> +        mem_space_id++;
> +        mcd_mem_space_st phys_secure = {
> +            .name = "Physical (Secure)",
> +            .id = mem_space_id,
> +            .type = 18,
> +            .bits_per_mau = 8,
> +            .invariance = 1,
> +            .endian = 1,
> +            .min_addr = 0,
> +            .max_addr = -1,
> +            .supported_access_options = 0,
> +            .is_secure = true,
> +            .is_physical = true,
> +        };
> +        g_array_append_vals(memspaces, (gconstpointer)&phys_secure, 1);
> +    }
> +    mem_space_id++;
> +    mcd_mem_space_st gpr = {
> +        .name = "GPR Registers",
> +        .id = mem_space_id,
> +        .type = 1,
> +        .bits_per_mau = 8,
> +        .invariance = 1,
> +        .endian = 1,
> +        .min_addr = 0,
> +        .max_addr = -1,
> +        .supported_access_options = 0,
> +    };
> +    g_array_append_vals(memspaces, (gconstpointer)&gpr, 1);
> +    mem_space_id++;
> +    mcd_mem_space_st cpr = {
> +        .name = "CP15 Registers",
> +        .id = mem_space_id,
> +        .type = 1,
> +        .bits_per_mau = 8,
> +        .invariance = 1,
> +        .endian = 1,
> +        .min_addr = 0,
> +        .max_addr = -1,
> +        .supported_access_options = 0,
> +    };
> +    g_array_append_vals(memspaces, (gconstpointer)&cpr, 1);
> +    return 0;
> +}
> +
> +int arm_mcd_parse_core_xml_file(CPUClass *cc, GArray *reggroups,
> +    GArray *registers, int *current_group_id)
> +{
> +    const char *xml_filename = NULL;
> +    const char *current_xml_filename = NULL;
> +    const char *xml_content = NULL;
> +    int i = 0;
> +
> +    /* 1. get correct file */
> +    xml_filename = cc->gdb_core_xml_file;
> +    for (i = 0; ; i++) {
> +        current_xml_filename = gdb_static_features[i].xmlname;
> +        if (!current_xml_filename || (strncmp(current_xml_filename,
> +            xml_filename, strlen(xml_filename)) == 0
> +            && strlen(current_xml_filename) == strlen(xml_filename)))
> +            break;
> +    }

I guess this will need re-writing to use the new GDBFeature builder
which has been merged:

  cc -m64 -mcx16 -Idebug/mcdstub/libmcd_system.fa.p -Idebug/mcdstub -I../../debug/mcdstub -I. -Iqapi -Itrace -Iui -Iui/shader -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O2 -g -fstack-protector-strong -Wempty-body -Wendif-labels -Wexpansion-to-defined -Wformat-security -Wformat-y2k -Wignored-qualifiers -Wimplicit-fallthrough=2 -Winit-self -Wmissing-format-attribute -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -Wshadow=local -Wstrict-prototypes -Wtype-limits -Wundef -Wvla -Wwrite-strings -Wno-missing-include-dirs -Wno-psabi -Wno-shift-negative-value -isystem /home/alex/lsrc/qemu.git/linux-headers -isystem linux-headers -iquote . -iquote /home/alex/lsrc/qemu.git -iquote /home/alex/lsrc/qemu.git/include -iquote /home/alex/lsrc/qemu.git/host/include/x86_64 -iquote /home/alex/lsrc/qemu.git/host/include/generic -iquote /home/alex/lsrc/qemu.git/tcg/i386 -pthread -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fno-common -fwrapv -fzero-call-used-regs=used-gpr -ftrivial-auto-var-init=zero -fPIE -MD -MQ debug/mcdstub/libmcd_system.fa.p/arm_mcdstub.c.o -MF debug/mcdstub/libmcd_system.fa.p/arm_mcdstub.c.o.d -o debug/mcdstub/libmcd_system.fa.p/arm_mcdstub.c.o -c ../../debug/mcdstub/arm_mcdstub.c
  ../../debug/mcdstub/arm_mcdstub.c: In function ‘arm_mcd_parse_general_xml_files’:
  ../../debug/mcdstub/arm_mcdstub.c:171:25: error: ‘GDBRegisterState’ has no member named ‘xml’
    171 |         xml_filename = r->xml;
        |                         ^~
  ../../debug/mcdstub/arm_mcdstub.c:175:15: error: ‘CPUClass’ has no member named ‘gdb_get_dynamic_xml’
    175 |         if (cc->gdb_get_dynamic_xml) {
        |               ^~
  ../../debug/mcdstub/arm_mcdstub.c:176:29: error: ‘CPUClass’ has no member named ‘gdb_get_dynamic_xml’
    176 |             xml_content = cc->gdb_get_dynamic_xml(cpu, xml_filename);
        |                             ^~

Hopefully the code can be more generic now and avoid having to re-parse
generated xml.


> +    /* without gpr registers we can do nothing */
> +    if (!current_xml_filename) {
> +        return -1;
> +    }
> +
> +    /* 2. add group for gpr registers */
> +    mcd_reg_group_st gprregs = {
> +        .name = "GPR Registers",
> +        .id = *current_group_id
> +    };
> +    g_array_append_vals(reggroups, (gconstpointer)&gprregs, 1);
> +    *current_group_id = *current_group_id + 1;
> +
> +    /* 3. parse xml */
> +    /* the offset for gpr is always zero */
> +    xml_content = gdb_static_features[i].xml;
> +    parse_reg_xml(xml_content, strlen(xml_content), registers,
> +        MCD_ARM_REG_TYPE_GPR, 0);
> +    return 0;
> +}
> +
> +int arm_mcd_parse_general_xml_files(CPUState *cpu, GArray *reggroups,
> +    GArray *registers, int *current_group_id) {
> +    const char *xml_filename = NULL;
> +    const char *current_xml_filename = NULL;
> +    const char *xml_content = NULL;
> +    uint8_t reg_type = 0;
> +    CPUClass *cc = CPU_GET_CLASS(cpu);
> +
> +    /* iterate over all gdb xml files*/
> +    GDBRegisterState *r;
> +    for (guint i = 0; i < cpu->gdb_regs->len; i++) {
> +        r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i);
> +
> +        xml_filename = r->xml;
> +        xml_content = NULL;
> +
> +        /* 1. get xml content */
> +        if (cc->gdb_get_dynamic_xml) {
> +            xml_content = cc->gdb_get_dynamic_xml(cpu, xml_filename);
> +        }
> +        if (xml_content) {
> +            if (strcmp(xml_filename, "system-registers.xml") == 0) {
> +                /* these are the coprocessor register */
> +                mcd_reg_group_st corprocessorregs = {
> +                    .name = "CP15 Registers",
> +                    .id = *current_group_id
> +                };
> +                g_array_append_vals(reggroups,
> +                    (gconstpointer)&corprocessorregs, 1);
> +                *current_group_id = *current_group_id + 1;
> +                reg_type = MCD_ARM_REG_TYPE_CPR;
> +            }
> +        } else {
> +            /* its not a coprocessor xml -> it is a static xml file */
> +            int j = 0;
> +            for (j = 0; ; j++) {
> +                current_xml_filename = gdb_static_features[j].xmlname;
> +                if (!current_xml_filename || (strncmp(current_xml_filename,
> +                    xml_filename, strlen(xml_filename)) == 0
> +                    && strlen(current_xml_filename) == strlen(xml_filename)))
> +                    break;
> +            }
> +            if (current_xml_filename) {
> +                xml_content = gdb_static_features[j].xml;
> +                /* select correct reg_type */
> +                if (strcmp(current_xml_filename, "arm-vfp.xml") == 0) {
> +                    reg_type = MCD_ARM_REG_TYPE_VFP;
> +                } else if (strcmp(current_xml_filename, "arm-vfp3.xml") == 0) {
> +                    reg_type = MCD_ARM_REG_TYPE_VFP;
> +                } else if (strcmp(current_xml_filename,
> +                    "arm-vfp-sysregs.xml") == 0) {
> +                    reg_type = MCD_ARM_REG_TYPE_VFP_SYS;
> +                } else if (strcmp(current_xml_filename,
> +                    "arm-neon.xml") == 0) {
> +                    reg_type = MCD_ARM_REG_TYPE_VFP;
> +                } else if (strcmp(current_xml_filename,
> +                    "arm-m-profile-mve.xml") == 0) {
> +                    reg_type = MCD_ARM_REG_TYPE_MVE;
> +                }
> +            } else {
> +                continue;
> +            }
> +        }
> +        /* 2. parse xml */
> +        parse_reg_xml(xml_content, strlen(xml_content), registers, reg_type,
> +            r->base_reg);
> +    }
> +    return 0;
> +}
> +
> +int arm_mcd_get_additional_register_info(GArray *reggroups, GArray *registers,
> +    CPUState *cpu)
> +{
> +    mcd_reg_st *current_register;
> +    uint32_t i = 0;
> +
> +    /* iterate over all registers */
> +    for (i = 0; i < registers->len; i++) {
> +        current_register = &(g_array_index(registers, mcd_reg_st, i));
> +        /* add mcd_reg_group_id and mcd_mem_space_id */
> +        if (strcmp(current_register->group, "cp_regs") == 0) {
> +            /* coprocessor registers */
> +            current_register->mcd_reg_group_id = 2;
> +            current_register->mcd_mem_space_id = 6;
> +            /*
> +             * get info for opcode
> +             * for 32bit the opcode is only 16 bit long
> +             * for 64bit it is 32 bit long
> +             */
> +            current_register->opcode |=
> +                arm_mcd_get_opcode(cpu, current_register->internal_id);
> +        } else {
> +            /* gpr register */
> +            current_register->mcd_reg_group_id = 1;
> +            current_register->mcd_mem_space_id = 5;
> +        }
> +    }
> +    return 0;
> +}
> +
> +uint16_t arm_mcd_get_opcode(CPUState *cs, uint32_t n)
> +{
> +    /* TODO: not working with current build structure */
> +    return 0;
> +}
> diff --git a/debug/mcdstub/mcdstub.c b/debug/mcdstub/mcdstub.c
> index 45daa38689..4095b3f8ce 100644
> --- a/debug/mcdstub/mcdstub.c
> +++ b/debug/mcdstub/mcdstub.c
> @@ -34,6 +34,7 @@
>  
>  #include "mcdstub/mcd_shared_defines.h"
>  #include "mcdstub/mcdstub.h"
> +#include "mcdstub/arm_mcdstub.h"
>  
>  typedef struct {
>      CharBackend chr;
> @@ -150,6 +151,25 @@ static CPUState *mcd_first_attached_cpu(void)
>      return cpu;
>  }
>  
> +/**
> + * mcd_get_cpu() - Returns the CPU the index i_cpu_index.
> + *
> + * @cpu_index: Index of the desired CPU.
> + */
> +static CPUState *mcd_get_cpu(uint32_t cpu_index)
> +{
> +    CPUState *cpu = first_cpu;
> +
> +    while (cpu) {
> +        if (cpu->cpu_index == cpu_index) {
> +            return cpu;
> +        }
> +        cpu = mcd_next_attached_cpu(cpu);
> +    }
> +
> +    return cpu;
> +}
> +
>  /**
>   * mcd_vm_state_change() - Handles a state change of the QEMU VM.
>   *
> @@ -221,6 +241,15 @@ static int mcd_put_packet(const char *buf)
>      return mcd_put_packet_binary(buf, strlen(buf));
>  }
>  
> +/**
> + * mcd_put_strbuf() - Calls :c:func:`mcd_put_packet` with the str_buf of the
> + * mcdserver_state.
> + */
> +static void mcd_put_strbuf(void)
> +{
> +    mcd_put_packet(mcdserver_state.str_buf->str);
> +}
> +
>  /**
>   * cmd_parse_params() - Extracts all parameters from a TCP packet.
>   *
> @@ -480,6 +509,134 @@ static void handle_close_server(GArray *params, void *user_ctx)
>      }
>  }
>  
> +/**
> + * handle_gen_query() - Handler for all TCP query packets.
> + *
> + * Calls :c:func:`process_string_cmd` with all query functions in the
> + * mcd_query_cmds_table. :c:func:`process_string_cmd` then selects the correct
> + * one. This function just passes on the TCP packet data string from the
> + * parameters.
> + * @params: GArray with all TCP packet parameters.
> + */
> +static void handle_gen_query(GArray *params, void *user_ctx)
> +{
> +    if (!params->len) {
> +        return;
> +    }
> +    /* iterate over all possible query functions and execute the right one */
> +    if (process_string_cmd(NULL, get_param(params, 0)->data,
> +                           mcdserver_state.mcd_query_cmds_table,
> +                           ARRAY_SIZE(mcdserver_state.mcd_query_cmds_table))) {
> +        mcd_put_packet("");
> +    }
> +}
> +
> +/**
> + * handle_open_core() - Handler for opening a core.
> + *
> + * This function initializes all data for the core with the ID provided in
> + * the first parameter. In has a swtich case for different architectures.
> + * Currently only 32-Bit ARM is supported. The data includes memory spaces,
> + * register groups and registers themselves. They get stored into GLists where
> + * every entry in the list corresponds to one opened core.
> + * @params: GArray with all TCP packet parameters.
> + */
> +static void handle_open_core(GArray *params, void *user_ctx)
> +{
> +    uint32_t cpu_id = get_param(params, 0)->cpu_id;
> +    CPUState *cpu = mcd_get_cpu(cpu_id);
> +    mcdserver_state.c_cpu = cpu;
> +    CPUClass *cc = CPU_GET_CLASS(cpu);
> +    const gchar *arch = cc->gdb_arch_name(cpu);
> +    int return_value = 0;
> +
> +    /* prepare data strucutures */
> +    GArray *memspaces = g_array_new(false, true, sizeof(mcd_mem_space_st));
> +    GArray *reggroups = g_array_new(false, true, sizeof(mcd_reg_group_st));
> +    GArray *registers = g_array_new(false, true, sizeof(mcd_reg_st));
> +
> +    if (strcmp(arch, MCDSTUB_ARCH_ARM) == 0) {
> +        /* TODO: make group and memspace ids dynamic */
> +        int current_group_id = 1;
> +        /* 1. store mem spaces */
> +        return_value = arm_mcd_store_mem_spaces(cpu, memspaces);
> +        if (return_value != 0) {
> +            g_assert_not_reached();
> +        }
> +        /* 2. parse core xml */
> +        return_value = arm_mcd_parse_core_xml_file(cc, reggroups,
> +            registers, &current_group_id);
> +        if (return_value != 0) {
> +            g_assert_not_reached();
> +        }
> +        /* 3. parse other xmls */
> +        return_value = arm_mcd_parse_general_xml_files(cpu, reggroups,
> +            registers, &current_group_id);
> +        if (return_value != 0) {
> +            g_assert_not_reached();
> +        }
> +        /* 4. add additional data the the regs from the xmls */
> +        return_value = arm_mcd_get_additional_register_info(reggroups,
> +            registers, cpu);
> +        if (return_value != 0) {
> +            g_assert_not_reached();
> +        }
> +        /* 5. store all found data */
> +        if (g_list_nth(mcdserver_state.all_memspaces, cpu_id)) {
> +            GList *memspaces_ptr =
> +                g_list_nth(mcdserver_state.all_memspaces, cpu_id);
> +            memspaces_ptr->data = memspaces;
> +        } else {
> +            mcdserver_state.all_memspaces =
> +                g_list_insert(mcdserver_state.all_memspaces, memspaces, cpu_id);
> +        }
> +        if (g_list_nth(mcdserver_state.all_reggroups, cpu_id)) {
> +            GList *reggroups_ptr =
> +                g_list_nth(mcdserver_state.all_reggroups, cpu_id);
> +            reggroups_ptr->data = reggroups;
> +        } else {
> +            mcdserver_state.all_reggroups =
> +                g_list_insert(mcdserver_state.all_reggroups, reggroups, cpu_id);
> +        }
> +        if (g_list_nth(mcdserver_state.all_registers, cpu_id)) {
> +            GList *registers_ptr =
> +                g_list_nth(mcdserver_state.all_registers, cpu_id);
> +            registers_ptr->data = registers;
> +        } else {
> +            mcdserver_state.all_registers =
> +                g_list_insert(mcdserver_state.all_registers, registers, cpu_id);
> +        }
> +    } else {
> +        /* we don't support other architectures */
> +        g_assert_not_reached();
> +    }
> +}
> +
> +/**
> + * handle_close_core() - Handler for closing a core.
> + *
> + * Frees all memory allocated for core specific information. This includes
> + * memory spaces, register groups and registers.
> + * @params: GArray with all TCP packet parameters.
> + */
> +static void handle_close_core(GArray *params, void *user_ctx)
> +{
> +    /* free memory for correct core */
> +    uint32_t cpu_id = get_param(params, 0)->cpu_id;
> +    GArray *memspaces = g_list_nth_data(mcdserver_state.all_memspaces, cpu_id);
> +    mcdserver_state.all_memspaces =
> +        g_list_remove(mcdserver_state.all_memspaces, memspaces);
> +    g_array_free(memspaces, TRUE);
> +    GArray *reggroups = g_list_nth_data(mcdserver_state.all_reggroups, cpu_id);
> +    mcdserver_state.all_reggroups =
> +        g_list_remove(mcdserver_state.all_reggroups, reggroups);
> +    g_array_free(reggroups, TRUE);
> +    GArray *registers = g_list_nth_data(mcdserver_state.all_registers, cpu_id);
> +    mcdserver_state.all_registers =
> +        g_list_remove(mcdserver_state.all_registers, registers);
> +    g_array_free(registers, TRUE);
> +}
> +
>  /**
>   * mcd_handle_packet() - Evaluates the type of received packet and chooses the
>   * correct handler.
> @@ -516,6 +673,36 @@ static int mcd_handle_packet(const char *line_buf)
>              cmd_parser = &close_server_cmd_desc;
>          }
>          break;
> +    case TCP_CHAR_QUERY:
> +        {
> +            static MCDCmdParseEntry query_cmd_desc = {
> +                .handler = handle_gen_query,
> +                .cmd = {TCP_CHAR_QUERY, '\0'},
> +                .schema = {ARG_SCHEMA_STRING, '\0'},
> +            };
> +            cmd_parser = &query_cmd_desc;
> +        }
> +        break;
> +    case TCP_CHAR_OPEN_CORE:
> +        {
> +            static MCDCmdParseEntry open_core_cmd_desc = {
> +                .handler = handle_open_core,
> +                .cmd = {TCP_CHAR_OPEN_CORE, '\0'},
> +                .schema = {ARG_SCHEMA_CORENUM, '\0'},
> +            };
> +            cmd_parser = &open_core_cmd_desc;
> +        }
> +        break;
> +    case TCP_CHAR_CLOSE_CORE:
> +        {
> +            static MCDCmdParseEntry close_core_cmd_desc = {
> +                .handler = handle_close_core,
> +                .cmd = {TCP_CHAR_CLOSE_CORE, '\0'},
> +                .schema = {ARG_SCHEMA_CORENUM, '\0'},
> +            };
> +            cmd_parser = &close_core_cmd_desc;
> +        }
> +        break;
>      default:
>          /* command not supported */
>          mcd_put_packet("");
> @@ -663,6 +850,49 @@ static void mcd_chr_event(void *opaque, QEMUChrEvent event)
>      }
>  }
>  
> +/**
> + * handle_query_system() - Handler for the system query.
> + *
> + * Sends the system name, which is "qemu-system".
> + * @params: GArray with all TCP packet parameters.
> + */
> +static void handle_query_system(GArray *params, void *user_ctx)
> +{
> +    mcd_put_packet(MCD_SYSTEM_NAME);
> +}
> +
> +/**
> + * handle_query_cores() - Handler for the core query.
> + *
> + * This function sends the type of core and number of cores currently
> + * simulated by QEMU. It also sends a device name for the MCD data structure.
> + * @params: GArray with all TCP packet parameters.
> + */
> +static void handle_query_cores(GArray *params, void *user_ctx)
> +{
> +    /* get first cpu */
> +    CPUState *cpu = mcd_first_attached_cpu();
> +    if (!cpu) {
> +        return;
> +    }
> +
> +    ObjectClass *oc = object_get_class(OBJECT(cpu));
> +    const char *cpu_model = object_class_get_name(oc);
> +
> +    CPUClass *cc = CPU_GET_CLASS(cpu);
> +    const gchar *arch = cc->gdb_arch_name(cpu);
> +
> +    uint32_t nr_cores = cpu->nr_cores;
> +    char device_name[ARGUMENT_STRING_LENGTH] = {0};
> +    if (arch) {
> +        snprintf(device_name, sizeof(device_name), "qemu-%s-device", arch);
> +    }
> +    g_string_printf(mcdserver_state.str_buf, "%s=%s.%s=%s.%s=%u.",
> +        TCP_ARGUMENT_DEVICE, device_name, TCP_ARGUMENT_CORE, cpu_model,
> +        TCP_ARGUMENT_AMOUNT_CORE, nr_cores);
> +    mcd_put_strbuf();
> +}
> +
>  /**
>   * init_query_cmds_table() - Initializes all query functions.
>   *
> @@ -671,7 +901,24 @@ static void mcd_chr_event(void *opaque, QEMUChrEvent event)
>   * @mcd_query_cmds_table: Lookup table with all query commands.
>   */
>  static void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table)
> -{}
> +{
> +    /* initalizes a list of all query commands */
> +    int cmd_number = 0;
> +
> +    MCDCmdParseEntry query_system = {
> +        .handler = handle_query_system,
> +        .cmd = QUERY_ARG_SYSTEM,
> +    };
> +    mcd_query_cmds_table[cmd_number] = query_system;
> +    cmd_number++;
> +
> +    MCDCmdParseEntry query_cores = {
> +        .handler = handle_query_cores,
> +        .cmd = QUERY_ARG_CORES,
> +    };
> +    mcd_query_cmds_table[cmd_number] = query_cores;
> +    cmd_number++;
> +}
>  
>  /**
>   * mcd_set_stop_cpu() - Sets c_cpu to the just stopped CPU.
> @@ -924,3 +1171,124 @@ int mcdserver_start(const char *device)
>  
>      return 0;
>  }
> +
> +void parse_reg_xml(const char *xml, int size, GArray* registers,
> +    uint8_t reg_type, uint32_t reg_id_offset)
> +{
> +    /* iterates over the complete xml file */
> +    int i, j;
> +    uint32_t current_reg_id = reg_id_offset;
> +    uint32_t internal_id = 0;
> +    int still_to_skip = 0;
> +    char argument[64] = {0};
> +    char value[64] = {0};
> +    bool is_reg = false;
> +    bool is_argument = false;
> +    bool is_value = false;
> +    GArray *reg_data = NULL;
> +
> +    char c;
> +    char *c_ptr;
> +
> +    xml_attrib attribute_j;
> +    const char *argument_j;
> +    const char *value_j;
> +
> +    for (i = 0; i < size; i++) {
> +        c = xml[i];
> +        c_ptr = &c;
> +
> +        if (still_to_skip > 0) {
> +            /* skip unwanted chars */
> +            still_to_skip--;
> +            continue;
> +        }
> +
> +        if (strncmp(&xml[i], "<reg", 4) == 0) {
> +            /* start of a register */
> +            still_to_skip = 3;
> +            is_reg = true;
> +            reg_data = g_array_new(false, true, sizeof(xml_attrib));
> +        } else if (is_reg) {
> +            if (strncmp(&xml[i], "/>", 2) == 0) {
> +                /* end of register info */
> +                still_to_skip = 1;
> +                is_reg = false;
> +
> +                /* create empty register */
> +                mcd_reg_st my_register = (const struct mcd_reg_st){ 0 };
> +
> +                /* add found attribtues */
> +                for (j = 0; j < reg_data->len; j++) {
> +                    attribute_j = g_array_index(reg_data, xml_attrib, j);
> +
> +                    argument_j = attribute_j.argument;
> +                    value_j = attribute_j.value;
> +
> +                    if (strcmp(argument_j, "name") == 0) {
> +                        strcpy(my_register.name, value_j);
> +                    } else if (strcmp(argument_j, "regnum") == 0) {
> +                        my_register.id = atoi(value_j);
> +                    } else if (strcmp(argument_j, "bitsize") == 0) {
> +                        my_register.bitsize = atoi(value_j);
> +                    } else if (strcmp(argument_j, "type") == 0) {
> +                        strcpy(my_register.type, value_j);
> +                    } else if (strcmp(argument_j, "group") == 0) {
> +                        strcpy(my_register.group, value_j);
> +                    }
> +                }
> +                /* add reg_type, internal_id and id*/
> +                my_register.reg_type = reg_type;
> +                my_register.internal_id = internal_id;
> +                internal_id++;
> +                if (!my_register.id) {
> +                    my_register.id = current_reg_id;
> +                    current_reg_id++;
> +                } else {
> +                    /* set correct ID for the next register */
> +                    current_reg_id = my_register.id + 1;
> +                }
> +                /* store register */
> +                g_array_append_vals(registers, (gconstpointer)&my_register, 1);
> +                /* free memory */
> +                g_array_free(reg_data, false);
> +            } else {
> +                /* store info for register */
> +                switch (c) {
> +                case ' ':
> +                    break;
> +                case '=':
> +                    is_argument = false;
> +                    break;
> +                case '"':
> +                    if (is_value) {
> +                        /* end of value reached */
> +                        is_value = false;
> +                        /* store arg-val combo */
> +                        xml_attrib current_attribute;
> +                        strcpy(current_attribute.argument, argument);
> +                        strcpy(current_attribute.value, value);
> +                        g_array_append_vals(reg_data,
> +                        (gconstpointer)&current_attribute, 1);
> +                        memset(argument, 0, sizeof(argument));
> +                        memset(value, 0, sizeof(value));
> +                    } else {
> +                        /*start of value */
> +                        is_value = true;
> +                    }
> +                    break;
> +                default:
> +                    if (is_argument) {
> +                        strncat(argument, c_ptr, 1);
> +                    } else if (is_value) {
> +                        strncat(value, c_ptr, 1);
> +                    } else {
> +                        is_argument = true;
> +                        strncat(argument, c_ptr, 1);
> +                    }
> +                    break;
> +                }
> +            }
> +        }
> +    }
> +}
> diff --git a/debug/mcdstub/meson.build b/debug/mcdstub/meson.build
> index 7e5ae878b0..3051a4e731 100644
> --- a/debug/mcdstub/meson.build
> +++ b/debug/mcdstub/meson.build
> @@ -1,6 +1,6 @@
>  # only system emulation is supported over mcd
>  mcd_system_ss = ss.source_set()
> -mcd_system_ss.add(files('mcdstub.c'))
> +mcd_system_ss.add(files('mcdstub.c', 'arm_mcdstub.c'))
>  mcd_system_ss = mcd_system_ss.apply(config_host, strict: false)
>  
>  libmcd_system = static_library('mcd_system',
> diff --git a/include/mcdstub/arm_mcdstub.h b/include/mcdstub/arm_mcdstub.h
> index c24aaf1202..9961145f07 100644
> --- a/include/mcdstub/arm_mcdstub.h
> +++ b/include/mcdstub/arm_mcdstub.h
> @@ -16,3 +16,88 @@
>   *
>   * SPDX-License-Identifier: LGPL-2.0+
>   */
> +
> +#ifndef ARM_MCDSTUB_H
> +#define ARM_MCDSTUB_H
> +
> +#include "hw/core/cpu.h"
> +#include "mcdstub_common.h"
> +/* just used for the register xml files */
> +#include "exec/gdbstub.h"
> +
> +/* ids for each different type of register */
> +enum {
> +    MCD_ARM_REG_TYPE_GPR,
> +    MCD_ARM_REG_TYPE_VFP,
> +    MCD_ARM_REG_TYPE_VFP_SYS,
> +    MCD_ARM_REG_TYPE_MVE,
> +    MCD_ARM_REG_TYPE_CPR,
> +};
> +
> +/**
> + * arm_mcd_store_mem_spaces() - Stores all 32-Bit ARM specific memory spaces.
> + *
> + * This function stores the memory spaces into the memspaces GArray.
> + * It only stores secure memory spaces if the CPU has more than one address
> + * space. It also stores a GPR and a CP15 register memory space.
> + * @memspaces: GArray of memory spaces.
> + * @cpu: CPU state.
> + */
> +int arm_mcd_store_mem_spaces(CPUState *cpu, GArray *memspaces);
> +
> +/**
> + * arm_mcd_parse_core_xml_file() - Parses the GPR registers.
> + *
> + * This function parses the core XML file, which includes the GPR registers.
> + * The regsters get stored in a GArray and a GPR register group is stored in a
> + * second GArray.
> + * @reggroups: GArray of register groups.
> + * @registers: GArray of registers.
> + * @cc: The CPU class.
> + * @current_group_id: The current group ID. It increases after
> + * each group.
> + */
> +int arm_mcd_parse_core_xml_file(CPUClass *cc, GArray *reggroups,
> +    GArray *registers, int *current_group_id);
> +
> +/**
> + * arm_mcd_parse_general_xml_files() - Parses all but the GPR registers.
> + *
> + * This function parses all XML files except for the core XML file.
> + * The regsters get stored in a GArray and if the system-registers.xml file is
> + * parsed, it also adds a CP15 register group.
> + * @reggroups: GArray of register groups.
> + * @registers: GArray of registers.
> + * @cpu: The CPU state.
> + * @current_group_id: The current group ID. It increases after
> + * each added group.
> + */
> +int arm_mcd_parse_general_xml_files(CPUState *cpu, GArray* reggroups,
> +    GArray *registers, int *current_group_id);
> +
> +/**
> + * arm_mcd_get_additional_register_info() - Adds additional data to parsed
> + * registers.
> + *
> + * This function is called, after :c:func:`arm_mcd_parse_core_xml_file` and
> + * :c:func:`arm_mcd_parse_general_xml_files`. It adds additional data for all
> + * already parsed registers. The registers get a correct ID, group, memory
> + * space and opcode, if they are CP15 registers.
> + * @reggroups: GArray of register groups.
> + * @registers: GArray of registers.
> + * @cpu: The CPU state.
> + */
> +int arm_mcd_get_additional_register_info(GArray *reggroups, GArray *registers,
> +    CPUState *cpu);
> +
> +/**
> + * arm_mcd_get_opcode() - Returns the opcode for a coprocessor register.
> + *
> + * This function uses the opc1, opc2, crm and crn members of the register to
> + * create the opcode. The formular for creating the opcode is determined by ARM.
> + * @n: The register ID of the CP register.
> + * @cs: CPU state.
> + */
> +uint16_t arm_mcd_get_opcode(CPUState *cs, uint32_t n);
> +
> +#endif /* ARM_MCDSTUB_H */
> diff --git a/include/mcdstub/mcdstub.h b/include/mcdstub/mcdstub.h
> index 26aa33c0e3..ac14b2cda8 100644
> --- a/include/mcdstub/mcdstub.h
> +++ b/include/mcdstub/mcdstub.h
> @@ -151,11 +151,6 @@ typedef struct MCDState {
>  /* lives in mcdstub.c */
>  extern MCDState mcdserver_state;
>  
> -typedef struct xml_attrib {
> -    char argument[ARGUMENT_STRING_LENGTH];
> -    char value[ARGUMENT_STRING_LENGTH];
> -} xml_attrib;
> -
>  typedef struct mcd_reset_st {
>      const char *name;
>      uint8_t id;
> diff --git a/include/mcdstub/mcdstub_common.h b/include/mcdstub/mcdstub_common.h
> index b64748c080..d6ff55005e 100644
> --- a/include/mcdstub/mcdstub_common.h
> +++ b/include/mcdstub/mcdstub_common.h
> @@ -61,4 +61,23 @@ typedef struct mcd_reg_group_st {
>      uint32_t id;
>  } mcd_reg_group_st;
>  
> +typedef struct xml_attrib {
> +    char argument[ARGUMENT_STRING_LENGTH];
> +    char value[ARGUMENT_STRING_LENGTH];
> +} xml_attrib;
> +
> +/**
> + * parse_reg_xml() -  Parses a GDB register XML file
> + *
> + * This fuction extracts all registers from the provided xml file and stores
> + * them into the registers GArray. It extracts the register name, bitsize, type
> + * and group if they are set.
> + * @xml: String with contents of the XML file.
> + * @registers: GArray with stored registers.
> + * @reg_type: Register type (depending on file).
> + * @size: Number of characters in the xml string.
> + */
> +void parse_reg_xml(const char *xml, int size, GArray* registers,
> +    uint8_t reg_type, uint32_t reg_id_offset);
> +
>  #endif /* MCDSTUB_COMMON_H */

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v5 03/18] gdbstub: GDBRegisterState moved to gdbstub.h so it can be used outside of the gdbstub
  2023-12-20 16:25 ` [PATCH v5 03/18] gdbstub: GDBRegisterState moved to gdbstub.h so it can be used outside of the gdbstub Nicolas Eder
@ 2024-02-29 15:23   ` Alex Bennée
  0 siblings, 0 replies; 28+ messages in thread
From: Alex Bennée @ 2024-02-29 15:23 UTC (permalink / raw)
  To: Nicolas Eder; +Cc: qemu-devel, Philippe Mathieu-Daudé, Christian Boenig

Nicolas Eder <nicolas.eder@lauterbach.com> writes:

> ---
>  debug/gdbstub/gdbstub.c | 8 --------
>  include/exec/gdbstub.h  | 8 ++++++++
>  2 files changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/debug/gdbstub/gdbstub.c b/debug/gdbstub/gdbstub.c
> index f43d4355c0..5df7841878 100644
> --- a/debug/gdbstub/gdbstub.c
> +++ b/debug/gdbstub/gdbstub.c
> @@ -45,14 +45,6 @@
>  
>  #include "internals.h"
>  
> -typedef struct GDBRegisterState {
> -    int base_reg;
> -    int num_regs;
> -    gdb_get_reg_cb get_reg;
> -    gdb_set_reg_cb set_reg;
> -    const char *xml;
> -} GDBRegisterState;
> -
>  GDBState gdbserver_state;
>  
>  void gdb_init_gdbserver_state(void)
> diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
> index d8a3c56fa2..cdbad65930 100644
> --- a/include/exec/gdbstub.h
> +++ b/include/exec/gdbstub.h
> @@ -27,6 +27,14 @@ typedef struct GDBFeatureBuilder {
>  typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg);
>  typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg);
>  
> +typedef struct GDBRegisterState {
> +    int base_reg;
> +    int num_regs;
> +    gdb_get_reg_cb get_reg;
> +    gdb_set_reg_cb set_reg;
> +    const char *xml;
> +} GDBRegisterState;
> +

There is a clash with the recent GDBFeature changes. I think this is
still private to the debug sub-system so maybe it belongs in:

 debug/registers.h

and then the subdirs can just do:

  #include "../registers.h"

>  /**
>   * gdb_register_coprocessor() - register a supplemental set of registers
>   * @cpu - the CPU associated with registers

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v5 01/18] gdbstub, mcdstub: file and build structure adapted to accomodate for the mcdstub
  2023-12-20 16:25 ` [PATCH v5 01/18] gdbstub, mcdstub: file and build structure adapted to accomodate for the mcdstub Nicolas Eder
@ 2024-02-29 15:33   ` Alex Bennée
  0 siblings, 0 replies; 28+ messages in thread
From: Alex Bennée @ 2024-02-29 15:33 UTC (permalink / raw)
  To: Nicolas Eder; +Cc: qemu-devel, Philippe Mathieu-Daudé, Christian Boenig

Nicolas Eder <nicolas.eder@lauterbach.com> writes:

> mcdstub files created including the shared header between the mcd shared library and the mcdstub.
> MAINTAINERS file updated

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v5 02/18] gdbstub: hex conversion functions moved to cutils.h
  2023-12-20 16:25 ` [PATCH v5 02/18] gdbstub: hex conversion functions moved to cutils.h Nicolas Eder
@ 2024-02-29 15:33   ` Alex Bennée
  0 siblings, 0 replies; 28+ messages in thread
From: Alex Bennée @ 2024-02-29 15:33 UTC (permalink / raw)
  To: Nicolas Eder; +Cc: qemu-devel, Philippe Mathieu-Daudé, Christian Boenig

Nicolas Eder <nicolas.eder@lauterbach.com> writes:

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v5 04/18] gdbstub: DebugClass added to system mode.
  2023-12-20 16:25 ` [PATCH v5 04/18] gdbstub: DebugClass added to system mode Nicolas Eder
@ 2024-02-29 16:35   ` Alex Bennée
  2024-03-05 11:27     ` nicolas.eder
  0 siblings, 1 reply; 28+ messages in thread
From: Alex Bennée @ 2024-02-29 16:35 UTC (permalink / raw)
  To: Nicolas Eder; +Cc: qemu-devel, Philippe Mathieu-Daudé, Christian Boenig

Nicolas Eder <nicolas.eder@lauterbach.com> writes:

> This class is used to abstract debug features between different debuggers
> ---
>  debug/common/debug.c     | 33 +++++++++++++++++++++++++++++++++
>  debug/common/meson.build |  1 +
>  debug/gdbstub/system.c   | 18 ++++++++++++++++++
>  debug/meson.build        |  1 +
>  include/hw/boards.h      |  1 +
>  include/qemu/debug.h     | 20 ++++++++++++++++++++
>  include/qemu/typedefs.h  |  2 ++
>  system/cpus.c            |  9 ++++++++-
>  8 files changed, 84 insertions(+), 1 deletion(-)
>
> diff --git a/debug/common/debug.c b/debug/common/debug.c
> index c24aaf1202..476c969c98 100644
> --- a/debug/common/debug.c
> +++ b/debug/common/debug.c
> @@ -16,3 +16,36 @@
>   *
>   * SPDX-License-Identifier: LGPL-2.0+
>   */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/debug.h"
> +#include "qom/object_interfaces.h"
> +
> +static void debug_instance_init(Object *obj)
> +{
> +}
> +
> +static void debug_finalize(Object *obj)
> +{
> +}
> +
> +static void debug_class_init(ObjectClass *oc, void *data)
> +{
> +}
> +
> +static const TypeInfo debug_info = {
> +    .name = TYPE_DEBUG,
> +    .parent = TYPE_OBJECT,
> +    .instance_size = sizeof(DebugState),
> +    .instance_init = debug_instance_init,
> +    .instance_finalize = debug_finalize,
> +    .class_size = sizeof(DebugClass),
> +    .class_init = debug_class_init
> +};
> +
> +static void debug_register_types(void)
> +{
> +    type_register_static(&debug_info);
> +}

You shouldn't need empty functions if you are not using them. Moreover
you should use the inheritance feature and have something like:

static void gdb_debug_class_init(ObjectClass *klass, void *data)
{
    DebugClass *dc = DEBUG_CLASS(klass);
    dc->set_stop_cpu = gdb_set_stop_cpu;
};

static const TypeInfo debug_info[] = {
    {
        .name = TYPE_DEBUG,
        .parent = TYPE_OBJECT,
        .instance_size = sizeof(DebugState),
        .class_size = sizeof(DebugClass),
        .abstract = true,
    },
    {
        .name = TYPE_GDB_DEBUG,
        .parent = TYPE_DEBUG,
        .class_init = gdb_debug_class_init,
    },
};

DEFINE_TYPES(debug_info)


> +
<snip>
>  
> +/**
> + * gdb_init_debug_class() - initialize gdb-specific DebugClass
> + */
> +static void gdb_init_debug_class(void)
> +{
> +    Object *obj;
> +    obj = object_new(TYPE_DEBUG);
> +    DebugState *ds = DEBUG(obj);
> +    DebugClass *dc = DEBUG_GET_CLASS(ds);
> +    dc->set_stop_cpu = gdb_set_stop_cpu;

This should be part of the class init above

> +    MachineState *ms = MACHINE(qdev_get_machine());
> +    ms->debug_state = ds;
> +}
>          }
>      } else {
> -        gdb_set_stop_cpu(cpu);
> +        MachineState *ms = MACHINE(qdev_get_machine());
> +        DebugState *ds = ms->debug_state;
> +        DebugClass *dc = DEBUG_GET_CLASS(ds);
> +
> +        if (dc->set_stop_cpu) {
> +            dc->set_stop_cpu(cpu);
> +        }

If there is no explicit state perhaps we should just save the class
rather than the instance.

>          qemu_system_debug_request();
>          cpu->stopped = true;
>      }

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v5 05/18] mcdstub: memory helper functions added
  2023-12-20 16:25 ` [PATCH v5 05/18] mcdstub: memory helper functions added Nicolas Eder
@ 2024-02-29 16:56   ` Alex Bennée
  2024-03-05 11:03     ` nicolas.eder
  0 siblings, 1 reply; 28+ messages in thread
From: Alex Bennée @ 2024-02-29 16:56 UTC (permalink / raw)
  To: Nicolas Eder; +Cc: qemu-devel, Philippe Mathieu-Daudé, Christian Boenig

Nicolas Eder <nicolas.eder@lauterbach.com> writes:

> ---
>  include/exec/cpu-common.h |  3 +++
>  include/exec/memory.h     |  9 +++++++++
>  system/memory.c           | 11 +++++++++++
>  system/physmem.c          | 26 ++++++++++++++++++++++++++
>  4 files changed, 49 insertions(+)
>
> diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
> index 41115d8919..dd989b5ab2 100644
> --- a/include/exec/cpu-common.h
> +++ b/include/exec/cpu-common.h
> @@ -182,6 +182,9 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length);
>  int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
>                          void *ptr, size_t len, bool is_write);
>  
> +int cpu_memory_get_physical_address(CPUState *cpu, vaddr *addr, size_t *len);
> +
> +
>  /* vl.c */
>  void list_cpus(void);
>  
> diff --git a/include/exec/memory.h b/include/exec/memory.h
> index 831f7c996d..174de807d5 100644
> --- a/include/exec/memory.h
> +++ b/include/exec/memory.h
> @@ -3142,6 +3142,15 @@ bool ram_block_discard_is_disabled(void);
>   */
>  bool ram_block_discard_is_required(void);
>  
> +/*
> + * mcd_find_address_space() - Find the address spaces with the corresponding
> + * name.
> + *
> + * Currently only used by the mcd debugger.
> + * @as_name: Name to look for.
> + */
> +AddressSpace *mcd_find_address_space(const char *as_name);
> +

Don't hard code this for mcd - maybe address_space_find_byname()?

>  #endif
>  
>  #endif
> diff --git a/system/memory.c b/system/memory.c
> index 798b6c0a17..9a8fa79e0c 100644
> --- a/system/memory.c
> +++ b/system/memory.c
> @@ -3562,6 +3562,17 @@ void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled)
>      }
>  }
>  
> +AddressSpace *mcd_find_address_space(const char *as_name)
> +{
> +    AddressSpace *as;
> +    QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
> +        if (strcmp(as->name, as_name) == 0) {
> +            return as;
> +        }
> +    }
> +    return NULL;
> +}
> +
>  void memory_region_init_ram(MemoryRegion *mr,
>                              Object *owner,
>                              const char *name,
> diff --git a/system/physmem.c b/system/physmem.c
> index a63853a7bc..70733c67c7 100644
> --- a/system/physmem.c
> +++ b/system/physmem.c
> @@ -3422,6 +3422,32 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
>      return 0;
>  }
>  
> +int cpu_memory_get_physical_address(CPUState *cpu, vaddr *addr, size_t *len)
> +{
> +    hwaddr phys_addr;
> +    vaddr l, page;
> +
> +    cpu_synchronize_state(cpu);
> +    MemTxAttrs attrs;
> +
> +    page = *addr & TARGET_PAGE_MASK;
> +    phys_addr = cpu_get_phys_page_attrs_debug(cpu, page, &attrs);
> +    /* if no physical page mapped, return an error */
> +    if (phys_addr == -1) {
> +        return -1;
> +    }
> +    l = (page + TARGET_PAGE_SIZE) - *addr;
> +    if (l > *len) {
> +        l = *len;
> +    }
> +    phys_addr += (*addr & ~TARGET_PAGE_MASK);
> +
> +    /* set output values */
> +    *addr = phys_addr;
> +    *len = l;
> +    return 0;
> +}

I think there is some scope to re-factor some code that is shared with
cpu_memory_rw_debug. In fact rather than using this to feed
mcd_read_write_memory() maybe you really just want a
cpu_physical_memory_rw_debug()?

Although as you are going from vaddr anyway where does
cpu_memory_rw_debug() fail for you?

> +
>  /*
>   * Allows code that needs to deal with migration bitmaps etc to still be built
>   * target independent.

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v5 05/18] mcdstub: memory helper functions added
  2024-02-29 16:56   ` Alex Bennée
@ 2024-03-05 11:03     ` nicolas.eder
  0 siblings, 0 replies; 28+ messages in thread
From: nicolas.eder @ 2024-03-05 11:03 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Philippe Mathieu-Daudé, Christian Boenig

[-- Attachment #1: Type: text/plain, Size: 1231 bytes --]

On 29/02/2024 17:56, Alex Bennée wrote:

> I think there is some scope to re-factor some code that is shared with
> cpu_memory_rw_debug. In fact rather than using this to feed
> mcd_read_write_memory() maybe you really just want a
> cpu_physical_memory_rw_debug()?
>
> Although as you are going from vaddr anyway where does
> cpu_memory_rw_debug() fail for you?

Hi Alex, thanks for your feedback!

The reason I wrote the memory access the way I did is the following: 
Regardless of the state of the CPU, MCD always wants to be able to 
access all memory spaces (here secure and non-secure).

For example, if the CPU is currently in secure mode I still want to be 
able to perform a memory access via the non-secure memory space. This 
access should be successful, if a non-secure memory address is requested 
and unsuccessful, if a secure memory address is requested.

I could not get this decoupled memory access to work using the existing 
cpu_memory_rw_debug(). It only works properly when using the current 
main memory space (e.g. secure when the CPU is in secure mode).

I did not want to make changes to the existing memory functions because 
I think I could not have checked for all repercussions.

Best regards

Nicolas

[-- Attachment #2: Type: text/html, Size: 1616 bytes --]

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v5 11/18] mcdstub: system and core queries added
  2024-02-29 15:11   ` Alex Bennée
@ 2024-03-05 11:08     ` nicolas.eder
  0 siblings, 0 replies; 28+ messages in thread
From: nicolas.eder @ 2024-03-05 11:08 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Philippe Mathieu-Daudé, Christian Boenig

[-- Attachment #1: Type: text/plain, Size: 336 bytes --]


On 29/02/2024 16:11, Alex Bennée wrote:
> Hopefully the code can be more generic now and avoid having to re-parse
> generated xml.

I adapted my code to the recent register-related changes. There is still 
some arm-specific code, but thanks to gdb_get_register_list() and 
gdb_read_register() I could completely omit the XML parsing.

[-- Attachment #2: Type: text/html, Size: 683 bytes --]

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v5 04/18] gdbstub: DebugClass added to system mode.
  2024-02-29 16:35   ` Alex Bennée
@ 2024-03-05 11:27     ` nicolas.eder
  0 siblings, 0 replies; 28+ messages in thread
From: nicolas.eder @ 2024-03-05 11:27 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Philippe Mathieu-Daudé, Christian Boenig

[-- Attachment #1: Type: text/plain, Size: 1351 bytes --]

Hi Alex,

I managed to include all of your feedback except for the one on this patch:

> You shouldn't need empty functions if you are not using them. Moreover
> you should use the inheritance feature and have something like:
>
> static void gdb_debug_class_init(ObjectClass *klass, void *data)
> {
>      DebugClass *dc = DEBUG_CLASS(klass);
>      dc->set_stop_cpu = gdb_set_stop_cpu;
> };
>
> static const TypeInfo debug_info[] = {
>      {
>          .name = TYPE_DEBUG,
>          .parent = TYPE_OBJECT,
>          .instance_size = sizeof(DebugState),
>          .class_size = sizeof(DebugClass),
>          .abstract = true,
>      },
>      {
>          .name = TYPE_GDB_DEBUG,
>          .parent = TYPE_DEBUG,
>          .class_init = gdb_debug_class_init,
>      },
> };
>
> DEFINE_TYPES(debug_info)

My approach was to delete debug/common/debug.c and put the above code 
into debug/gdbstub/system.c and an analog version for MCD in 
debug/mcdstub/mcdstub.c. However, I don't know when to store the 
DebugClass to the MachineState (MachineState *ms = 
MACHINE(qdev_get_machine()); ms->debug_class = dc;). It doesn't seem to 
work when inside of gdb_debug_class_init().

I still lack an understanding of when/how exactly gdb_debug_class_init() 
is called.

Was my approach wrong and when should I store the DebugClass?

Best regards,

Nicolas

[-- Attachment #2: Type: text/html, Size: 2272 bytes --]

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2024-03-05 11:28 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-20 16:25 [PATCH v5 00/18] first version of mcdstub Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 01/18] gdbstub, mcdstub: file and build structure adapted to accomodate for the mcdstub Nicolas Eder
2024-02-29 15:33   ` Alex Bennée
2023-12-20 16:25 ` [PATCH v5 02/18] gdbstub: hex conversion functions moved to cutils.h Nicolas Eder
2024-02-29 15:33   ` Alex Bennée
2023-12-20 16:25 ` [PATCH v5 03/18] gdbstub: GDBRegisterState moved to gdbstub.h so it can be used outside of the gdbstub Nicolas Eder
2024-02-29 15:23   ` Alex Bennée
2023-12-20 16:25 ` [PATCH v5 04/18] gdbstub: DebugClass added to system mode Nicolas Eder
2024-02-29 16:35   ` Alex Bennée
2024-03-05 11:27     ` nicolas.eder
2023-12-20 16:25 ` [PATCH v5 05/18] mcdstub: memory helper functions added Nicolas Eder
2024-02-29 16:56   ` Alex Bennée
2024-03-05 11:03     ` nicolas.eder
2023-12-20 16:25 ` [PATCH v5 06/18] mcdstub: -mcd start option added, mcd specific defines added Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 07/18] mcdstub: mcdserver initialization functions added Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 08/18] cutils: qemu_strtou32 function added Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 09/18] mcdstub: TCP packet plumbing added Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 10/18] mcdstub: open and close server functions added Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 11/18] mcdstub: system and core queries added Nicolas Eder
2024-02-29 15:11   ` Alex Bennée
2024-03-05 11:08     ` nicolas.eder
2023-12-20 16:25 ` [PATCH v5 12/18] mcdstub: all core specific " Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 13/18] mcdstub: go, step and break added Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 14/18] mcdstub: state query added Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 15/18] mcdstub: skeleton for reset handling added Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 16/18] mcdstub: register access added Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 17/18] mcdstub: memory " Nicolas Eder
2023-12-20 16:25 ` [PATCH v5 18/18] mcdstub: break/watchpoints added Nicolas Eder

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.