fs9p.cc revision 12076:d6fa15da87cd
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 <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);
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}
533