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#include <fcntl.h>
18#include <unistd.h>
19#include <sys/types.h>
20#include <sys/socket.h>
21#include <sys/un.h>
22#include <atomic>
23
24#define QMP_SOCK_POLL_TIMEOUT 300
25#define QMP_RECV_BUFFER_LEN 8192
26class qmp : public sc_core::sc_module
27{
28 SCP_LOGGER();
29
31 int m_sockfd = 0;
32
33 std::string buffer = "";
34 std::thread reader_thread;
35 std::string socket_path;
36 std::atomic_bool stop_running;
37
38public:
39 cci::cci_param<std::string> p_qmp_str;
40 cci::cci_param<bool> p_monitor;
41
42 qmp(const sc_core::sc_module_name& name, sc_core::sc_object* o): qmp(name, *(dynamic_cast<QemuInstance*>(o))) {}
43 qmp(const sc_core::sc_module_name& n, QemuInstance& inst)
44 : sc_core::sc_module(n)
45 , p_qmp_str("qmp_str", "", "qmp options string, i.e. unix:./qmp-sock,server,wait=off")
46 , qmp_socket("qmp_socket")
47 , p_monitor("monitor", true, "use the HMP monitor (true, default) - or QMP (false) ")
48 , stop_running{ false }
49 {
50 SCP_TRACE(())("qmp constructor");
51 if (p_qmp_str.get_value().empty()) {
52 SCP_FATAL(())("qmp options string is empty!");
53 }
54 if (p_qmp_str.get_value().find("unix") != std::string::npos) {
55 auto first = p_qmp_str.get_value().find(":") + 1;
56 auto last = p_qmp_str.get_value().find(",");
57 socket_path = p_qmp_str.get_value().substr(first, last - first);
58 // unlink(socket_path.c_str());
59 }
60 // https://qemu-project.gitlab.io/qemu/interop/qemu-qmp-ref.html
61 if (p_monitor) {
62 inst.add_arg("-monitor");
63 } else {
64 inst.add_arg("-qmp");
65 }
66 inst.add_arg(p_qmp_str.get_value().c_str());
67
68 qmp_socket.register_b_transport(this, &qmp::b_transport);
69 qmp_socket.can_receive_any();
70 }
71
72 void b_transport(tlm::tlm_generic_payload& txn, sc_core::sc_time& delay)
73 {
74 char* data = (char*)txn.get_data_ptr();
75 int length = txn.get_data_length();
76
77 /* collect the string in a buffer, till we see a newline, at which point, if it starts with a brace, send it as
78 * is, otherwise wrap it as a human monitor command */
79 buffer = buffer + std::string(data, length);
80 if (data[length - 1] == '\n' || data[length - 1] == '\r') {
81 if (!p_monitor) {
82 buffer.erase(
83 std::remove_if(buffer.begin(), buffer.end(), [](char c) { return c == '\r' || c == '\n'; }),
84 buffer.end());
85 if (buffer[0] != '{') {
86 SCP_WARN(())
87 ("Wrapping raw HMP command {} on QMP interface, consider selecting monitor mode", buffer);
88 buffer = R"("{ "execute": "human-monitor-command", "arguments": { "command-line": ")" + buffer +
89 R"(" } }")";
90 }
91 }
92 if (m_sockfd > 0) {
93 send(m_sockfd, buffer.c_str(), buffer.length(), 0);
94 }
95 buffer = "";
96 }
97 /* echo for the user */
98 for (int i = 0; i < txn.get_data_length(); i++) {
99 qmp_socket.enqueue(txn.get_data_ptr()[i]);
100 }
101 }
102
103 void start_of_simulation()
104 {
105 reader_thread = std::thread([this]() {
107 struct pollfd qmp_poll;
108 qmp_poll.fd = m_sockfd;
109 qmp_poll.events = POLLIN;
110 int ret;
111 while (!stop_running) {
112 ret = poll(&qmp_poll, 1, QMP_SOCK_POLL_TIMEOUT);
113 if ((ret == -1 && errno == EINTR) || (ret == 0) /*timeout*/) {
114 continue;
115 } else if ((ret > 0) && (qmp_poll.revents & POLLIN)) {
116 if (!qmp_recv()) break;
117 } else {
118 break;
119 }
120 }
121
122 if (m_sockfd > 0) {
123 close(m_sockfd);
124 }
125 m_sockfd = 0;
126 });
127 }
128
129 bool qmp_recv()
130 {
131 unsigned char buffer[QMP_RECV_BUFFER_LEN];
132 int l = recv(m_sockfd, buffer, QMP_RECV_BUFFER_LEN, 0);
133 if (l < 0) return false;
134 for (int i = 0; i < l; i++) {
135 qmp_socket.enqueue(buffer[i]);
136 }
137 return true;
138 }
139
141 {
142 SCP_INFO(())("Connecting QMP socket to unix socket {}", socket_path);
143
144 m_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
145 if (m_sockfd <= 0) {
146 SCP_ERR(())("Unable to connect to QMP socket");
147 }
148
149 struct sockaddr_un addr;
150 memset(&addr, 0, sizeof(struct sockaddr_un));
151 addr.sun_family = AF_UNIX;
152 strncpy(addr.sun_path, socket_path.c_str(), sizeof(addr.sun_path) - 1);
153
154 if (connect(m_sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) == -1) {
155 SCP_ERR(())("Unable to connect to QMP socket");
156 }
157
158 if (!p_monitor) {
159 std::string msg = R"({ "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } })";
160 if (write(m_sockfd, msg.c_str(), msg.size()) == -1) {
161 SCP_ERR(())("Can't send initialization command");
162 }
163 }
164 }
165
166 ~qmp()
167 {
168 stop_running = true;
169 if (reader_thread.joinable()) {
170 reader_thread.join();
171 }
172 }
173};
174
175extern "C" void module_register();
176#endif //_LIBQBOX_COMPONENTS_QMP_H
This class encapsulates a libqemu-cxx qemu::LibQemu instance. It handles QEMU parameters and instance...
Definition qemu-instance.h:89
void add_arg(const char *arg)
Add a command line argument to the qemu instance.
Definition qemu-instance.h:329
Definition target.h:160
Definition biflow-socket.h:472
Definition qmp.h:27