cpu: Add HTM Instruction Flags
[gem5.git] / src / dev / virtio / fs9p.cc
1 /*
2 * Copyright (c) 2014-2017 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include "dev/virtio/fs9p.hh"
39
40 #include <fcntl.h>
41 #include <netdb.h>
42 #include <netinet/in.h>
43 #include <sys/socket.h>
44 #include <sys/types.h>
45 #include <sys/un.h>
46 #include <sys/wait.h>
47 #include <unistd.h>
48
49 #include <csignal>
50 #include <fstream>
51
52 #include "base/callback.hh"
53 #include "base/output.hh"
54 #include "debug/VIO9P.hh"
55 #include "debug/VIO9PData.hh"
56 #include "params/VirtIO9PBase.hh"
57 #include "params/VirtIO9PDiod.hh"
58 #include "params/VirtIO9PProxy.hh"
59 #include "params/VirtIO9PSocket.hh"
60 #include "sim/system.hh"
61
62 struct P9MsgInfo {
63 P9MsgInfo(P9MsgType _type, std::string _name)
64 : type(_type), name(_name) {}
65
66 P9MsgType type;
67 std::string name;
68 };
69
70 typedef std::map<P9MsgType, P9MsgInfo> P9MsgInfoMap;
71
72 #define P9MSG(type, name) \
73 { (type), P9MsgInfo((type), "T" # name ) }, \
74 { (type + 1), P9MsgInfo((type + 1), "R" # name ) }
75
76 static const P9MsgInfoMap p9_msg_info {
77 P9MSG(6, LERROR),
78 P9MSG(8, STATFS),
79 P9MSG(12, LOPEN),
80 P9MSG(14, LCREATE),
81 P9MSG(16, SYMLINK),
82 P9MSG(18, MKNOD),
83 P9MSG(20, RENAME),
84 P9MSG(22, READLINK),
85 P9MSG(24, GETATTR),
86 P9MSG(26, SETATTR),
87 P9MSG(30, XATTRWALK),
88 P9MSG(32, XATTRCREATE),
89 P9MSG(40, READDIR),
90 P9MSG(50, FSYNC),
91 P9MSG(52, LOCK),
92 P9MSG(54, GETLOCK),
93 P9MSG(70, LINK),
94 P9MSG(72, MKDIR),
95 P9MSG(74, RENAMEAT),
96 P9MSG(76, UNLINKAT),
97 P9MSG(100, VERSION),
98 P9MSG(102, AUTH),
99 P9MSG(104, ATTACH),
100 P9MSG(106, ERROR),
101 P9MSG(108, FLUSH),
102 P9MSG(110, WALK),
103 P9MSG(112, OPEN),
104 P9MSG(114, CREATE),
105 P9MSG(116, READ),
106 P9MSG(118, WRITE),
107 P9MSG(120, CLUNK),
108 P9MSG(122, REMOVE),
109 P9MSG(124, STAT),
110 P9MSG(126, WSTAT),
111 };
112
113 #undef P9MSG
114
115 VirtIO9PBase::VirtIO9PBase(Params *params)
116 : VirtIODeviceBase(params, ID_9P,
117 sizeof(Config) + params->tag.size(),
118 F_MOUNT_TAG),
119 queue(params->system->physProxy, byteOrder, params->queueSize, *this)
120 {
121 config.reset((Config *)
122 operator new(configSize));
123 config->len = htog(params->tag.size(), byteOrder);
124 memcpy(config->tag, params->tag.c_str(), params->tag.size());
125
126 registerQueue(queue);
127 }
128
129
130 VirtIO9PBase::~VirtIO9PBase()
131 {
132 }
133
134 void
135 VirtIO9PBase::readConfig(PacketPtr pkt, Addr cfgOffset)
136 {
137 readConfigBlob(pkt, cfgOffset, (uint8_t *)config.get());
138 }
139
140 void
141 VirtIO9PBase::FSQueue::onNotifyDescriptor(VirtDescriptor *desc)
142 {
143 DPRINTF(VIO9P, "Got input data descriptor (len: %i)\n", desc->size());
144 DPRINTF(VIO9P, "\tPending transactions: %i\n", parent.pendingTransactions.size());
145
146 P9MsgHeader header;
147 desc->chainRead(0, (uint8_t *)&header, sizeof(header));
148 header = p9toh(header);
149
150 uint8_t data[header.len - sizeof(header)];
151 desc->chainRead(sizeof(header), data, sizeof(data));
152
153 // Keep track of pending transactions
154 parent.pendingTransactions[header.tag] = desc;
155
156 DPRINTF(VIO9P, "recvTMsg\n");
157 parent.dumpMsg(header, data, sizeof(data));
158
159 // Notify device of message
160 parent.recvTMsg(header, data, sizeof(data));
161 }
162
163 void
164 VirtIO9PBase::sendRMsg(const P9MsgHeader &header, const uint8_t *data, size_t size)
165 {
166 DPRINTF(VIO9P, "Sending RMsg\n");
167 dumpMsg(header, data, size);
168 DPRINTF(VIO9P, "\tPending transactions: %i\n", pendingTransactions.size());
169 assert(header.len >= sizeof(header));
170
171 VirtDescriptor *main_desc(pendingTransactions[header.tag]);
172 pendingTransactions.erase(header.tag);
173
174 // Find the first output descriptor
175 VirtDescriptor *out_desc(main_desc);
176 while (out_desc && !out_desc->isOutgoing())
177 out_desc = out_desc->next();
178 if (!out_desc)
179 panic("sendRMsg: Framing error, no output descriptor.\n");
180
181 P9MsgHeader header_out(htop9(header));
182 header_out.len = htop9(sizeof(P9MsgHeader) + size);
183
184 out_desc->chainWrite(0, (uint8_t *)&header_out, sizeof(header_out));
185 out_desc->chainWrite(sizeof(header_out), data, size);
186
187 queue.produceDescriptor(main_desc, sizeof(P9MsgHeader) + size);
188 kick();
189 }
190
191 void
192 VirtIO9PBase::dumpMsg(const P9MsgHeader &header, const uint8_t *data, size_t size)
193 {
194 #ifndef NDEBUG
195 if (!DTRACE(VIO9P))
196 return;
197
198 const P9MsgInfoMap::const_iterator it_msg(p9_msg_info.find(header.type));
199 if (it_msg != p9_msg_info.cend()) {
200 const P9MsgInfo &info(it_msg->second);
201 DPRINTF(VIO9P, "P9Msg[len = %i, type = %s (%i), tag = %i]\n",
202 header.len, info.name, header.type, header.tag);
203 } else {
204 DPRINTF(VIO9P, "P9Msg[len = %i, type = Unknown (%i), tag = %i]\n",
205 header.len, header.type, header.tag);
206 }
207 DDUMP(VIO9PData, data, size);
208 #endif
209 }
210
211
212 VirtIO9PProxy::VirtIO9PProxy(Params *params)
213 : VirtIO9PBase(params), deviceUsed(false)
214 {
215 }
216
217 VirtIO9PProxy::~VirtIO9PProxy()
218 {
219 }
220
221
222 void
223 VirtIO9PProxy::serialize(CheckpointOut &cp) const
224 {
225 if (deviceUsed) {
226 warn("Serializing VirtIO9Base device after device has been used. It is "
227 "likely that state will be lost, and that the device will cease "
228 "to work!");
229 }
230 SERIALIZE_SCALAR(deviceUsed);
231
232 VirtIO9PBase::serialize(cp);
233 }
234
235 void
236 VirtIO9PProxy::unserialize(CheckpointIn &cp)
237 {
238 UNSERIALIZE_SCALAR(deviceUsed);
239
240 if (deviceUsed) {
241 warn("Unserializing VirtIO9Base device after device has been used. It is "
242 "likely that state has been lost, and that the device will cease "
243 "to work!");
244 }
245 VirtIO9PBase::unserialize(cp);
246 }
247
248
249 void
250 VirtIO9PProxy::recvTMsg(const P9MsgHeader &header,
251 const uint8_t *data, size_t size)
252 {
253 deviceUsed = true;
254 assert(header.len == sizeof(header) + size);
255 // While technically not needed, we send the packet as one
256 // contiguous segment to make some packet dissectors happy.
257 uint8_t out[header.len];
258 P9MsgHeader header_out(htop9(header));
259 memcpy(out, (uint8_t *)&header_out, sizeof(header_out));
260 memcpy(out + sizeof(header_out), data, size);
261 writeAll(out, sizeof(header_out) + size);
262 }
263
264 void
265 VirtIO9PProxy::serverDataReady()
266 {
267 P9MsgHeader header;
268 readAll((uint8_t *)&header, sizeof(header));
269 header = p9toh(header);
270
271 const ssize_t payload_len(header.len - sizeof(header));
272 if (payload_len < 0)
273 panic("Payload length is negative!\n");
274 uint8_t data[payload_len];
275 readAll(data, payload_len);
276
277 sendRMsg(header, data, payload_len);
278 }
279
280
281 void
282 VirtIO9PProxy::readAll(uint8_t *data, size_t len)
283 {
284 while (len) {
285 ssize_t ret;
286 while ((ret = read(data, len)) == -EAGAIN)
287 ;
288 if (ret < 0)
289 panic("readAll: Read failed: %i\n", -ret);
290
291 len -= ret;
292 data += ret;
293 }
294 }
295
296 void
297 VirtIO9PProxy::writeAll(const uint8_t *data, size_t len)
298 {
299 while (len) {
300 ssize_t ret;
301 while ((ret = write(data, len)) == -EAGAIN)
302 ;
303 if (ret < 0)
304 panic("writeAll: write failed: %i\n", -ret);
305
306 len -= ret;
307 data += ret;
308 }
309 }
310
311
312
313 VirtIO9PDiod::VirtIO9PDiod(Params *params)
314 : VirtIO9PProxy(params),
315 fd_to_diod(-1), fd_from_diod(-1), diod_pid(-1)
316 {
317 // Register an exit callback so we can kill the diod process
318 registerExitCallback([this]() { terminateDiod(); });
319 }
320
321 VirtIO9PDiod::~VirtIO9PDiod()
322 {
323 }
324
325 void
326 VirtIO9PDiod::startup()
327 {
328 startDiod();
329 dataEvent.reset(new DiodDataEvent(*this, fd_from_diod, POLLIN));
330 pollQueue.schedule(dataEvent.get());
331 }
332
333 void
334 VirtIO9PDiod::startDiod()
335 {
336 const Params *p(dynamic_cast<const Params *>(params()));
337 int pipe_rfd[2];
338 int pipe_wfd[2];
339 const int DIOD_RFD = 3;
340 const int DIOD_WFD = 4;
341
342 const char *diod(p->diod.c_str());
343
344 DPRINTF(VIO9P, "Using diod at %s \n", p->diod.c_str());
345
346 if (pipe(pipe_rfd) == -1 || pipe(pipe_wfd) == -1)
347 panic("Failed to create DIOD pipes: %i\n", errno);
348
349 fd_to_diod = pipe_rfd[1];
350 fd_from_diod = pipe_wfd[0];
351
352 // Create Unix domain socket
353 int socket_id = socket(AF_UNIX, SOCK_STREAM, 0);
354 if (socket_id == -1) {
355 panic("Socket creation failed %i \n", errno);
356 }
357 // Bind the socket to a path which will not be read
358 struct sockaddr_un socket_address;
359 memset(&socket_address, 0, sizeof(struct sockaddr_un));
360 socket_address.sun_family = AF_UNIX;
361
362 const std::string socket_path = simout.resolve(p->socketPath);
363 fatal_if(!OutputDirectory::isAbsolute(socket_path), "Please make the" \
364 " output directory an absolute path, else diod will fail!\n");
365
366 // Prevent overflow in strcpy
367 fatal_if(sizeof(socket_address.sun_path) <= socket_path.length(),
368 "Incorrect length of socket path");
369 strncpy(socket_address.sun_path, socket_path.c_str(),
370 sizeof(socket_address.sun_path) - 1);
371 if (bind(socket_id, (struct sockaddr*) &socket_address,
372 sizeof(struct sockaddr_un)) == -1){
373 perror("Socket binding");
374 panic("Socket binding to %i failed - most likely the output dir" \
375 " and hence unused socket already exists \n", socket_id);
376 }
377
378 diod_pid = fork();
379 if (diod_pid == -1) {
380 panic("Fork failed: %i\n", errno);
381 } else if (diod_pid == 0) {
382 // Create the socket which will later by used by the diod process
383 close(STDIN_FILENO);
384 if (dup2(pipe_rfd[0], DIOD_RFD) == -1 ||
385 dup2(pipe_wfd[1], DIOD_WFD) == -1) {
386
387 panic("Failed to setup read/write pipes: %i\n",
388 errno);
389 }
390
391 // Start diod
392 execlp(diod, diod,
393 "-d", DTRACE(VIO9P) ? "1" : "0", // show debug output
394 "-f", // start in foreground
395 "-r", "3", // setup read FD
396 "-w", "4", // setup write FD
397 "-e", p->root.c_str(), // path to export
398 "-n", // disable security
399 "-S", // squash all users
400 "-l", socket_path.c_str(), // pass the socket
401 (char *)NULL);
402 perror("Starting DIOD");
403 panic("Failed to execute diod to %s: %i\n",socket_path, errno);
404 } else {
405 close(pipe_rfd[0]);
406 close(pipe_wfd[1]);
407 inform("Started diod with PID %u, you might need to manually kill " \
408 " diod if gem5 crashes \n", diod_pid);
409 }
410
411 #undef DIOD_RFD
412 #undef DIOD_WFD
413 }
414
415 ssize_t
416 VirtIO9PDiod::read(uint8_t *data, size_t len)
417 {
418 assert(fd_from_diod != -1);
419 const int ret(::read(fd_from_diod, (void *)data, len));
420 return ret < 0 ? -errno : ret;
421 }
422
423 ssize_t
424 VirtIO9PDiod::write(const uint8_t *data, size_t len)
425 {
426 assert(fd_to_diod != -1);
427 const int ret(::write(fd_to_diod, (const void *)data, len));
428 return ret < 0 ? -errno : ret;
429 }
430
431 void
432 VirtIO9PDiod::DiodDataEvent::process(int revent)
433 {
434 parent.serverDataReady();
435 }
436
437 void
438 VirtIO9PDiod::terminateDiod()
439 {
440 assert(diod_pid != -1);
441
442 DPRINTF(VIO9P, "Trying to kill diod at pid %u \n", diod_pid);
443
444 if (kill(diod_pid, SIGTERM) != 0) {
445 perror("Killing diod process");
446 warn("Failed to kill diod using SIGTERM");
447 return;
448 }
449
450 // Check if kill worked
451 for (unsigned i = 0; i < 5; i++) {
452 int wait_return = waitpid(diod_pid, NULL, WNOHANG);
453 if (wait_return == diod_pid) {
454 // Managed to kill diod
455 return;
456 } else if (wait_return == 0) {
457 // Diod is not killed so sleep and try again
458 usleep(500);
459 } else {
460 // Failed in waitpid
461 perror("Waitpid");
462 warn("Failed in waitpid");
463 }
464 }
465
466 // Try again to kill diod with sigkill
467 inform("Trying to kill diod with SIGKILL as SIGTERM failed \n");
468 if (kill(diod_pid, SIGKILL) != 0) {
469 perror("Killing diod process");
470 warn("Failed to kill diod using SIGKILL");
471 } else {
472 // Managed to kill diod
473 return;
474 }
475
476 }
477 VirtIO9PDiod *
478 VirtIO9PDiodParams::create()
479 {
480 return new VirtIO9PDiod(this);
481 }
482
483
484
485
486 VirtIO9PSocket::VirtIO9PSocket(Params *params)
487 : VirtIO9PProxy(params), fdSocket(-1)
488 {
489 }
490
491 VirtIO9PSocket::~VirtIO9PSocket()
492 {
493 }
494
495 void
496 VirtIO9PSocket::startup()
497 {
498 connectSocket();
499 dataEvent.reset(new SocketDataEvent(*this, fdSocket, POLLIN));
500 pollQueue.schedule(dataEvent.get());
501 }
502
503 void
504 VirtIO9PSocket::connectSocket()
505 {
506 const Params &p(dynamic_cast<const Params &>(*params()));
507
508 int ret;
509 struct addrinfo hints, *result;
510 memset(&hints, 0, sizeof(hints));
511 hints.ai_family = AF_UNSPEC;
512 hints.ai_socktype = SOCK_STREAM;
513 hints.ai_flags = 0;
514 hints.ai_protocol = 0;
515
516 if ((ret = getaddrinfo(p.server.c_str(), p.port.c_str(),
517 &hints, &result)) != 0)
518 panic("getaddrinfo: %s\n", gai_strerror(ret));
519
520 DPRINTF(VIO9P, "Connecting to 9p server '%s'.\n", p.server);
521 for (struct addrinfo *rp = result; rp; rp = rp->ai_next) {
522 fdSocket = socket(rp->ai_family, rp->ai_socktype,
523 rp->ai_protocol);
524 if (fdSocket == -1) {
525 continue;
526 } else if (connect(fdSocket, rp->ai_addr, rp->ai_addrlen) != -1) {
527 break;
528 } else {
529 close(fdSocket);
530 fdSocket = -1;
531 }
532 }
533
534 freeaddrinfo(result);
535
536 if (fdSocket == -1)
537 panic("Failed to connect to 9p server (%s:%s)", p.server, p.port);
538 }
539
540 void
541 VirtIO9PSocket::socketDisconnect()
542 {
543 panic("9P Socket disconnected!\n");
544 }
545
546 ssize_t
547 VirtIO9PSocket::read(uint8_t *data, size_t len)
548 {
549 assert(fdSocket != -1);
550 int ret;
551
552 ret = ::recv(fdSocket, (void *)data, len, 0);
553 if (ret == 0)
554 socketDisconnect();
555
556 return ret < 0 ? -errno : ret;
557 }
558
559 ssize_t
560 VirtIO9PSocket::write(const uint8_t *data, size_t len)
561 {
562 assert(fdSocket != -1);
563 int ret(::send(fdSocket, (const void *)data, len, 0));
564 return ret < 0 ? -errno : ret;
565 }
566
567 void
568 VirtIO9PSocket::SocketDataEvent::process(int revent)
569 {
570 parent.serverDataReady();
571 }
572
573
574 VirtIO9PSocket *
575 VirtIO9PSocketParams::create()
576 {
577 return new VirtIO9PSocket(this);
578 }