| /* | |
| * Copyright (c) 2016 Advanced Micro Devices, Inc. | |
| * All rights reserved. | |
| * | |
| * For use for simulation and test purposes only | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are met: | |
| * | |
| * 1. Redistributions of source code must retain the above copyright notice, | |
| * this list of conditions and the following disclaimer. | |
| * | |
| * 2. Redistributions in binary form must reproduce the above copyright notice, | |
| * this list of conditions and the following disclaimer in the documentation | |
| * and/or other materials provided with the distribution. | |
| * | |
| * 3. Neither the name of the copyright holder nor the names of its | |
| * contributors may be used to endorse or promote products derived from this | |
| * software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| * POSSIBILITY OF SUCH DAMAGE. | |
| * | |
| * Authors: Brandon Potter | |
| */ | |
| #include "sim/fd_array.hh" | |
| #include <fcntl.h> | |
| #include <unistd.h> | |
| #include <array> | |
| #include <memory> | |
| #include <string> | |
| #include "base/logging.hh" | |
| #include "base/output.hh" | |
| #include "params/Process.hh" | |
| #include "sim/fd_entry.hh" | |
| FDArray::FDArray(std::string const& input, std::string const& output, | |
| std::string const& errout) | |
| : _fdArray(), _input(input), _output(output), _errout(errout), | |
| _imap {{"", -1}, | |
| {"cin", STDIN_FILENO}, | |
| {"stdin", STDIN_FILENO}}, | |
| _oemap{{"", -1}, | |
| {"cout", STDOUT_FILENO}, | |
| {"stdout", STDOUT_FILENO}, | |
| {"cerr", STDERR_FILENO}, | |
| {"stderr", STDERR_FILENO}} | |
| { | |
| int sim_fd; | |
| std::map<std::string, int>::iterator it; | |
| /** | |
| * Search through the input options and setup the default fd if match is | |
| * found; otherwise, open an input file and seek to location. | |
| */ | |
| if ((it = _imap.find(input)) != _imap.end()) | |
| sim_fd = it->second; | |
| else | |
| sim_fd = openInputFile(input); | |
| auto ffd = std::make_shared<FileFDEntry>(sim_fd, O_RDONLY, input, false); | |
| _fdArray[STDIN_FILENO] = ffd; | |
| /** | |
| * Search through the output/error options and setup the default fd if | |
| * match is found; otherwise, open an output file and seek to location. | |
| */ | |
| if ((it = _oemap.find(output)) != _oemap.end()) | |
| sim_fd = it->second; | |
| else | |
| sim_fd = openOutputFile(output); | |
| ffd = std::make_shared<FileFDEntry>(sim_fd, O_WRONLY | O_CREAT | O_TRUNC, | |
| output, false); | |
| _fdArray[STDOUT_FILENO] = ffd; | |
| if (output == errout) | |
| ; /* Reuse the same file descriptor if these match. */ | |
| else if ((it = _oemap.find(errout)) != _oemap.end()) | |
| sim_fd = it->second; | |
| else | |
| sim_fd = openOutputFile(errout); | |
| ffd = std::make_shared<FileFDEntry>(sim_fd, O_WRONLY | O_CREAT | O_TRUNC, | |
| errout, false); | |
| _fdArray[STDERR_FILENO] = ffd; | |
| } | |
| void | |
| FDArray::updateFileOffsets() | |
| { | |
| for (auto& fdp : _fdArray) { | |
| /** | |
| * It only makes sense to check the offsets if the file descriptor | |
| * type is 'File' (which indicates that this file is backed by a | |
| * file on the host). If the type is File, then record the offset. | |
| */ | |
| auto ffd = std::dynamic_pointer_cast<FileFDEntry>(fdp); | |
| if (!ffd) | |
| continue; | |
| /** | |
| * Use lseek with SEEK_CUR with offset 0 to figure out where the | |
| * offset currently resides and pass that back to our setter. | |
| */ | |
| int sim_fd = ffd->getSimFD(); | |
| ffd->setFileOffset(lseek(sim_fd, 0, SEEK_CUR)); | |
| } | |
| } | |
| void | |
| FDArray::restoreFileOffsets() | |
| { | |
| /** | |
| * Use this lambda to highlight what we mean to do with the seek. | |
| * Notice that this either seeks correctly (sets the file location on the | |
| * host) or it fails with a fatal. The error is fatal because it's not | |
| * possible to guarantee that the simulation will proceed as it should | |
| * have in the same way that it would have proceeded sans checkpoints. | |
| */ | |
| auto seek = [] (std::shared_ptr<FileFDEntry> ffd) | |
| { | |
| if (lseek(ffd->getSimFD(), ffd->getFileOffset(), SEEK_SET) < 0) | |
| fatal("Unable to seek to location in %s", ffd->getFileName()); | |
| }; | |
| std::map<std::string, int>::iterator it; | |
| /** | |
| * Search through the input options and set fd if match is found; | |
| * otherwise, open an input file and seek to location. | |
| * Check if user has specified a different input file, and if so, use it | |
| * instead of the file specified in the checkpoint. This also resets the | |
| * file offset from the checkpointed value | |
| */ | |
| std::shared_ptr<FDEntry> stdin_fde = _fdArray[STDIN_FILENO]; | |
| auto stdin_ffd = std::dynamic_pointer_cast<FileFDEntry>(stdin_fde); | |
| if (_input != stdin_ffd->getFileName()) { | |
| warn("Using new input file (%s) rather than checkpointed (%s)\n", | |
| _input, stdin_ffd->getFileName()); | |
| stdin_ffd->setFileName(_input); | |
| stdin_ffd->setFileOffset(0); | |
| } | |
| if ((it = _imap.find(stdin_ffd->getFileName())) != _imap.end()) { | |
| stdin_ffd->setSimFD(it->second); | |
| } else { | |
| stdin_ffd->setSimFD(openInputFile(stdin_ffd->getFileName())); | |
| seek(stdin_ffd); | |
| } | |
| /** | |
| * Search through the output options and set fd if match is found; | |
| * otherwise, open an output file and seek to location. | |
| * Check if user has specified a different output file, and if so, use it | |
| * instead of the file specified in the checkpoint. This also resets the | |
| * file offset from the checkpointed value | |
| */ | |
| std::shared_ptr<FDEntry> stdout_fde = _fdArray[STDOUT_FILENO]; | |
| auto stdout_ffd = std::dynamic_pointer_cast<FileFDEntry>(stdout_fde); | |
| if (_output != stdout_ffd->getFileName()) { | |
| warn("Using new output file (%s) rather than checkpointed (%s)\n", | |
| _output, stdout_ffd->getFileName()); | |
| stdout_ffd->setFileName(_output); | |
| stdout_ffd->setFileOffset(0); | |
| } | |
| if ((it = _oemap.find(stdout_ffd->getFileName())) != _oemap.end()) { | |
| stdout_ffd->setSimFD(it->second); | |
| } else { | |
| stdout_ffd->setSimFD(openOutputFile(stdout_ffd->getFileName())); | |
| seek(stdout_ffd); | |
| } | |
| /** | |
| * Search through the error options and set fd if match is found; | |
| * otherwise, open an error file and seek to location. | |
| * Check if user has specified a different error file, and if so, use it | |
| * instead of the file specified in the checkpoint. This also resets the | |
| * file offset from the checkpointed value | |
| */ | |
| std::shared_ptr<FDEntry> stderr_fde = _fdArray[STDERR_FILENO]; | |
| auto stderr_ffd = std::dynamic_pointer_cast<FileFDEntry>(stderr_fde); | |
| if (_errout != stderr_ffd->getFileName()) { | |
| warn("Using new error file (%s) rather than checkpointed (%s)\n", | |
| _errout, stderr_ffd->getFileName()); | |
| stderr_ffd->setFileName(_errout); | |
| stderr_ffd->setFileOffset(0); | |
| } | |
| if (stdout_ffd->getFileName() == stderr_ffd->getFileName()) { | |
| /* Reuse the same sim_fd file descriptor if these match. */ | |
| stderr_ffd->setSimFD(stdout_ffd->getSimFD()); | |
| } else if ((it = _oemap.find(stderr_ffd->getFileName())) != _oemap.end()) { | |
| stderr_ffd->setSimFD(it->second); | |
| } else { | |
| stderr_ffd->setSimFD(openOutputFile(stderr_ffd->getFileName())); | |
| seek(stderr_ffd); | |
| } | |
| for (int tgt_fd = 3; tgt_fd < _fdArray.size(); tgt_fd++) { | |
| std::shared_ptr<FDEntry> fdp = _fdArray[tgt_fd]; | |
| if (!fdp) | |
| continue; | |
| /* Need to reconnect pipe ends. */ | |
| if (auto pfd = std::dynamic_pointer_cast<PipeFDEntry>(fdp)) { | |
| /** | |
| * Check which end of the pipe we are looking at; we only want | |
| * to setup the pipe once so we arbitrarily choose the read | |
| * end to be the end that sets up the pipe. | |
| */ | |
| if (pfd->getEndType() == PipeFDEntry::EndType::write) | |
| continue; | |
| /* Setup the pipe or fatal out of the simulation. */ | |
| int fd_pair[2]; | |
| if (pipe(fd_pair) < 0) | |
| fatal("Unable to create new pipe"); | |
| /** | |
| * Reconstruct the ends of the pipe by reassigning the pipe | |
| * that we created on the host. This one is the read end. | |
| */ | |
| pfd->setSimFD(fd_pair[0]); | |
| /** | |
| * Grab the write end by referencing the read ends source and | |
| * using that tgt_fd to index the array. | |
| */ | |
| int prs = pfd->getPipeReadSource(); | |
| std::shared_ptr<FDEntry> write_fdp = _fdArray[prs]; | |
| /* Now cast it and make sure that we are still sane. */ | |
| auto write_pfd = std::dynamic_pointer_cast<PipeFDEntry>(write_fdp); | |
| /* Hook up the write end back to the right side of the pipe. */ | |
| write_pfd->setSimFD(fd_pair[1]); | |
| } | |
| /* Need to reassign 'driver'. */ | |
| if (auto dfd = std::dynamic_pointer_cast<DeviceFDEntry>(fdp)) { | |
| /** | |
| * Yeah, how does one retain the entire driver state from this | |
| * particular set of code? If you figure it out, add some code | |
| * here to rectify the issue. | |
| */ | |
| fatal("Unable to restore checkpoints with emulated drivers"); | |
| } | |
| /* Need to open files and seek. */ | |
| if (auto ffd = std::dynamic_pointer_cast<FileFDEntry>(fdp)) { | |
| /** | |
| * Assume that this has the mode of an output file so there's no | |
| * need to worry about properly recording the mode. If you're | |
| * reading this and this happens to be your issue, at least be | |
| * happy that you've discovered the issue (and not mad at me). | |
| * Onward ho! | |
| */ | |
| int sim_fd = openFile(ffd->getFileName(), ffd->getFlags(), 0664); | |
| ffd->setSimFD(sim_fd); | |
| seek(ffd); | |
| } | |
| } | |
| } | |
| int | |
| FDArray::allocFD(std::shared_ptr<FDEntry> in) | |
| { | |
| for (int i = 0; i < _fdArray.size(); i++) { | |
| std::shared_ptr<FDEntry> fdp = _fdArray[i]; | |
| if (!fdp) { | |
| _fdArray[i] = in; | |
| return i; | |
| } | |
| } | |
| fatal("Out of target file descriptors"); | |
| } | |
| int | |
| FDArray::openFile(std::string const& filename, int flags, mode_t mode) const | |
| { | |
| int sim_fd = open(filename.c_str(), flags, mode); | |
| if (sim_fd != -1) | |
| return sim_fd; | |
| fatal("Unable to open %s with mode %O", filename, mode); | |
| } | |
| int | |
| FDArray::openInputFile(std::string const& filename) const | |
| { | |
| return openFile(filename, O_RDONLY, 00); | |
| } | |
| int | |
| FDArray::openOutputFile(std::string const& filename) const | |
| { | |
| return openFile(simout.resolve(filename), | |
| O_WRONLY | O_CREAT | O_TRUNC, 0664); | |
| } | |
| std::shared_ptr<FDEntry> | |
| FDArray::getFDEntry(int tgt_fd) | |
| { | |
| assert(0 <= tgt_fd && tgt_fd < _fdArray.size()); | |
| return _fdArray[tgt_fd]; | |
| } | |
| void | |
| FDArray::setFDEntry(int tgt_fd, std::shared_ptr<FDEntry> fdep) | |
| { | |
| assert(0 <= tgt_fd && tgt_fd < _fdArray.size()); | |
| _fdArray[tgt_fd] = fdep; | |
| } | |
| int | |
| FDArray::closeFDEntry(int tgt_fd) | |
| { | |
| if (tgt_fd >= _fdArray.size() || tgt_fd < 0) | |
| return -EBADF; | |
| int sim_fd = -1; | |
| auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>(_fdArray[tgt_fd]); | |
| if (hbfdp) | |
| sim_fd = hbfdp->getSimFD(); | |
| int status = 0; | |
| if (sim_fd > 2) | |
| status = close(sim_fd); | |
| if (status == 0) | |
| _fdArray[tgt_fd] = nullptr; | |
| return status; | |
| } |