summaryrefslogtreecommitdiff
path: root/src/sim/fd_array.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/sim/fd_array.cc')
-rw-r--r--src/sim/fd_array.cc344
1 files changed, 344 insertions, 0 deletions
diff --git a/src/sim/fd_array.cc b/src/sim/fd_array.cc
new file mode 100644
index 000000000..d73707054
--- /dev/null
+++ b/src/sim/fd_array.cc
@@ -0,0 +1,344 @@
+/*
+ * 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.
+ *
+ * Author: Brandon Potter
+ */
+
+#include "sim/fd_array.hh"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <array>
+#include <memory>
+#include <string>
+
+#include "base/misc.hh"
+#include "params/Process.hh"
+#include "sim/fd_entry.hh"
+
+FDArray::FDArray(std::string const& input, std::string const& output,
+ std::string const& errout)
+ : _input(input), _output(output), _errout(errout), _fdArray(),
+ 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.
+ */
+ void (*seek)(std::shared_ptr<FileFDEntry>)
+ = [] (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(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];
+}
+
+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;
+}