quic/qbox
Loading...
Searching...
No Matches
qmp.h
1/*
2 * This file is part of libqbox
3 * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8#ifndef _LIBQBOX_COMPONENTS_RESET_QMP_H
9#define _LIBQBOX_COMPONENTS_RESET_QMP_H
10
11#include <ports/qemu-initiator-signal-socket.h>
12#include <ports/qemu-target-signal-socket.h>
13#include <ports/biflow-socket.h>
14#include <device.h>
15#include <qemu-instance.h>
16#include <module_factory_registery.h>
17#ifdef _WIN32
18#include <winsock2.h>
19#include <afunix.h>
20#include <ws2tcpip.h>
21#else
22#include <fcntl.h>
23#include <unistd.h>
24#include <sys/types.h>
25#include <sys/socket.h>
26#include <sys/un.h>
27#include <poll.h>
28#endif
29#include <atomic>
30
31
32#define QMP_SOCK_POLL_TIMEOUT 300
33#define QMP_RECV_BUFFER_LEN 8192
34
35#ifdef _WIN32
36typedef SOCKET socket_t;
37static constexpr socket_t INVALID_SOCK = INVALID_SOCKET;
38#define CLOSE_SOCKET closesocket
39#define SOCK_POLL WSAPoll
40#else
41typedef int socket_t;
42static constexpr socket_t INVALID_SOCK = -1;
43#define CLOSE_SOCKET close
44#define SOCK_POLL poll
45#endif
46
47class qmp : public sc_core::sc_module
48{
49 SCP_LOGGER();
50
52 socket_t m_sockfd = INVALID_SOCK;
53
54 std::string buffer = "";
55 std::thread reader_thread;
56 std::string socket_path;
57 std::atomic_bool stop_running;
58
59public:
60 cci::cci_param<std::string> p_qmp_str;
61 cci::cci_param<bool> p_monitor;
62
63 qmp(const sc_core::sc_module_name& name, sc_core::sc_object* o): qmp(name, *(dynamic_cast<QemuInstance*>(o))) {}
64 qmp(const sc_core::sc_module_name& n, QemuInstance& inst)
65 : sc_core::sc_module(n)
66 , p_qmp_str("qmp_str", "", "qmp options string, i.e. unix:./qmp-sock,server,wait=off")
67 , qmp_socket("qmp_socket")
68 , p_monitor("monitor", true, "use the HMP monitor (true, default) - or QMP (false) ")
69 , stop_running{ false }
70 {
71#ifdef _WIN32
73 if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) {
74 SCP_FATAL(())("WSAStartup failed");
75 }
76#endif
77 SCP_TRACE(())("qmp constructor");
78 if (p_qmp_str.get_value().empty()) {
79 SCP_FATAL(())("qmp options string is empty!");
80 }
81 if (p_qmp_str.get_value().find("unix") != std::string::npos) {
82 auto first = p_qmp_str.get_value().find(":") + 1;
83 auto last = p_qmp_str.get_value().find(",");
84 socket_path = p_qmp_str.get_value().substr(first, last - first);
85 // unlink(socket_path.c_str());
86 }
87 // https://qemu-project.gitlab.io/qemu/interop/qemu-qmp-ref.html
88 if (p_monitor) {
89 inst.add_arg("-monitor");
90 } else {
91 inst.add_arg("-qmp");
92 }
93 inst.add_arg(p_qmp_str.get_value().c_str());
94
95 qmp_socket.register_b_transport(this, &qmp::b_transport);
96 qmp_socket.can_receive_any();
97 }
98
99 void b_transport(tlm::tlm_generic_payload& txn, sc_core::sc_time& delay)
100 {
101 char* data = (char*)txn.get_data_ptr();
102 int length = txn.get_data_length();
103
104 /* collect the string in a buffer, till we see a newline, at which point, if it starts with a brace, send it as
105 * is, otherwise wrap it as a human monitor command */
106 buffer = buffer + std::string(data, length);
107 if (data[length - 1] == '\n' || data[length - 1] == '\r') {
108 if (!p_monitor) {
109 buffer.erase(
110 std::remove_if(buffer.begin(), buffer.end(), [](char c) { return c == '\r' || c == '\n'; }),
111 buffer.end());
112 if (buffer[0] != '{') {
113 SCP_WARN(())
114 ("Wrapping raw HMP command {} on QMP interface, consider selecting monitor mode", buffer);
115 buffer = R"("{ "execute": "human-monitor-command", "arguments": { "command-line": ")" + buffer +
116 R"(" } }")";
117 }
118 }
119 if (m_sockfd != INVALID_SOCK) {
120 send(m_sockfd, buffer.c_str(), (int)buffer.length(), 0);
121 }
122 buffer = "";
123 }
124 /* echo for the user */
125 if (p_monitor) {
126 for (int i = 0; i < txn.get_data_length(); i++) {
127 qmp_socket.enqueue(txn.get_data_ptr()[i]);
128 }
129 }
130 }
131
132 void start_of_simulation()
133 {
134 reader_thread = std::thread([this]() {
136 struct pollfd qmp_poll;
137 qmp_poll.fd = m_sockfd;
138 qmp_poll.events = POLLIN;
139 int ret;
140 while (!stop_running) {
141 ret = SOCK_POLL(&qmp_poll, 1, QMP_SOCK_POLL_TIMEOUT);
142 if ((ret == -1 && errno == EINTR) || (ret == 0) /*timeout*/) {
143 continue;
144 } else if ((ret > 0) && (qmp_poll.revents & POLLIN)) {
145 if (!qmp_recv()) break;
146 } else {
147 break;
148 }
149 }
150
151 if (m_sockfd != INVALID_SOCK) {
152 CLOSE_SOCKET(m_sockfd);
153 }
154 m_sockfd = INVALID_SOCK;
155 });
156 }
157
158 bool qmp_recv()
159 {
160 char buffer[QMP_RECV_BUFFER_LEN];
161 int l = recv(m_sockfd, buffer, QMP_RECV_BUFFER_LEN, 0);
162 if (l < 0) return false;
163 for (int i = 0; i < l; i++) {
164 qmp_socket.enqueue(buffer[i]);
165 }
166 return true;
167 }
168
170 {
171 SCP_INFO(())("Connecting QMP socket to unix socket {}", socket_path);
172
173 m_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
174 if (m_sockfd == INVALID_SOCK) {
175 SCP_ERR(())("Unable to connect to QMP socket");
176 }
177
178 struct sockaddr_un addr;
179 memset(&addr, 0, sizeof(struct sockaddr_un));
180 addr.sun_family = AF_UNIX;
181 strncpy(addr.sun_path, socket_path.c_str(), sizeof(addr.sun_path) - 1);
182
183 if (connect(m_sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) == -1) {
184 SCP_ERR(())("Unable to connect to QMP socket");
185 }
186
187 if (!p_monitor) {
188 std::string msg = R"({ "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } })";
189 if (send(m_sockfd, msg.c_str(), (int)msg.size(), 0) == -1) {
190 SCP_ERR(())("Can't send initialization command");
191 }
192 }
193 }
194
195 ~qmp()
196 {
197 stop_running = true;
198 if (reader_thread.joinable()) {
199 reader_thread.join();
200 }
201#ifdef _WIN32
202 WSACleanup();
203#endif
204 }
205};
206
207extern "C" void module_register();
208#endif //_LIBQBOX_COMPONENTS_QMP_H
This class encapsulates a libqemu-cxx qemu::LibQemu instance. It handles QEMU parameters and instance...
Definition qemu-instance.h:86
void add_arg(const char *arg)
Add a command line argument to the qemu instance.
Definition qemu-instance.h:327
Definition target.h:160
Definition biflow-socket.h:471
Definition qmp.h:48