2 * Copyright (c) 2014-2017 ARM Limited
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.
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.
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.
38 #include "dev/virtio/fs9p.hh"
42 #include <netinet/in.h>
43 #include <sys/socket.h>
44 #include <sys/types.h>
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"
63 P9MsgInfo(P9MsgType _type
, std::string _name
)
64 : type(_type
), name(_name
) {}
70 typedef std::map
<P9MsgType
, P9MsgInfo
> P9MsgInfoMap
;
72 #define P9MSG(type, name) \
73 { (type), P9MsgInfo((type), "T" # name ) }, \
74 { (type + 1), P9MsgInfo((type + 1), "R" # name ) }
76 static const P9MsgInfoMap p9_msg_info
{
88 P9MSG(32, XATTRCREATE
),
115 VirtIO9PBase::VirtIO9PBase(Params
*params
)
116 : VirtIODeviceBase(params
, ID_9P
,
117 sizeof(Config
) + params
->tag
.size(),
119 queue(params
->system
->physProxy
, byteOrder
, params
->queueSize
, *this)
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());
126 registerQueue(queue
);
130 VirtIO9PBase::~VirtIO9PBase()
135 VirtIO9PBase::readConfig(PacketPtr pkt
, Addr cfgOffset
)
137 readConfigBlob(pkt
, cfgOffset
, (uint8_t *)config
.get());
141 VirtIO9PBase::FSQueue::onNotifyDescriptor(VirtDescriptor
*desc
)
143 DPRINTF(VIO9P
, "Got input data descriptor (len: %i)\n", desc
->size());
144 DPRINTF(VIO9P
, "\tPending transactions: %i\n", parent
.pendingTransactions
.size());
147 desc
->chainRead(0, (uint8_t *)&header
, sizeof(header
));
148 header
= p9toh(header
);
150 uint8_t data
[header
.len
- sizeof(header
)];
151 desc
->chainRead(sizeof(header
), data
, sizeof(data
));
153 // Keep track of pending transactions
154 parent
.pendingTransactions
[header
.tag
] = desc
;
156 DPRINTF(VIO9P
, "recvTMsg\n");
157 parent
.dumpMsg(header
, data
, sizeof(data
));
159 // Notify device of message
160 parent
.recvTMsg(header
, data
, sizeof(data
));
164 VirtIO9PBase::sendRMsg(const P9MsgHeader
&header
, const uint8_t *data
, size_t size
)
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
));
171 VirtDescriptor
*main_desc(pendingTransactions
[header
.tag
]);
172 pendingTransactions
.erase(header
.tag
);
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();
179 panic("sendRMsg: Framing error, no output descriptor.\n");
181 P9MsgHeader
header_out(htop9(header
));
182 header_out
.len
= htop9(sizeof(P9MsgHeader
) + size
);
184 out_desc
->chainWrite(0, (uint8_t *)&header_out
, sizeof(header_out
));
185 out_desc
->chainWrite(sizeof(header_out
), data
, size
);
187 queue
.produceDescriptor(main_desc
, sizeof(P9MsgHeader
) + size
);
192 VirtIO9PBase::dumpMsg(const P9MsgHeader
&header
, const uint8_t *data
, size_t size
)
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
);
204 DPRINTF(VIO9P
, "P9Msg[len = %i, type = Unknown (%i), tag = %i]\n",
205 header
.len
, header
.type
, header
.tag
);
207 DDUMP(VIO9PData
, data
, size
);
212 VirtIO9PProxy::VirtIO9PProxy(Params
*params
)
213 : VirtIO9PBase(params
), deviceUsed(false)
217 VirtIO9PProxy::~VirtIO9PProxy()
223 VirtIO9PProxy::serialize(CheckpointOut
&cp
) const
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 "
230 SERIALIZE_SCALAR(deviceUsed
);
232 VirtIO9PBase::serialize(cp
);
236 VirtIO9PProxy::unserialize(CheckpointIn
&cp
)
238 UNSERIALIZE_SCALAR(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 "
245 VirtIO9PBase::unserialize(cp
);
250 VirtIO9PProxy::recvTMsg(const P9MsgHeader
&header
,
251 const uint8_t *data
, size_t size
)
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
);
265 VirtIO9PProxy::serverDataReady()
268 readAll((uint8_t *)&header
, sizeof(header
));
269 header
= p9toh(header
);
271 const ssize_t
payload_len(header
.len
- sizeof(header
));
273 panic("Payload length is negative!\n");
274 uint8_t data
[payload_len
];
275 readAll(data
, payload_len
);
277 sendRMsg(header
, data
, payload_len
);
282 VirtIO9PProxy::readAll(uint8_t *data
, size_t len
)
286 while ((ret
= read(data
, len
)) == -EAGAIN
)
289 panic("readAll: Read failed: %i\n", -ret
);
297 VirtIO9PProxy::writeAll(const uint8_t *data
, size_t len
)
301 while ((ret
= write(data
, len
)) == -EAGAIN
)
304 panic("writeAll: write failed: %i\n", -ret
);
313 VirtIO9PDiod::VirtIO9PDiod(Params
*params
)
314 : VirtIO9PProxy(params
),
315 fd_to_diod(-1), fd_from_diod(-1), diod_pid(-1)
317 // Register an exit callback so we can kill the diod process
318 registerExitCallback([this]() { terminateDiod(); });
321 VirtIO9PDiod::~VirtIO9PDiod()
326 VirtIO9PDiod::startup()
329 dataEvent
.reset(new DiodDataEvent(*this, fd_from_diod
, POLLIN
));
330 pollQueue
.schedule(dataEvent
.get());
334 VirtIO9PDiod::startDiod()
336 const Params
*p(dynamic_cast<const Params
*>(params()));
339 const int DIOD_RFD
= 3;
340 const int DIOD_WFD
= 4;
342 const char *diod(p
->diod
.c_str());
344 DPRINTF(VIO9P
, "Using diod at %s \n", p
->diod
.c_str());
346 if (pipe(pipe_rfd
) == -1 || pipe(pipe_wfd
) == -1)
347 panic("Failed to create DIOD pipes: %i\n", errno
);
349 fd_to_diod
= pipe_rfd
[1];
350 fd_from_diod
= pipe_wfd
[0];
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
);
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
;
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");
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
);
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
384 if (dup2(pipe_rfd
[0], DIOD_RFD
) == -1 ||
385 dup2(pipe_wfd
[1], DIOD_WFD
) == -1) {
387 panic("Failed to setup read/write pipes: %i\n",
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
402 perror("Starting DIOD");
403 panic("Failed to execute diod to %s: %i\n",socket_path
, errno
);
407 inform("Started diod with PID %u, you might need to manually kill " \
408 " diod if gem5 crashes \n", diod_pid
);
416 VirtIO9PDiod::read(uint8_t *data
, size_t len
)
418 assert(fd_from_diod
!= -1);
419 const int ret(::read(fd_from_diod
, (void *)data
, len
));
420 return ret
< 0 ? -errno
: ret
;
424 VirtIO9PDiod::write(const uint8_t *data
, size_t len
)
426 assert(fd_to_diod
!= -1);
427 const int ret(::write(fd_to_diod
, (const void *)data
, len
));
428 return ret
< 0 ? -errno
: ret
;
432 VirtIO9PDiod::DiodDataEvent::process(int revent
)
434 parent
.serverDataReady();
438 VirtIO9PDiod::terminateDiod()
440 assert(diod_pid
!= -1);
442 DPRINTF(VIO9P
, "Trying to kill diod at pid %u \n", diod_pid
);
444 if (kill(diod_pid
, SIGTERM
) != 0) {
445 perror("Killing diod process");
446 warn("Failed to kill diod using SIGTERM");
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
456 } else if (wait_return
== 0) {
457 // Diod is not killed so sleep and try again
462 warn("Failed in waitpid");
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");
472 // Managed to kill diod
478 VirtIO9PDiodParams::create()
480 return new VirtIO9PDiod(this);
486 VirtIO9PSocket::VirtIO9PSocket(Params
*params
)
487 : VirtIO9PProxy(params
), fdSocket(-1)
491 VirtIO9PSocket::~VirtIO9PSocket()
496 VirtIO9PSocket::startup()
499 dataEvent
.reset(new SocketDataEvent(*this, fdSocket
, POLLIN
));
500 pollQueue
.schedule(dataEvent
.get());
504 VirtIO9PSocket::connectSocket()
506 const Params
&p(dynamic_cast<const Params
&>(*params()));
509 struct addrinfo hints
, *result
;
510 memset(&hints
, 0, sizeof(hints
));
511 hints
.ai_family
= AF_UNSPEC
;
512 hints
.ai_socktype
= SOCK_STREAM
;
514 hints
.ai_protocol
= 0;
516 if ((ret
= getaddrinfo(p
.server
.c_str(), p
.port
.c_str(),
517 &hints
, &result
)) != 0)
518 panic("getaddrinfo: %s\n", gai_strerror(ret
));
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
,
524 if (fdSocket
== -1) {
526 } else if (connect(fdSocket
, rp
->ai_addr
, rp
->ai_addrlen
) != -1) {
534 freeaddrinfo(result
);
537 panic("Failed to connect to 9p server (%s:%s)", p
.server
, p
.port
);
541 VirtIO9PSocket::socketDisconnect()
543 panic("9P Socket disconnected!\n");
547 VirtIO9PSocket::read(uint8_t *data
, size_t len
)
549 assert(fdSocket
!= -1);
552 ret
= ::recv(fdSocket
, (void *)data
, len
, 0);
556 return ret
< 0 ? -errno
: ret
;
560 VirtIO9PSocket::write(const uint8_t *data
, size_t len
)
562 assert(fdSocket
!= -1);
563 int ret(::send(fdSocket
, (const void *)data
, len
, 0));
564 return ret
< 0 ? -errno
: ret
;
568 VirtIO9PSocket::SocketDataEvent::process(int revent
)
570 parent
.serverDataReady();
575 VirtIO9PSocketParams::create()
577 return new VirtIO9PSocket(this);