From: Matthew Poremba Date: Tue, 11 Feb 2020 23:46:16 +0000 (-0800) Subject: sim-se: Implement Virtual Memory Area API X-Git-Tag: v20.0.0.0~304 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=1566e47017b33c58fbd6325bb3d0f73f40e0befe;p=gem5.git sim-se: Implement Virtual Memory Area API Virtual memory areas are used to track regions of memory which may change over the course of execution, such as heap, stack, and mmap. It is a high-level mimicry of Linux' memory management. VMAs are intended to be used to support lazy allocation of physical pages to valid VMAs as the virtual addresses are touched. Lazy allocation increases speed of simulation for SE mode processes which, for example, mmap large files. The VMAs can also be queried to generate a map of the process' memory which is used in some libraries such as pthreads. This changeset only adds APIs for virtual memory areas. These are used in a subsequent changeset. Change-Id: Ibbdce5be79a95e3231d2e1c9ee8f397b4503f0fb Signed-off-by: Brandon Potter Signed-off-by: Michael LeBeane Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/25365 Reviewed-by: Giacomo Travaglini Reviewed-by: Jason Lowe-Power Maintainer: Jason Lowe-Power Tested-by: kokoro --- diff --git a/src/sim/SConscript b/src/sim/SConscript index 30f04879e..06b0822c2 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 000000000..6f6306c6b --- /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 000000000..3d5d10410 --- /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__