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