shadPS4/src/core/address_space.h
Stephen Miller 4ba0e62670
Kernel.Vmm: Attempt to address race conditions involving ClampRangeSize, CopySparseMemory, and TryWriteBacking (#3956)
* no

no

* Adjust locking strategy

Use a separate mutex for the initial error checks + GPU unmap instead of using the reader lock. Make sure all writers lock this separate mutex, and for those that don't perform GPU unmaps, lock the writer lock immediately too.

This gets around every race condition I've envisioned so far, and hopefully does the trick?

* Clang

* Always GPU unmap

GPU unmaps have logic built-in to only run on mapped areas.
Not sure if userfaultfd would work with this, but since that's already broken anyway, I'll let reviewers decide that.

Without doing this, I'd need to do an extra pass through VMAs to find what all needs to be GPU modified before I can unmap from GPU, then perform remaining unmap work. Especially for places like MapMemory, that's a lot of code bloat.

* Fixups

* Update memory.cpp

* Rename mutex

It's really just a mutex for the sole purpose of dealing with GPU unmaps, so unmap_mutex is a bit more fitting than transition_mutex
2026-01-27 12:25:23 +02:00

103 lines
3.1 KiB
C++

// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <boost/icl/separate_interval_set.hpp>
#include "common/arch.h"
#include "common/enum.h"
#include "common/types.h"
namespace Core {
enum class MemoryPermission : u32 {
None = 0,
Read = 1 << 0,
Write = 1 << 1,
ReadWrite = Read | Write,
Execute = 1 << 2,
ReadWriteExecute = Read | Write | Execute,
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
/**
* Represents the user virtual address space backed by a dmem memory block
*/
class AddressSpace {
public:
explicit AddressSpace();
~AddressSpace();
[[nodiscard]] u8* BackingBase() const noexcept {
return backing_base;
}
[[nodiscard]] VAddr SystemManagedVirtualBase() noexcept {
return reinterpret_cast<VAddr>(system_managed_base);
}
[[nodiscard]] const u8* SystemManagedVirtualBase() const noexcept {
return system_managed_base;
}
[[nodiscard]] u64 SystemManagedVirtualSize() const noexcept {
return system_managed_size;
}
[[nodiscard]] VAddr SystemReservedVirtualBase() noexcept {
return reinterpret_cast<VAddr>(system_reserved_base);
}
[[nodiscard]] const u8* SystemReservedVirtualBase() const noexcept {
return system_reserved_base;
}
[[nodiscard]] u64 SystemReservedVirtualSize() const noexcept {
return system_reserved_size;
}
[[nodiscard]] VAddr UserVirtualBase() noexcept {
return reinterpret_cast<VAddr>(user_base);
}
[[nodiscard]] const u8* UserVirtualBase() const noexcept {
return user_base;
}
[[nodiscard]] u64 UserVirtualSize() const noexcept {
return user_size;
}
/**
* @brief Maps memory to the specified virtual address.
* @param virtual_addr The base address to place the mapping.
* If zero is provided an address in system managed area is picked.
* @param size The size of the area to map.
* @param phys_addr The offset of the backing file handle to map.
* The same backing region may be aliased into different virtual regions.
* If zero is provided the mapping is considered as private.
* @return A pointer to the mapped memory.
*/
void* Map(VAddr virtual_addr, u64 size, PAddr phys_addr = -1, bool exec = false);
/// Memory maps a specified file descriptor.
void* MapFile(VAddr virtual_addr, u64 size, u64 offset, u32 prot, uintptr_t fd);
/// Unmaps specified virtual memory area.
void Unmap(VAddr virtual_addr, u64 size);
/// Protects requested region.
void Protect(VAddr virtual_addr, u64 size, MemoryPermission perms);
// Returns an interval set containing all usable regions.
boost::icl::interval_set<VAddr> GetUsableRegions();
private:
struct Impl;
std::unique_ptr<Impl> impl;
u8* backing_base{};
u8* system_managed_base{};
u64 system_managed_size{};
u8* system_reserved_base{};
u64 system_reserved_size{};
u8* user_base{};
u64 user_size{};
};
} // namespace Core