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