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