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