1/* 2 * Copyright (c) 2016 Advanced Micro Devices, Inc. 3 * All rights reserved. 4 * 5 * For use for simulation and test purposes only 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright notice, 14 * this list of conditions and the following disclaimer in the documentation 15 * and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the copyright holder nor the names of its 18 * contributors may be used to endorse or promote products derived from this 19 * software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 * 33 * Authors: Brandon Potter 34 */ 35 36#include "sim/fd_array.hh" 37 38#include <fcntl.h> 39#include <unistd.h> 40 41#include <array> 42#include <memory> 43#include <string> 44 45#include "base/logging.hh" 46#include "base/output.hh" 47#include "params/Process.hh" 48#include "sim/fd_entry.hh" 49 50FDArray::FDArray(std::string const& input, std::string const& output, 51 std::string const& errout) 52 : _fdArray(), _input(input), _output(output), _errout(errout), 53 _imap {{"", -1}, 54 {"cin", STDIN_FILENO}, 55 {"stdin", STDIN_FILENO}}, 56 _oemap{{"", -1}, 57 {"cout", STDOUT_FILENO}, 58 {"stdout", STDOUT_FILENO}, 59 {"cerr", STDERR_FILENO}, 60 {"stderr", STDERR_FILENO}} 61{ 62 int sim_fd; 63 std::map<std::string, int>::iterator it; 64 65 /** 66 * Search through the input options and setup the default fd if match is 67 * found; otherwise, open an input file and seek to location. 68 */ 69 if ((it = _imap.find(input)) != _imap.end()) 70 sim_fd = it->second; 71 else 72 sim_fd = openInputFile(input); 73 74 auto ffd = std::make_shared<FileFDEntry>(sim_fd, O_RDONLY, input, false); 75 _fdArray[STDIN_FILENO] = ffd; 76 77 /** 78 * Search through the output/error options and setup the default fd if 79 * match is found; otherwise, open an output file and seek to location. 80 */ 81 if ((it = _oemap.find(output)) != _oemap.end()) 82 sim_fd = it->second; 83 else 84 sim_fd = openOutputFile(output); 85 86 ffd = std::make_shared<FileFDEntry>(sim_fd, O_WRONLY | O_CREAT | O_TRUNC, 87 output, false); 88 _fdArray[STDOUT_FILENO] = ffd; 89 90 if (output == errout) 91 ; /* Reuse the same file descriptor if these match. */ 92 else if ((it = _oemap.find(errout)) != _oemap.end()) 93 sim_fd = it->second; 94 else 95 sim_fd = openOutputFile(errout); 96 97 ffd = std::make_shared<FileFDEntry>(sim_fd, O_WRONLY | O_CREAT | O_TRUNC, 98 errout, false); 99 _fdArray[STDERR_FILENO] = ffd; 100} 101 102void 103FDArray::updateFileOffsets() 104{ 105 for (auto& fdp : _fdArray) { 106 /** 107 * It only makes sense to check the offsets if the file descriptor 108 * type is 'File' (which indicates that this file is backed by a 109 * file on the host). If the type is File, then record the offset. 110 */ 111 auto ffd = std::dynamic_pointer_cast<FileFDEntry>(fdp); 112 113 if (!ffd) 114 continue; 115 116 /** 117 * Use lseek with SEEK_CUR with offset 0 to figure out where the 118 * offset currently resides and pass that back to our setter. 119 */ 120 int sim_fd = ffd->getSimFD(); 121 ffd->setFileOffset(lseek(sim_fd, 0, SEEK_CUR)); 122 } 123} 124 125void 126FDArray::restoreFileOffsets() 127{ 128 /** 129 * Use this lambda to highlight what we mean to do with the seek. 130 * Notice that this either seeks correctly (sets the file location on the 131 * host) or it fails with a fatal. The error is fatal because it's not 132 * possible to guarantee that the simulation will proceed as it should 133 * have in the same way that it would have proceeded sans checkpoints. 134 */ 135 auto seek = [] (std::shared_ptr<FileFDEntry> ffd) 136 { 137 if (lseek(ffd->getSimFD(), ffd->getFileOffset(), SEEK_SET) < 0) 138 fatal("Unable to seek to location in %s", ffd->getFileName()); 139 }; 140 141 std::map<std::string, int>::iterator it; 142 143 /** 144 * Search through the input options and set fd if match is found; 145 * otherwise, open an input file and seek to location. 146 * Check if user has specified a different input file, and if so, use it 147 * instead of the file specified in the checkpoint. This also resets the 148 * file offset from the checkpointed value 149 */ 150 std::shared_ptr<FDEntry> stdin_fde = _fdArray[STDIN_FILENO]; 151 auto stdin_ffd = std::dynamic_pointer_cast<FileFDEntry>(stdin_fde); 152 153 if (_input != stdin_ffd->getFileName()) { 154 warn("Using new input file (%s) rather than checkpointed (%s)\n", 155 _input, stdin_ffd->getFileName()); 156 stdin_ffd->setFileName(_input); 157 stdin_ffd->setFileOffset(0); 158 } 159 160 if ((it = _imap.find(stdin_ffd->getFileName())) != _imap.end()) { 161 stdin_ffd->setSimFD(it->second); 162 } else { 163 stdin_ffd->setSimFD(openInputFile(stdin_ffd->getFileName())); 164 seek(stdin_ffd); 165 } 166 167 /** 168 * Search through the output options and set fd if match is found; 169 * otherwise, open an output file and seek to location. 170 * Check if user has specified a different output file, and if so, use it 171 * instead of the file specified in the checkpoint. This also resets the 172 * file offset from the checkpointed value 173 */ 174 std::shared_ptr<FDEntry> stdout_fde = _fdArray[STDOUT_FILENO]; 175 auto stdout_ffd = std::dynamic_pointer_cast<FileFDEntry>(stdout_fde); 176 177 if (_output != stdout_ffd->getFileName()) { 178 warn("Using new output file (%s) rather than checkpointed (%s)\n", 179 _output, stdout_ffd->getFileName()); 180 stdout_ffd->setFileName(_output); 181 stdout_ffd->setFileOffset(0); 182 } 183 184 if ((it = _oemap.find(stdout_ffd->getFileName())) != _oemap.end()) { 185 stdout_ffd->setSimFD(it->second); 186 } else { 187 stdout_ffd->setSimFD(openOutputFile(stdout_ffd->getFileName())); 188 seek(stdout_ffd); 189 } 190 191 /** 192 * Search through the error options and set fd if match is found; 193 * otherwise, open an error file and seek to location. 194 * Check if user has specified a different error file, and if so, use it 195 * instead of the file specified in the checkpoint. This also resets the 196 * file offset from the checkpointed value 197 */ 198 std::shared_ptr<FDEntry> stderr_fde = _fdArray[STDERR_FILENO]; 199 auto stderr_ffd = std::dynamic_pointer_cast<FileFDEntry>(stderr_fde); 200 201 if (_errout != stderr_ffd->getFileName()) { 202 warn("Using new error file (%s) rather than checkpointed (%s)\n", 203 _errout, stderr_ffd->getFileName()); 204 stderr_ffd->setFileName(_errout); 205 stderr_ffd->setFileOffset(0); 206 } 207 208 if (stdout_ffd->getFileName() == stderr_ffd->getFileName()) { 209 /* Reuse the same sim_fd file descriptor if these match. */ 210 stderr_ffd->setSimFD(stdout_ffd->getSimFD()); 211 } else if ((it = _oemap.find(stderr_ffd->getFileName())) != _oemap.end()) { 212 stderr_ffd->setSimFD(it->second); 213 } else { 214 stderr_ffd->setSimFD(openOutputFile(stderr_ffd->getFileName())); 215 seek(stderr_ffd); 216 } 217 218 for (int tgt_fd = 3; tgt_fd < _fdArray.size(); tgt_fd++) { 219 std::shared_ptr<FDEntry> fdp = _fdArray[tgt_fd]; 220 if (!fdp) 221 continue; 222 223 /* Need to reconnect pipe ends. */ 224 if (auto pfd = std::dynamic_pointer_cast<PipeFDEntry>(fdp)) { 225 /** 226 * Check which end of the pipe we are looking at; we only want 227 * to setup the pipe once so we arbitrarily choose the read 228 * end to be the end that sets up the pipe. 229 */ 230 if (pfd->getEndType() == PipeFDEntry::EndType::write) 231 continue; 232 233 /* Setup the pipe or fatal out of the simulation. */ 234 int fd_pair[2]; 235 if (pipe(fd_pair) < 0) 236 fatal("Unable to create new pipe"); 237 238 /** 239 * Reconstruct the ends of the pipe by reassigning the pipe 240 * that we created on the host. This one is the read end. 241 */ 242 pfd->setSimFD(fd_pair[0]); 243 244 /** 245 * Grab the write end by referencing the read ends source and 246 * using that tgt_fd to index the array. 247 */ 248 int prs = pfd->getPipeReadSource(); 249 std::shared_ptr<FDEntry> write_fdp = _fdArray[prs]; 250 251 /* Now cast it and make sure that we are still sane. */ 252 auto write_pfd = std::dynamic_pointer_cast<PipeFDEntry>(write_fdp); 253 254 /* Hook up the write end back to the right side of the pipe. */ 255 write_pfd->setSimFD(fd_pair[1]); 256 } 257 258 /* Need to reassign 'driver'. */ 259 if (auto dfd = std::dynamic_pointer_cast<DeviceFDEntry>(fdp)) { 260 /** 261 * Yeah, how does one retain the entire driver state from this 262 * particular set of code? If you figure it out, add some code 263 * here to rectify the issue. 264 */ 265 fatal("Unable to restore checkpoints with emulated drivers"); 266 } 267 268 /* Need to open files and seek. */ 269 if (auto ffd = std::dynamic_pointer_cast<FileFDEntry>(fdp)) { 270 /** 271 * Assume that this has the mode of an output file so there's no 272 * need to worry about properly recording the mode. If you're 273 * reading this and this happens to be your issue, at least be 274 * happy that you've discovered the issue (and not mad at me). 275 * Onward ho! 276 */ 277 int sim_fd = openFile(ffd->getFileName(), ffd->getFlags(), 0664); 278 ffd->setSimFD(sim_fd); 279 seek(ffd); 280 } 281 } 282} 283 284int 285FDArray::allocFD(std::shared_ptr<FDEntry> in) 286{ 287 for (int i = 0; i < _fdArray.size(); i++) { 288 std::shared_ptr<FDEntry> fdp = _fdArray[i]; 289 if (!fdp) { 290 _fdArray[i] = in; 291 return i; 292 } 293 } 294 fatal("Out of target file descriptors"); 295} 296 297int 298FDArray::openFile(std::string const& filename, int flags, mode_t mode) const 299{ 300 int sim_fd = open(filename.c_str(), flags, mode); 301 if (sim_fd != -1) 302 return sim_fd; 303 fatal("Unable to open %s with mode %O", filename, mode); 304} 305 306int 307FDArray::openInputFile(std::string const& filename) const 308{ 309 return openFile(filename, O_RDONLY, 00); 310} 311 312int 313FDArray::openOutputFile(std::string const& filename) const 314{ 315 return openFile(simout.resolve(filename), 316 O_WRONLY | O_CREAT | O_TRUNC, 0664); 317} 318 319std::shared_ptr<FDEntry> 320FDArray::getFDEntry(int tgt_fd) 321{ 322 assert(0 <= tgt_fd && tgt_fd < _fdArray.size()); 323 return _fdArray[tgt_fd]; 324} 325 326void 327FDArray::setFDEntry(int tgt_fd, std::shared_ptr<FDEntry> fdep) 328{ 329 assert(0 <= tgt_fd && tgt_fd < _fdArray.size()); 330 _fdArray[tgt_fd] = fdep; 331} 332 333int 334FDArray::closeFDEntry(int tgt_fd) 335{ 336 if (tgt_fd >= _fdArray.size() || tgt_fd < 0) 337 return -EBADF; 338 339 int sim_fd = -1; 340 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>(_fdArray[tgt_fd]); 341 if (hbfdp) 342 sim_fd = hbfdp->getSimFD(); 343 344 int status = 0; 345 if (sim_fd > 2) 346 status = close(sim_fd); 347 348 if (status == 0) 349 _fdArray[tgt_fd] = nullptr; 350 351 return status; 352} 353