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.
37 * Authors: Andreas Sandberg
40 #include "dev/virtio/fs9p.hh"
44 #include <netinet/in.h>
45 #include <sys/socket.h>
46 #include <sys/types.h>
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"
65 P9MsgInfo(P9MsgType _type
, std::string _name
)
66 : type(_type
), name(_name
) {}
72 typedef std::map
<P9MsgType
, P9MsgInfo
> P9MsgInfoMap
;
74 #define P9MSG(type, name) \
75 { (type), P9MsgInfo((type), "T" # name ) }, \
76 { (type + 1), P9MsgInfo((type + 1), "R" # name ) }
78 static const P9MsgInfoMap p9_msg_info
{
90 P9MSG(32, XATTRCREATE
),
117 VirtIO9PBase::VirtIO9PBase(Params
*params
)
118 : VirtIODeviceBase(params
, ID_9P
,
119 sizeof(Config
) + params
->tag
.size(),
121 queue(params
->system
->physProxy
, params
->queueSize
, *this)
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());
128 registerQueue(queue
);
132 VirtIO9PBase::~VirtIO9PBase()
137 VirtIO9PBase::readConfig(PacketPtr pkt
, Addr cfgOffset
)
139 readConfigBlob(pkt
, cfgOffset
, (uint8_t *)config
.get());
143 VirtIO9PBase::FSQueue::onNotifyDescriptor(VirtDescriptor
*desc
)
145 DPRINTF(VIO9P
, "Got input data descriptor (len: %i)\n", desc
->size());
146 DPRINTF(VIO9P
, "\tPending transactions: %i\n", parent
.pendingTransactions
.size());
149 desc
->chainRead(0, (uint8_t *)&header
, sizeof(header
));
150 header
= p9toh(header
);
152 uint8_t data
[header
.len
- sizeof(header
)];
153 desc
->chainRead(sizeof(header
), data
, sizeof(data
));
155 // Keep track of pending transactions
156 parent
.pendingTransactions
[header
.tag
] = desc
;
158 DPRINTF(VIO9P
, "recvTMsg\n");
159 parent
.dumpMsg(header
, data
, sizeof(data
));
161 // Notify device of message
162 parent
.recvTMsg(header
, data
, sizeof(data
));
166 VirtIO9PBase::sendRMsg(const P9MsgHeader
&header
, const uint8_t *data
, size_t size
)
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
));
173 VirtDescriptor
*main_desc(pendingTransactions
[header
.tag
]);
174 pendingTransactions
.erase(header
.tag
);
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();
181 panic("sendRMsg: Framing error, no output descriptor.\n");
183 P9MsgHeader
header_out(htop9(header
));
184 header_out
.len
= htop9(sizeof(P9MsgHeader
) + size
);
186 out_desc
->chainWrite(0, (uint8_t *)&header_out
, sizeof(header_out
));
187 out_desc
->chainWrite(sizeof(header_out
), data
, size
);
189 queue
.produceDescriptor(main_desc
, sizeof(P9MsgHeader
) + size
);
194 VirtIO9PBase::dumpMsg(const P9MsgHeader
&header
, const uint8_t *data
, size_t size
)
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
);
206 DPRINTF(VIO9P
, "P9Msg[len = %i, type = Unknown (%i), tag = %i]\n",
207 header
.len
, header
.type
, header
.tag
);
209 DDUMP(VIO9PData
, data
, size
);
214 VirtIO9PProxy::VirtIO9PProxy(Params
*params
)
215 : VirtIO9PBase(params
), deviceUsed(false)
219 VirtIO9PProxy::~VirtIO9PProxy()
225 VirtIO9PProxy::serialize(CheckpointOut
&cp
) const
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 "
232 SERIALIZE_SCALAR(deviceUsed
);
234 VirtIO9PBase::serialize(cp
);
238 VirtIO9PProxy::unserialize(CheckpointIn
&cp
)
240 UNSERIALIZE_SCALAR(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 "
247 VirtIO9PBase::unserialize(cp
);
252 VirtIO9PProxy::recvTMsg(const P9MsgHeader
&header
,
253 const uint8_t *data
, size_t size
)
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
);
267 VirtIO9PProxy::serverDataReady()
270 readAll((uint8_t *)&header
, sizeof(header
));
271 header
= p9toh(header
);
273 const ssize_t
payload_len(header
.len
- sizeof(header
));
275 panic("Payload length is negative!\n");
276 uint8_t data
[payload_len
];
277 readAll(data
, payload_len
);
279 sendRMsg(header
, data
, payload_len
);
284 VirtIO9PProxy::readAll(uint8_t *data
, size_t len
)
288 while ((ret
= read(data
, len
)) == -EAGAIN
)
291 panic("readAll: Read failed: %i\n", -ret
);
299 VirtIO9PProxy::writeAll(const uint8_t *data
, size_t len
)
303 while ((ret
= write(data
, len
)) == -EAGAIN
)
306 panic("writeAll: write failed: %i\n", -ret
);
315 VirtIO9PDiod::VirtIO9PDiod(Params
*params
)
316 : VirtIO9PProxy(params
),
317 fd_to_diod(-1), fd_from_diod(-1), diod_pid(-1)
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
);
325 VirtIO9PDiod::~VirtIO9PDiod()
330 VirtIO9PDiod::startup()
333 dataEvent
.reset(new DiodDataEvent(*this, fd_from_diod
, POLLIN
));
334 pollQueue
.schedule(dataEvent
.get());
338 VirtIO9PDiod::startDiod()
340 const Params
*p(dynamic_cast<const Params
*>(params()));
343 const int DIOD_RFD
= 3;
344 const int DIOD_WFD
= 4;
346 const char *diod(p
->diod
.c_str());
348 DPRINTF(VIO9P
, "Using diod at %s \n", p
->diod
.c_str());
350 if (pipe(pipe_rfd
) == -1 || pipe(pipe_wfd
) == -1)
351 panic("Failed to create DIOD pipes: %i\n", errno
);
353 fd_to_diod
= pipe_rfd
[1];
354 fd_from_diod
= pipe_wfd
[0];
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
);
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
;
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");
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
);
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
388 if (dup2(pipe_rfd
[0], DIOD_RFD
) == -1 ||
389 dup2(pipe_wfd
[1], DIOD_WFD
) == -1) {
391 panic("Failed to setup read/write pipes: %i\n",
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
405 perror("Starting DIOD");
406 panic("Failed to execute diod to %s: %i\n",socket_path
, errno
);
410 inform("Started diod with PID %u, you might need to manually kill " \
411 " diod if gem5 crashes \n", diod_pid
);
419 VirtIO9PDiod::read(uint8_t *data
, size_t len
)
421 assert(fd_from_diod
!= -1);
422 const int ret(::read(fd_from_diod
, (void *)data
, len
));
423 return ret
< 0 ? -errno
: ret
;
427 VirtIO9PDiod::write(const uint8_t *data
, size_t len
)
429 assert(fd_to_diod
!= -1);
430 const int ret(::write(fd_to_diod
, (const void *)data
, len
));
431 return ret
< 0 ? -errno
: ret
;
435 VirtIO9PDiod::DiodDataEvent::process(int revent
)
437 parent
.serverDataReady();
441 VirtIO9PDiod::terminateDiod()
443 assert(diod_pid
!= -1);
445 DPRINTF(VIO9P
, "Trying to kill diod at pid %u \n", diod_pid
);
447 if (kill(diod_pid
, SIGTERM
) != 0) {
448 perror("Killing diod process");
449 warn("Failed to kill diod using SIGTERM");
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
459 } else if (wait_return
== 0) {
460 // Diod is not killed so sleep and try again
465 warn("Failed in waitpid");
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");
475 // Managed to kill diod
481 VirtIO9PDiodParams::create()
483 return new VirtIO9PDiod(this);
489 VirtIO9PSocket::VirtIO9PSocket(Params
*params
)
490 : VirtIO9PProxy(params
), fdSocket(-1)
494 VirtIO9PSocket::~VirtIO9PSocket()
499 VirtIO9PSocket::startup()
502 dataEvent
.reset(new SocketDataEvent(*this, fdSocket
, POLLIN
));
503 pollQueue
.schedule(dataEvent
.get());
507 VirtIO9PSocket::connectSocket()
509 const Params
&p(dynamic_cast<const Params
&>(*params()));
512 struct addrinfo hints
, *result
;
513 memset(&hints
, 0, sizeof(hints
));
514 hints
.ai_family
= AF_UNSPEC
;
515 hints
.ai_socktype
= SOCK_STREAM
;
517 hints
.ai_protocol
= 0;
519 if ((ret
= getaddrinfo(p
.server
.c_str(), p
.port
.c_str(),
520 &hints
, &result
)) != 0)
521 panic("getaddrinfo: %s\n", gai_strerror(ret
));
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
,
527 if (fdSocket
== -1) {
529 } else if (connect(fdSocket
, rp
->ai_addr
, rp
->ai_addrlen
) != -1) {
537 freeaddrinfo(result
);
540 panic("Failed to connect to 9p server (%s:%s)", p
.server
, p
.port
);
544 VirtIO9PSocket::socketDisconnect()
546 panic("9P Socket disconnected!\n");
550 VirtIO9PSocket::read(uint8_t *data
, size_t len
)
552 assert(fdSocket
!= -1);
555 ret
= ::recv(fdSocket
, (void *)data
, len
, 0);
559 return ret
< 0 ? -errno
: ret
;
563 VirtIO9PSocket::write(const uint8_t *data
, size_t len
)
565 assert(fdSocket
!= -1);
566 int ret(::send(fdSocket
, (const void *)data
, len
, 0));
567 return ret
< 0 ? -errno
: ret
;
571 VirtIO9PSocket::SocketDataEvent::process(int revent
)
573 parent
.serverDataReady();
578 VirtIO9PSocketParams::create()
580 return new VirtIO9PSocket(this);