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