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 if (p_monitor) {
99 for (int i = 0; i < txn.get_data_length(); i++) {
100 qmp_socket.enqueue(txn.get_data_ptr()[i]);
101 }
102 }
103 }
104
105 void start_of_simulation()
106 {
107 reader_thread = std::thread([this]() {
109 struct pollfd qmp_poll;
110 qmp_poll.fd = m_sockfd;
111 qmp_poll.events = POLLIN;
112 int ret;
113 while (!stop_running) {
114 ret = poll(&qmp_poll, 1, QMP_SOCK_POLL_TIMEOUT);
115 if ((ret == -1 && errno == EINTR) || (ret == 0) /*timeout*/) {
116 continue;
117 } else if ((ret > 0) && (qmp_poll.revents & POLLIN)) {
118 if (!qmp_recv()) break;
119 } else {
120 break;
121 }
122 }
123
124 if (m_sockfd > 0) {
125 close(m_sockfd);
126 }
127 m_sockfd = 0;
128 });
129 }
130
131 bool qmp_recv()
132 {
133 unsigned char buffer[QMP_RECV_BUFFER_LEN];
134 int l = recv(m_sockfd, buffer, QMP_RECV_BUFFER_LEN, 0);
135 if (l < 0) return false;
136 for (int i = 0; i < l; i++) {
137 qmp_socket.enqueue(buffer[i]);
138 }
139 return true;
140 }
141
143 {
144 SCP_INFO(())("Connecting QMP socket to unix socket {}", socket_path);
145
146 m_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
147 if (m_sockfd <= 0) {
148 SCP_ERR(())("Unable to connect to QMP socket");
149 }
150
151 struct sockaddr_un addr;
152 memset(&addr, 0, sizeof(struct sockaddr_un));
153 addr.sun_family = AF_UNIX;
154 strncpy(addr.sun_path, socket_path.c_str(), sizeof(addr.sun_path) - 1);
155
156 if (connect(m_sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) == -1) {
157 SCP_ERR(())("Unable to connect to QMP socket");
158 }
159
160 if (!p_monitor) {
161 std::string msg = R"({ "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } })";
162 if (write(m_sockfd, msg.c_str(), msg.size()) == -1) {
163 SCP_ERR(())("Can't send initialization command");
164 }
165 }
166 }
167
168 ~qmp()
169 {
170 stop_running = true;
171 if (reader_thread.joinable()) {
172 reader_thread.join();
173 }
174 }
175};
176
177extern "C" void module_register();
178#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