diff --git a/src/sim/SConscript b/src/sim/SConscript index 30f04879e0..06b0822c2e 100644 --- a/src/sim/SConscript +++ b/src/sim/SConscript @@ -90,6 +90,7 @@ if env['TARGET_ISA'] != 'null': Source('pseudo_inst.cc') Source('syscall_emul.cc') Source('syscall_desc.cc') + Source('vma.cc') if env['TARGET_ISA'] != 'x86': Source('microcode_rom.cc') @@ -117,5 +118,6 @@ DebugFlag('WorkItems') DebugFlag('ClockDomain') DebugFlag('VoltageDomain') DebugFlag('DVFS') +DebugFlag('Vma') CompoundFlag('SyscallAll', [ 'SyscallBase', 'SyscallVerbose']) diff --git a/src/sim/vma.cc b/src/sim/vma.cc new file mode 100644 index 0000000000..6f6306c6b8 --- /dev/null +++ b/src/sim/vma.cc @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2017-2020 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#include "sim/vma.hh" + +#include +#include + +#include "base/types.hh" +#include "config/the_isa.hh" + +void +VMA::fillMemPages(Addr start, Addr size, PortProxy &port) const +{ + auto offset = start - _addrRange.start(); + + /** + * Try to copy a full page, but don't overrun the size of the file. + */ + if (offset < _hostBufLen) { + auto size = std::min(_hostBufLen - offset, _pageBytes); + port.writeBlob(start, (uint8_t*)_hostBuf + offset, size); + } +} + +bool +VMA::isStrictSuperset(const AddrRange &r) const +{ + return (r.start() > _addrRange.start() && r.end() < _addrRange.end()); +} + +void +VMA::sliceRegionRight(Addr slice_addr) +{ + if (hasHostBuf()) { + auto nonoverlap_len = slice_addr - _addrRange.start(); + _hostBufLen = std::min(_hostBufLen, nonoverlap_len); + } + + _addrRange = AddrRange(_addrRange.start(), slice_addr); + + DPRINTF(Vma, "slice right vma start %#x end %#x\n", _addrRange.start(), + _addrRange.end()); + + sanityCheck(); +} + +void +VMA::sliceRegionLeft(Addr slice_addr) +{ + if (hasHostBuf()) { + auto overlap_len = slice_addr - _addrRange.start(); + + if (overlap_len >= _hostBufLen) { + _hostBufLen = 0; + _hostBuf = nullptr; + _origHostBuf = nullptr; + } else { + _hostBufLen -= overlap_len; + } + + _hostBuf = (void *)((uint8_t *)_hostBuf + overlap_len); + } + + _addrRange = AddrRange(slice_addr, _addrRange.end()); + + DPRINTF(Vma, "slice left vma start %#x end %#x\n", _addrRange.start(), + _addrRange.end()); + + sanityCheck(); +} + +void +VMA::sanityCheck() +{ + /** + * Avoid regions without a length. + */ + assert(_addrRange.start() != _addrRange.end()); + + /** + * Avoid regions with an end point before the start point + */ + assert(_addrRange.start() < _addrRange.end()); + + /** + * Avoid non-aligned regions; we assume in the code that the + * regions are page aligned so consider this to be a bug. + */ + assert((_addrRange.start() % _pageBytes) == 0); + assert((_addrRange.end() % _pageBytes) == 0); +} + +VMA::MappedFileBuffer::MappedFileBuffer(int fd, size_t length, + off_t offset) + : _buffer(nullptr), _length(length) +{ + panic_if(_length == 0, "Tried to mmap file of length zero"); + + struct stat file_stat; + if (fstat(fd, &file_stat) > 0) { + panic("Cannot stat file: %s\n", strerror(errno)); + } + + // Don't bother mapping more than the actual file size + panic_if(offset > file_stat.st_size, + "Tried to mmap with offset greater than file size"); + _length = std::min((size_t)(file_stat.st_size - offset), _length); + + // cannot call mmap with _length == 0 + if (_length) { + _buffer = mmap(NULL, _length, PROT_READ, + MAP_PRIVATE, fd, offset); + if (_buffer == MAP_FAILED) { + panic("Failed to map file into host address space: %s", + strerror(errno)); + } + } else { + panic("Tried to mmap 0 bytes"); + } +} + +VMA::MappedFileBuffer::~MappedFileBuffer() +{ + if (_buffer) { + panic_if(munmap(_buffer, _length) == -1, + "mmap: failed to unmap file-backed host memory: %s", + strerror(errno)); + } +} diff --git a/src/sim/vma.hh b/src/sim/vma.hh new file mode 100644 index 0000000000..3d5d104100 --- /dev/null +++ b/src/sim/vma.hh @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2017-2020 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#ifndef __SRC_MEM_VMA_HH__ +#define __SRC_MEM_VMA_HH__ + +#include + +#include "arch/isa_traits.hh" +#include "base/addr_range.hh" +#include "base/types.hh" +#include "debug/Vma.hh" +#include "mem/se_translating_port_proxy.hh" + +class VMA +{ + class MappedFileBuffer; + + public: + VMA(AddrRange r, Addr page_bytes, const std::string& vma_name="anon", + int fd=-1, off_t off=0) + : _addrRange(r), _pageBytes(page_bytes), _vmaName(vma_name) + { + DPRINTF(Vma, "Creating vma start %#x len %llu end %#x\n", + r.start(), r.size(), r.end()); + + if (fd != -1) { + _origHostBuf = + std::make_shared(fd, r.size(), off); + _hostBuf = _origHostBuf->getBuffer(); + _hostBufLen = _origHostBuf->getLength(); + } + + sanityCheck(); + } + + /** + * Remap the virtual memory area starting at new_start. + */ + void + remap(Addr new_start) + { + _addrRange = AddrRange(new_start, new_start + _addrRange.size()); + + DPRINTF(Vma, "Remapping vma start %#x end %#x\n", _addrRange.start(), + _addrRange.end()); + + sanityCheck(); + } + + /** + * Check if the virtual memory area has an equivalent buffer on the + * host machine. + */ + bool hasHostBuf() const { return _origHostBuf != nullptr; } + + /** + * Copy memory from a buffer which resides on the host machine into a + * section of memory on the target. + */ + void fillMemPages(Addr start, Addr size, PortProxy &port) const; + + /** + * Returns true if desired range exists within this virtual memory area + * and does not include the start and end addresses. + */ + bool isStrictSuperset(const AddrRange &range) const; + + /** + * Remove the address range to the right of slice_addr. + */ + void sliceRegionRight(Addr slice_addr); + + /** + * Remove the address range to the left of slice_addr. + */ + void sliceRegionLeft(Addr slice_addr); + + const std::string& getName() { return _vmaName; } + + /** + * Defer AddrRange related calls to the AddrRange. + */ + Addr size() { return _addrRange.size(); } + Addr start() { return _addrRange.start(); } + Addr end() { return _addrRange.end(); } + + bool + mergesWith(const AddrRange& r) const + { + return _addrRange.mergesWith(r); + } + + bool + intersects(const AddrRange& r) const + { + return _addrRange.intersects(r); + } + + bool + isSubset(const AddrRange& r) const + { + return _addrRange.isSubset(r); + } + + bool + contains(const Addr& a) const + { + return _addrRange.contains(a); + } + + private: + void sanityCheck(); + + /** + * Address range for this virtual memory area. + */ + AddrRange _addrRange; + + /** + * Number of bytes in an OS page. + */ + Addr _pageBytes; + + /** + * The host file backing will be chopped up and reassigned as pages are + * mapped, remapped, and unmapped. In addition to the current host + * pointer and length, each virtual memory area will also keep a + * reference-counted handle to the original host memory. The last virtual + * memory area to die cleans up the host memory it handles. + */ + std::shared_ptr _origHostBuf; + + /** + * Host buffer ptr for this virtual memory area. + */ + void *_hostBuf; + + /** + * Length of host buffer for this virtual memory area. + */ + uint64_t _hostBufLen; + + /** + * Human-readable name associated with the virtual memory area. + * The name is useful for debugging and also exposing vma state through + * the psuedo file system (i.e. Linux's /proc/self/maps) to the + * application. + */ + std::string _vmaName; + + /** + * MappedFileBuffer is a wrapper around a region of host memory backed by a + * file. The constructor attempts to map a file from host memory, and the + * destructor attempts to unmap it. If there is a problem with the host + * mapping/unmapping, then we panic. + */ + class MappedFileBuffer + { + public: + MappedFileBuffer(int fd, size_t length, off_t offset); + ~MappedFileBuffer(); + + void *getBuffer() const { return _buffer; } + uint64_t getLength() const { return _length; } + + private: + void *_buffer; // Host buffer ptr + size_t _length; // Length of host ptr + }; +}; + +#endif // __SRC_MEM_VMA_HH__