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
64struct P9MsgInfo {
65    P9MsgInfo(P9MsgType _type, std::string _name)
66        : type(_type), name(_name) {}
67
68    P9MsgType type;
69    std::string name;
70};
71
72typedef 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
78static 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
117VirtIO9PBase::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
132VirtIO9PBase::~VirtIO9PBase()
133{
134}
135
136void
137VirtIO9PBase::readConfig(PacketPtr pkt, Addr cfgOffset)
138{
139    readConfigBlob(pkt, cfgOffset, (uint8_t *)config.get());
140}
141
142void
143VirtIO9PBase::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
165void
166VirtIO9PBase::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
193void
194VirtIO9PBase::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
214VirtIO9PProxy::VirtIO9PProxy(Params *params)
215  : VirtIO9PBase(params), deviceUsed(false)
216{
217}
218
219VirtIO9PProxy::~VirtIO9PProxy()
220{
221}
222
223
224void
225VirtIO9PProxy::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
237void
238VirtIO9PProxy::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
251void
252VirtIO9PProxy::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
266void
267VirtIO9PProxy::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
283void
284VirtIO9PProxy::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
298void
299VirtIO9PProxy::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
315VirtIO9PDiod::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
325VirtIO9PDiod::~VirtIO9PDiod()
326{
327}
328
329void
330VirtIO9PDiod::startup()
331{
332    startDiod();
333    dataEvent.reset(new DiodDataEvent(*this, fd_from_diod, POLLIN));
334    pollQueue.schedule(dataEvent.get());
335}
336
337void
338VirtIO9PDiod::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
418ssize_t
419VirtIO9PDiod::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
426ssize_t
427VirtIO9PDiod::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
434void
435VirtIO9PDiod::DiodDataEvent::process(int revent)
436{
437    parent.serverDataReady();
438}
439
440void
441VirtIO9PDiod::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}
480VirtIO9PDiod *
481VirtIO9PDiodParams::create()
482{
483    return new VirtIO9PDiod(this);
484}
485
486
487
488
489VirtIO9PSocket::VirtIO9PSocket(Params *params)
490    : VirtIO9PProxy(params), fdSocket(-1)
491{
492}
493
494VirtIO9PSocket::~VirtIO9PSocket()
495{
496}
497
498void
499VirtIO9PSocket::startup()
500{
501    connectSocket();
502    dataEvent.reset(new SocketDataEvent(*this, fdSocket, POLLIN));
503    pollQueue.schedule(dataEvent.get());
504}
505
506void
507VirtIO9PSocket::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
543void
544VirtIO9PSocket::socketDisconnect()
545{
546    panic("9P Socket disconnected!\n");
547}
548
549ssize_t
550VirtIO9PSocket::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
562ssize_t
563VirtIO9PSocket::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
570void
571VirtIO9PSocket::SocketDataEvent::process(int revent)
572{
573    parent.serverDataReady();
574}
575
576
577VirtIO9PSocket *
578VirtIO9PSocketParams::create()
579{
580    return new VirtIO9PSocket(this);
581}
582