* [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* 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
* [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* 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 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
* [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* 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
* [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, ¤t_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, ¤t_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)¤t_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* 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, ¤t_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, ¤t_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)¤t_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
* [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