Compare commits
12 Commits
android-16
...
android-17
Author | SHA1 | Date | |
---|---|---|---|
ab2af23e47 | |||
52d3d87d09 | |||
30f76ed9c2 | |||
c85f87ffa4 | |||
7778f1906d | |||
05e3db3ac9 | |||
c57ae803a6 | |||
db7b2bc8f1 | |||
31bf57a310 | |||
cae675343c | |||
35501ba41c | |||
419055e484 |
12
README.md
12
README.md
@ -1,3 +1,15 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
| [12448](https://github.com/yuzu-emu/yuzu//pull/12448) | [`b1d4804c0`](https://github.com/yuzu-emu/yuzu//pull/12448/files) | renderer_vulkan: demote format assert to error log | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12449](https://github.com/yuzu-emu/yuzu//pull/12449) | [`6a1ddc502`](https://github.com/yuzu-emu/yuzu//pull/12449/files) | renderer_vulkan: skip SetObjectNameEXT on unsupported driver | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12466](https://github.com/yuzu-emu/yuzu//pull/12466) | [`5f3720138`](https://github.com/yuzu-emu/yuzu//pull/12466/files) | core: track separate heap allocation for linux | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12467](https://github.com/yuzu-emu/yuzu//pull/12467) | [`cfc6c5f8f`](https://github.com/yuzu-emu/yuzu//pull/12467/files) | Revert " shader_recompiler: use minimal clip distance array " | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
||||
-----
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
@ -64,6 +64,8 @@ add_library(common STATIC
|
||||
fs/path_util.cpp
|
||||
fs/path_util.h
|
||||
hash.h
|
||||
heap_tracker.cpp
|
||||
heap_tracker.h
|
||||
hex_util.cpp
|
||||
hex_util.h
|
||||
host_memory.cpp
|
||||
|
385
src/common/heap_tracker.cpp
Normal file
385
src/common/heap_tracker.cpp
Normal file
@ -0,0 +1,385 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/heap_tracker.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t MaxResidentMapCount = 0x8000;
|
||||
|
||||
} // namespace
|
||||
|
||||
HeapTracker::HeapTracker(Common::HostMemory& buffer) : m_buffer(buffer) {}
|
||||
HeapTracker::~HeapTracker() = default;
|
||||
|
||||
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||
MemoryPermission perm, bool is_separate_heap) {
|
||||
// When mapping other memory, map pages immediately.
|
||||
if (!is_separate_heap) {
|
||||
m_buffer.Map(virtual_offset, host_offset, length, perm, false);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// We are mapping part of a separate heap.
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* map = new SeparateHeapMap{
|
||||
.vaddr = virtual_offset,
|
||||
.paddr = host_offset,
|
||||
.size = length,
|
||||
.map_id = m_next_map_id++,
|
||||
.tick = m_tick++,
|
||||
.perm = perm,
|
||||
.is_resident = false,
|
||||
};
|
||||
|
||||
// Insert into mappings.
|
||||
m_mappings.insert(*map);
|
||||
}
|
||||
|
||||
// Finally, map.
|
||||
this->DeferredMapSeparateHeap(virtual_offset);
|
||||
}
|
||||
|
||||
void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
|
||||
// If this is a separate heap...
|
||||
if (is_separate_heap) {
|
||||
std::scoped_lock lk{m_rebuild_lock, m_lock};
|
||||
|
||||
const SeparateHeapMap key{
|
||||
.vaddr = virtual_offset,
|
||||
.size = size,
|
||||
};
|
||||
|
||||
// Split at the boundaries of the region we are removing.
|
||||
this->SplitHeapMapLocked(virtual_offset);
|
||||
this->SplitHeapMapLocked(virtual_offset + size);
|
||||
|
||||
// Erase all mappings in range.
|
||||
auto it = m_mappings.find(key);
|
||||
while (it != m_mappings.end() && it->vaddr < virtual_offset + size) {
|
||||
// Get pointer to item.
|
||||
SeparateHeapMap* const item = std::addressof(*it);
|
||||
|
||||
if (item->is_resident) {
|
||||
// Unlink from resident tree.
|
||||
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
|
||||
|
||||
// Decrease reference count.
|
||||
const auto count_it = m_resident_map_counts.find(item->map_id);
|
||||
this->RemoveReferenceLocked(count_it, 1);
|
||||
}
|
||||
|
||||
// Unlink from mapping tree and advance.
|
||||
it = m_mappings.erase(it);
|
||||
|
||||
// Free the item.
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
// Unmap pages.
|
||||
m_buffer.Unmap(virtual_offset, size, false);
|
||||
}
|
||||
|
||||
void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission perm) {
|
||||
// Ensure no rebuild occurs while reprotecting.
|
||||
std::shared_lock lk{m_rebuild_lock};
|
||||
|
||||
// Split at the boundaries of the region we are reprotecting.
|
||||
this->SplitHeapMap(virtual_offset, size);
|
||||
|
||||
// Declare tracking variables.
|
||||
VAddr cur = virtual_offset;
|
||||
VAddr end = virtual_offset + size;
|
||||
|
||||
while (cur < end) {
|
||||
VAddr next = cur;
|
||||
bool should_protect = false;
|
||||
|
||||
{
|
||||
std::scoped_lock lk2{m_lock};
|
||||
|
||||
const SeparateHeapMap key{
|
||||
.vaddr = next,
|
||||
};
|
||||
|
||||
// Try to get the next mapping corresponding to this address.
|
||||
const auto it = m_mappings.nfind_key(key);
|
||||
|
||||
if (it == m_mappings.end()) {
|
||||
// There are no separate heap mappings remaining.
|
||||
next = end;
|
||||
should_protect = true;
|
||||
} else if (it->vaddr == cur) {
|
||||
// We are in range.
|
||||
// Update permission bits.
|
||||
it->perm = perm;
|
||||
|
||||
// Determine next address and whether we should protect.
|
||||
next = cur + it->size;
|
||||
should_protect = it->is_resident;
|
||||
} else /* if (it->vaddr > cur) */ {
|
||||
// We weren't in range, but there is a block coming up that will be.
|
||||
next = it->vaddr;
|
||||
should_protect = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp to end.
|
||||
next = std::min(next, end);
|
||||
|
||||
// Reprotect, if we need to.
|
||||
if (should_protect) {
|
||||
m_buffer.Protect(cur, next - cur, perm);
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
|
||||
if (m_buffer.IsInVirtualRange(fault_address)) {
|
||||
return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
while (this->IsEvictRequiredLocked()) {
|
||||
// Unlock before we rebuild to ensure proper lock ordering.
|
||||
m_lock.unlock();
|
||||
|
||||
// Evict four maps.
|
||||
for (size_t i = 0; i < 4; /* ... */) {
|
||||
i += this->EvictSingleSeparateHeapMap();
|
||||
}
|
||||
|
||||
// Lock again.
|
||||
m_lock.lock();
|
||||
}
|
||||
|
||||
// Check to ensure this was a non-resident separate heap mapping.
|
||||
const auto it = this->GetNearestHeapMapLocked(virtual_offset);
|
||||
if (it == m_mappings.end()) {
|
||||
// Not in any separate heap.
|
||||
return false;
|
||||
}
|
||||
if (it->is_resident) {
|
||||
// Already mapped and shouldn't be considered again.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Map the area.
|
||||
m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
|
||||
|
||||
// This map is now resident.
|
||||
this->AddReferenceLocked(it->map_id, 1);
|
||||
it->is_resident = true;
|
||||
it->tick = m_tick++;
|
||||
|
||||
// Insert into resident maps.
|
||||
m_resident_mappings.insert(*it);
|
||||
|
||||
// We succeeded.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HeapTracker::EvictSingleSeparateHeapMap() {
|
||||
std::scoped_lock lk{m_rebuild_lock, m_lock};
|
||||
|
||||
ASSERT(!m_resident_mappings.empty());
|
||||
|
||||
// Select the item with the lowest tick to evict.
|
||||
auto* const item = std::addressof(*m_resident_mappings.begin());
|
||||
auto it = m_mappings.iterator_to(*item);
|
||||
|
||||
// Track the map ID.
|
||||
const size_t map_id = it->map_id;
|
||||
|
||||
// Walk backwards until we find the first entry.
|
||||
while (it != m_mappings.begin()) {
|
||||
// If the previous element does not have the same map ID, stop.
|
||||
const auto prev = std::prev(it);
|
||||
if (prev->map_id != map_id) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Continue.
|
||||
it = prev;
|
||||
}
|
||||
|
||||
// Track the begin and end address.
|
||||
const VAddr begin_vaddr = it->vaddr;
|
||||
VAddr end_vaddr = begin_vaddr;
|
||||
|
||||
// Get the count iterator.
|
||||
const auto count_it = m_resident_map_counts.find(map_id);
|
||||
|
||||
// Declare whether we have erased an underlying mapping.
|
||||
bool was_erased = false;
|
||||
|
||||
// Unmark and merge everything in range.
|
||||
while (it != m_mappings.end() && it->map_id == map_id) {
|
||||
if (it->is_resident) {
|
||||
// Remove from resident tree.
|
||||
m_resident_mappings.erase(m_resident_mappings.iterator_to(*it));
|
||||
it->is_resident = false;
|
||||
|
||||
// Remove reference count.
|
||||
was_erased |= this->RemoveReferenceLocked(count_it, 1);
|
||||
}
|
||||
|
||||
// Update the end address.
|
||||
end_vaddr = it->vaddr + it->size;
|
||||
|
||||
// Advance.
|
||||
it = this->MergeHeapMapForEvictLocked(it);
|
||||
}
|
||||
|
||||
// Finally, unmap.
|
||||
ASSERT(end_vaddr >= begin_vaddr);
|
||||
m_buffer.Unmap(begin_vaddr, end_vaddr - begin_vaddr, false);
|
||||
|
||||
// Return whether we actually removed a mapping.
|
||||
// This will be true if there were no holes, which is likely.
|
||||
return was_erased;
|
||||
}
|
||||
|
||||
void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
this->SplitHeapMapLocked(offset);
|
||||
this->SplitHeapMapLocked(offset + size);
|
||||
}
|
||||
|
||||
void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
||||
const auto it = this->GetNearestHeapMapLocked(offset);
|
||||
if (it == m_mappings.end() || it->vaddr == offset) {
|
||||
// Not contained or no split required.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the underlying item as the left.
|
||||
auto* const left = std::addressof(*it);
|
||||
|
||||
// Cache the original size values.
|
||||
const size_t size = left->size;
|
||||
|
||||
// Adjust the left map.
|
||||
const size_t left_size = offset - left->vaddr;
|
||||
left->size = left_size;
|
||||
|
||||
// Create the new right map.
|
||||
auto* const right = new SeparateHeapMap{
|
||||
.vaddr = left->vaddr + left_size,
|
||||
.paddr = left->paddr + left_size,
|
||||
.size = size - left_size,
|
||||
.map_id = left->map_id,
|
||||
.tick = left->tick,
|
||||
.perm = left->perm,
|
||||
.is_resident = left->is_resident,
|
||||
};
|
||||
|
||||
// Insert the new right map.
|
||||
m_mappings.insert(*right);
|
||||
|
||||
// If the original map was not resident, we are done.
|
||||
if (!left->is_resident) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update reference count.
|
||||
this->AddReferenceLocked(left->map_id, 1);
|
||||
|
||||
// Insert right into resident map.
|
||||
m_resident_mappings.insert(*right);
|
||||
}
|
||||
|
||||
HeapTracker::AddrTree::iterator HeapTracker::MergeHeapMapForEvictLocked(AddrTree::iterator it) {
|
||||
if (it == m_mappings.end()) {
|
||||
// Not contained.
|
||||
return it;
|
||||
}
|
||||
|
||||
if (it == m_mappings.begin()) {
|
||||
// Nothing to merge with.
|
||||
return std::next(it);
|
||||
}
|
||||
|
||||
// Get the left and right items.
|
||||
auto* const right = std::addressof(*it);
|
||||
auto* const left = std::addressof(*std::prev(it));
|
||||
|
||||
if (left->vaddr + left->size != right->vaddr) {
|
||||
// Virtual range not contiguous, cannot merge.
|
||||
return std::next(it);
|
||||
}
|
||||
|
||||
if (left->paddr + left->size != right->paddr) {
|
||||
// Physical range not contiguous, cannot merge.
|
||||
return std::next(it);
|
||||
}
|
||||
|
||||
if (left->perm != right->perm) {
|
||||
// Permissions mismatch, cannot merge.
|
||||
return std::next(it);
|
||||
}
|
||||
|
||||
if (left->map_id != right->map_id) {
|
||||
// Map ID mismatch, cannot merge.
|
||||
return std::next(it);
|
||||
}
|
||||
|
||||
// Merge size to the left.
|
||||
left->size += right->size;
|
||||
|
||||
// Erase the right element.
|
||||
const auto next_it = m_mappings.erase(it);
|
||||
|
||||
// Free the right element.
|
||||
delete right;
|
||||
|
||||
// Return the iterator to the next position.
|
||||
return next_it;
|
||||
}
|
||||
|
||||
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
|
||||
const SeparateHeapMap key{
|
||||
.vaddr = offset,
|
||||
};
|
||||
|
||||
return m_mappings.find(key);
|
||||
}
|
||||
|
||||
void HeapTracker::AddReferenceLocked(size_t map_id, size_t inc) {
|
||||
m_resident_map_counts[map_id]++;
|
||||
}
|
||||
|
||||
bool HeapTracker::RemoveReferenceLocked(MapCountTree::iterator it, size_t dec) {
|
||||
ASSERT(it != m_resident_map_counts.end());
|
||||
|
||||
const auto new_value = it->second -= dec;
|
||||
ASSERT(new_value >= 0);
|
||||
|
||||
if (new_value <= 0) {
|
||||
m_resident_map_counts.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HeapTracker::IsEvictRequiredLocked() {
|
||||
return m_resident_map_counts.size() > MaxResidentMapCount;
|
||||
}
|
||||
|
||||
} // namespace Common
|
103
src/common/heap_tracker.h
Normal file
103
src/common/heap_tracker.h
Normal file
@ -0,0 +1,103 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "common/host_memory.h"
|
||||
#include "common/intrusive_red_black_tree.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
struct SeparateHeapMap {
|
||||
Common::IntrusiveRedBlackTreeNode addr_node{};
|
||||
Common::IntrusiveRedBlackTreeNode tick_node{};
|
||||
VAddr vaddr{};
|
||||
PAddr paddr{};
|
||||
size_t size{};
|
||||
size_t map_id{};
|
||||
size_t tick{};
|
||||
MemoryPermission perm{};
|
||||
bool is_resident{};
|
||||
};
|
||||
|
||||
struct SeparateHeapMapAddrComparator {
|
||||
static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
|
||||
if (lhs.vaddr < rhs.vaddr) {
|
||||
return -1;
|
||||
} else if (lhs.vaddr <= (rhs.vaddr + rhs.size - 1)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SeparateHeapMapTickComparator {
|
||||
static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
|
||||
if (lhs.tick < rhs.tick) {
|
||||
return -1;
|
||||
} else if (lhs.tick > rhs.tick) {
|
||||
return 1;
|
||||
} else {
|
||||
return SeparateHeapMapAddrComparator::Compare(lhs, rhs);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class HeapTracker {
|
||||
public:
|
||||
explicit HeapTracker(Common::HostMemory& buffer);
|
||||
~HeapTracker();
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm,
|
||||
bool is_separate_heap);
|
||||
void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap);
|
||||
void Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
|
||||
u8* VirtualBasePointer() {
|
||||
return m_buffer.VirtualBasePointer();
|
||||
}
|
||||
|
||||
bool DeferredMapSeparateHeap(u8* fault_address);
|
||||
bool DeferredMapSeparateHeap(size_t virtual_offset);
|
||||
|
||||
private:
|
||||
Common::HostMemory& m_buffer;
|
||||
|
||||
std::shared_mutex m_rebuild_lock{};
|
||||
std::mutex m_lock{};
|
||||
size_t m_next_map_id{};
|
||||
size_t m_tick{};
|
||||
|
||||
private:
|
||||
using AddrTreeTraits =
|
||||
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>;
|
||||
using AddrTree = AddrTreeTraits::TreeType<SeparateHeapMapAddrComparator>;
|
||||
|
||||
using TickTreeTraits =
|
||||
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
|
||||
using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
|
||||
using MapCountTree = std::map<size_t, s64>;
|
||||
|
||||
MapCountTree m_resident_map_counts{};
|
||||
AddrTree m_mappings{};
|
||||
TickTree m_resident_mappings{};
|
||||
|
||||
private:
|
||||
void SplitHeapMap(VAddr offset, size_t size);
|
||||
void SplitHeapMapLocked(VAddr offset);
|
||||
|
||||
AddrTree::iterator MergeHeapMapForEvictLocked(AddrTree::iterator cur);
|
||||
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
||||
|
||||
bool EvictSingleSeparateHeapMap();
|
||||
|
||||
void AddReferenceLocked(size_t map_id, size_t inc);
|
||||
bool RemoveReferenceLocked(MapCountTree::iterator map_id, size_t dec);
|
||||
bool IsEvictRequiredLocked();
|
||||
};
|
||||
|
||||
} // namespace Common
|
@ -679,7 +679,7 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
||||
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
|
||||
|
||||
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||
MemoryPermission perms) {
|
||||
MemoryPermission perms, bool separate_heap) {
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(host_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
@ -691,7 +691,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||
impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
|
||||
}
|
||||
|
||||
void HostMemory::Unmap(size_t virtual_offset, size_t length) {
|
||||
void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) {
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
ASSERT(virtual_offset + length <= virtual_size);
|
||||
@ -701,14 +701,16 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) {
|
||||
impl->Unmap(virtual_offset + virtual_base_offset, length);
|
||||
}
|
||||
|
||||
void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write,
|
||||
bool execute) {
|
||||
void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
ASSERT(virtual_offset + length <= virtual_size);
|
||||
if (length == 0 || !virtual_base || !impl) {
|
||||
return;
|
||||
}
|
||||
const bool read = True(perm & MemoryPermission::Read);
|
||||
const bool write = True(perm & MemoryPermission::Write);
|
||||
const bool execute = True(perm & MemoryPermission::Execute);
|
||||
impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
|
||||
}
|
||||
|
||||
|
@ -40,11 +40,12 @@ public:
|
||||
HostMemory(HostMemory&& other) noexcept;
|
||||
HostMemory& operator=(HostMemory&& other) noexcept;
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms);
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms,
|
||||
bool separate_heap);
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length);
|
||||
void Unmap(size_t virtual_offset, size_t length, bool separate_heap);
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false);
|
||||
void Protect(size_t virtual_offset, size_t length, MemoryPermission perms);
|
||||
|
||||
void EnableDirectMappedAddress();
|
||||
|
||||
@ -64,6 +65,10 @@ public:
|
||||
return virtual_base;
|
||||
}
|
||||
|
||||
bool IsInVirtualRange(void* address) const noexcept {
|
||||
return address >= virtual_base && address < virtual_base + virtual_size;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t backing_size{};
|
||||
size_t virtual_size{};
|
||||
|
@ -978,6 +978,7 @@ endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
target_sources(core PRIVATE
|
||||
arm/dynarmic/arm_dynarmic.cpp
|
||||
arm/dynarmic/arm_dynarmic.h
|
||||
arm/dynarmic/arm_dynarmic_64.cpp
|
||||
arm/dynarmic/arm_dynarmic_64.h
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
namespace Core {
|
||||
|
||||
void ArmInterface::LogBacktrace(const Kernel::KProcess* process) const {
|
||||
void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
|
||||
Kernel::Svc::ThreadContext ctx;
|
||||
this->GetContext(ctx);
|
||||
|
||||
|
@ -95,7 +95,7 @@ public:
|
||||
virtual void SignalInterrupt(Kernel::KThread* thread) = 0;
|
||||
|
||||
// Stack trace generation.
|
||||
void LogBacktrace(const Kernel::KProcess* process) const;
|
||||
void LogBacktrace(Kernel::KProcess* process) const;
|
||||
|
||||
// Debug functionality.
|
||||
virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
|
||||
|
@ -79,7 +79,7 @@ constexpr std::array<u64, 2> SegmentBases{
|
||||
0x7100000000ULL,
|
||||
};
|
||||
|
||||
void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<BacktraceEntry>& out) {
|
||||
void SymbolicateBacktrace(Kernel::KProcess* process, std::vector<BacktraceEntry>& out) {
|
||||
auto modules = FindModules(process);
|
||||
|
||||
const bool is_64 = process->Is64Bit();
|
||||
@ -118,7 +118,7 @@ void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<Backtrace
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process,
|
||||
std::vector<BacktraceEntry> GetAArch64Backtrace(Kernel::KProcess* process,
|
||||
const Kernel::Svc::ThreadContext& ctx) {
|
||||
std::vector<BacktraceEntry> out;
|
||||
auto& memory = process->GetMemory();
|
||||
@ -144,7 +144,7 @@ std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process,
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process,
|
||||
std::vector<BacktraceEntry> GetAArch32Backtrace(Kernel::KProcess* process,
|
||||
const Kernel::Svc::ThreadContext& ctx) {
|
||||
std::vector<BacktraceEntry> out;
|
||||
auto& memory = process->GetMemory();
|
||||
@ -173,7 +173,7 @@ std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process,
|
||||
} // namespace
|
||||
|
||||
std::optional<std::string> GetThreadName(const Kernel::KThread* thread) {
|
||||
const auto* process = thread->GetOwnerProcess();
|
||||
auto* process = thread->GetOwnerProcess();
|
||||
if (process->Is64Bit()) {
|
||||
return GetNameFromThreadType64(process->GetMemory(), *thread);
|
||||
} else {
|
||||
@ -248,7 +248,7 @@ Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process,
|
||||
return cur_addr - 1;
|
||||
}
|
||||
|
||||
Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
|
||||
Loader::AppLoader::Modules FindModules(Kernel::KProcess* process) {
|
||||
Loader::AppLoader::Modules modules;
|
||||
|
||||
auto& page_table = process->GetPageTable();
|
||||
@ -312,7 +312,7 @@ Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
|
||||
return modules;
|
||||
}
|
||||
|
||||
Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process) {
|
||||
Kernel::KProcessAddress FindMainModuleEntrypoint(Kernel::KProcess* process) {
|
||||
// Do we have any loaded executable sections?
|
||||
auto modules = FindModules(process);
|
||||
|
||||
@ -337,7 +337,7 @@ void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 addres
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process,
|
||||
std::vector<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process,
|
||||
const Kernel::Svc::ThreadContext& ctx) {
|
||||
if (process->Is64Bit()) {
|
||||
return GetAArch64Backtrace(process, ctx);
|
||||
|
@ -14,9 +14,9 @@ std::optional<std::string> GetThreadName(const Kernel::KThread* thread);
|
||||
std::string_view GetThreadWaitReason(const Kernel::KThread* thread);
|
||||
std::string GetThreadState(const Kernel::KThread* thread);
|
||||
|
||||
Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process);
|
||||
Loader::AppLoader::Modules FindModules(Kernel::KProcess* process);
|
||||
Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base);
|
||||
Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process);
|
||||
Kernel::KProcessAddress FindMainModuleEntrypoint(Kernel::KProcess* process);
|
||||
|
||||
void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size);
|
||||
|
||||
@ -28,7 +28,7 @@ struct BacktraceEntry {
|
||||
std::string name;
|
||||
};
|
||||
|
||||
std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process,
|
||||
std::vector<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process,
|
||||
const Kernel::Svc::ThreadContext& ctx);
|
||||
std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread);
|
||||
|
||||
|
49
src/core/arm/dynarmic/arm_dynarmic.cpp
Normal file
49
src/core/arm/dynarmic/arm_dynarmic.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include "common/signal_chain.h"
|
||||
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace {
|
||||
|
||||
thread_local Core::Memory::Memory* g_current_memory{};
|
||||
std::once_flag g_registered{};
|
||||
struct sigaction g_old_segv {};
|
||||
|
||||
void HandleSigSegv(int sig, siginfo_t* info, void* ctx) {
|
||||
if (g_current_memory && g_current_memory->InvalidateSeparateHeap(info->si_addr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return g_old_segv.sa_sigaction(sig, info, ctx);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) {
|
||||
g_current_memory = std::addressof(process->GetMemory());
|
||||
}
|
||||
|
||||
ScopedJitExecution::~ScopedJitExecution() {
|
||||
g_current_memory = nullptr;
|
||||
}
|
||||
|
||||
void ScopedJitExecution::RegisterHandler() {
|
||||
std::call_once(g_registered, [] {
|
||||
struct sigaction sa {};
|
||||
sa.sa_sigaction = &HandleSigSegv;
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
Common::SigAction(SIGSEGV, std::addressof(sa), std::addressof(g_old_segv));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
#endif
|
@ -26,4 +26,24 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
|
||||
return static_cast<HaltReason>(hr);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
class ScopedJitExecution {
|
||||
public:
|
||||
explicit ScopedJitExecution(Kernel::KProcess* process);
|
||||
~ScopedJitExecution();
|
||||
static void RegisterHandler();
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class ScopedJitExecution {
|
||||
public:
|
||||
explicit ScopedJitExecution(Kernel::KProcess* process) {}
|
||||
~ScopedJitExecution() {}
|
||||
static void RegisterHandler() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Core
|
||||
|
@ -15,7 +15,7 @@ using namespace Common::Literals;
|
||||
|
||||
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks32(ArmDynarmic32& parent, const Kernel::KProcess* process)
|
||||
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process)
|
||||
: m_parent{parent}, m_memory(process->GetMemory()),
|
||||
m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()},
|
||||
m_check_memory_access{m_debugger_enabled ||
|
||||
@ -169,7 +169,7 @@ public:
|
||||
|
||||
ArmDynarmic32& m_parent;
|
||||
Core::Memory::Memory& m_memory;
|
||||
const Kernel::KProcess* m_process{};
|
||||
Kernel::KProcess* m_process{};
|
||||
const bool m_debugger_enabled{};
|
||||
const bool m_check_memory_access{};
|
||||
static constexpr u64 MinimumRunCycles = 10000U;
|
||||
@ -331,11 +331,15 @@ bool ArmDynarmic32::IsInThumbMode() const {
|
||||
}
|
||||
|
||||
HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
|
||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||
|
||||
m_jit->ClearExclusiveState();
|
||||
return TranslateHaltReason(m_jit->Run());
|
||||
}
|
||||
|
||||
HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
|
||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||
|
||||
m_jit->ClearExclusiveState();
|
||||
return TranslateHaltReason(m_jit->Step());
|
||||
}
|
||||
@ -370,13 +374,14 @@ void ArmDynarmic32::RewindBreakpointInstruction() {
|
||||
this->SetContext(m_breakpoint_context);
|
||||
}
|
||||
|
||||
ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process,
|
||||
ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProcess* process,
|
||||
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index)
|
||||
: ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor},
|
||||
m_cb(std::make_unique<DynarmicCallbacks32>(*this, process)),
|
||||
m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
|
||||
auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
|
||||
m_jit = MakeJit(&page_table_impl);
|
||||
ScopedJitExecution::RegisterHandler();
|
||||
}
|
||||
|
||||
ArmDynarmic32::~ArmDynarmic32() = default;
|
||||
|
@ -20,7 +20,7 @@ class System;
|
||||
|
||||
class ArmDynarmic32 final : public ArmInterface {
|
||||
public:
|
||||
ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process,
|
||||
ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProcess* process,
|
||||
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index);
|
||||
~ArmDynarmic32() override;
|
||||
|
||||
|
@ -15,7 +15,7 @@ using namespace Common::Literals;
|
||||
|
||||
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks64(ArmDynarmic64& parent, const Kernel::KProcess* process)
|
||||
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process)
|
||||
: m_parent{parent}, m_memory(process->GetMemory()),
|
||||
m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()},
|
||||
m_check_memory_access{m_debugger_enabled ||
|
||||
@ -216,7 +216,7 @@ public:
|
||||
Core::Memory::Memory& m_memory;
|
||||
u64 m_tpidrro_el0{};
|
||||
u64 m_tpidr_el0{};
|
||||
const Kernel::KProcess* m_process{};
|
||||
Kernel::KProcess* m_process{};
|
||||
const bool m_debugger_enabled{};
|
||||
const bool m_check_memory_access{};
|
||||
static constexpr u64 MinimumRunCycles = 10000U;
|
||||
@ -362,11 +362,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
|
||||
}
|
||||
|
||||
HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
|
||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||
|
||||
m_jit->ClearExclusiveState();
|
||||
return TranslateHaltReason(m_jit->Run());
|
||||
}
|
||||
|
||||
HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
|
||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||
|
||||
m_jit->ClearExclusiveState();
|
||||
return TranslateHaltReason(m_jit->Step());
|
||||
}
|
||||
@ -399,13 +403,14 @@ void ArmDynarmic64::RewindBreakpointInstruction() {
|
||||
this->SetContext(m_breakpoint_context);
|
||||
}
|
||||
|
||||
ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process,
|
||||
ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProcess* process,
|
||||
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index)
|
||||
: ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor},
|
||||
m_cb(std::make_unique<DynarmicCallbacks64>(*this, process)), m_core_index{core_index} {
|
||||
auto& page_table = process->GetPageTable().GetBasePageTable();
|
||||
auto& page_table_impl = page_table.GetImpl();
|
||||
m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
|
||||
ScopedJitExecution::RegisterHandler();
|
||||
}
|
||||
|
||||
ArmDynarmic64::~ArmDynarmic64() = default;
|
||||
|
@ -25,7 +25,7 @@ class System;
|
||||
|
||||
class ArmDynarmic64 final : public ArmInterface {
|
||||
public:
|
||||
ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process,
|
||||
ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProcess* process,
|
||||
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index);
|
||||
~ArmDynarmic64() override;
|
||||
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/gpu_dirty_memory_manager.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
@ -130,11 +129,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system)
|
||||
: kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
|
||||
cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{},
|
||||
time_manager{system}, gpu_dirty_memory_write_manager{} {
|
||||
memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
|
||||
}
|
||||
: kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system},
|
||||
reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {}
|
||||
|
||||
void Initialize(System& system) {
|
||||
device_memory = std::make_unique<Core::DeviceMemory>();
|
||||
@ -241,17 +237,17 @@ struct System::Impl {
|
||||
debugger = std::make_unique<Debugger>(system, port);
|
||||
}
|
||||
|
||||
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
|
||||
void InitializeKernel(System& system) {
|
||||
LOG_DEBUG(Core, "initialized OK");
|
||||
|
||||
// Setting changes may require a full system reinitialization (e.g., disabling multicore).
|
||||
ReinitializeIfNecessary(system);
|
||||
|
||||
memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
|
||||
|
||||
kernel.Initialize();
|
||||
cpu_manager.Initialize();
|
||||
}
|
||||
|
||||
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
|
||||
/// Reset all glue registrations
|
||||
arp_manager.ResetAll();
|
||||
|
||||
@ -300,17 +296,9 @@ struct System::Impl {
|
||||
return SystemResultStatus::ErrorGetLoader;
|
||||
}
|
||||
|
||||
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
|
||||
if (init_result != SystemResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
|
||||
static_cast<int>(init_result));
|
||||
ShutdownMainProcess();
|
||||
return init_result;
|
||||
}
|
||||
InitializeKernel(system);
|
||||
|
||||
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
|
||||
|
||||
// Create the process.
|
||||
// Create the application process.
|
||||
auto main_process = Kernel::KProcess::Create(system.Kernel());
|
||||
Kernel::KProcess::Register(system.Kernel(), main_process);
|
||||
kernel.AppendNewProcess(main_process);
|
||||
@ -323,7 +311,18 @@ struct System::Impl {
|
||||
return static_cast<SystemResultStatus>(
|
||||
static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
|
||||
}
|
||||
|
||||
// Set up the rest of the system.
|
||||
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
|
||||
if (init_result != SystemResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
|
||||
static_cast<int>(init_result));
|
||||
ShutdownMainProcess();
|
||||
return init_result;
|
||||
}
|
||||
|
||||
AddGlueRegistrationForProcess(*app_loader, *main_process);
|
||||
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
|
||||
|
||||
// Initialize cheat engine
|
||||
if (cheat_engine) {
|
||||
@ -426,7 +425,6 @@ struct System::Impl {
|
||||
cpu_manager.Shutdown();
|
||||
debugger.reset();
|
||||
kernel.Shutdown();
|
||||
memory.Reset();
|
||||
Network::RestartSocketOperations();
|
||||
|
||||
if (auto room_member = room_network.GetRoomMember().lock()) {
|
||||
@ -507,7 +505,6 @@ struct System::Impl {
|
||||
std::unique_ptr<Tegra::Host1x::Host1x> host1x_core;
|
||||
std::unique_ptr<Core::DeviceMemory> device_memory;
|
||||
std::unique_ptr<AudioCore::AudioCore> audio_core;
|
||||
Core::Memory::Memory memory;
|
||||
Core::HID::HIDCore hid_core;
|
||||
Network::RoomNetwork room_network;
|
||||
|
||||
@ -567,9 +564,6 @@ struct System::Impl {
|
||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
||||
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
|
||||
|
||||
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES>
|
||||
gpu_dirty_memory_write_manager{};
|
||||
|
||||
std::deque<std::vector<u8>> user_channel;
|
||||
};
|
||||
|
||||
@ -652,29 +646,12 @@ void System::PrepareReschedule(const u32 core_index) {
|
||||
impl->kernel.PrepareReschedule(core_index);
|
||||
}
|
||||
|
||||
Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() {
|
||||
const std::size_t core = impl->kernel.GetCurrentHostThreadID();
|
||||
return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
|
||||
? core
|
||||
: Core::Hardware::NUM_CPU_CORES - 1];
|
||||
}
|
||||
|
||||
/// Provides a constant reference to the current gou dirty memory manager.
|
||||
const Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() const {
|
||||
const std::size_t core = impl->kernel.GetCurrentHostThreadID();
|
||||
return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
|
||||
? core
|
||||
: Core::Hardware::NUM_CPU_CORES - 1];
|
||||
}
|
||||
|
||||
size_t System::GetCurrentHostThreadID() const {
|
||||
return impl->kernel.GetCurrentHostThreadID();
|
||||
}
|
||||
|
||||
void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
|
||||
for (auto& manager : impl->gpu_dirty_memory_write_manager) {
|
||||
manager.Gather(callback);
|
||||
}
|
||||
return this->ApplicationProcess()->GatherGPUDirtyMemory(callback);
|
||||
}
|
||||
|
||||
PerfStatsResults System::GetAndResetPerfStats() {
|
||||
@ -723,20 +700,12 @@ const Kernel::KProcess* System::ApplicationProcess() const {
|
||||
return impl->kernel.ApplicationProcess();
|
||||
}
|
||||
|
||||
ExclusiveMonitor& System::Monitor() {
|
||||
return impl->kernel.GetExclusiveMonitor();
|
||||
}
|
||||
|
||||
const ExclusiveMonitor& System::Monitor() const {
|
||||
return impl->kernel.GetExclusiveMonitor();
|
||||
}
|
||||
|
||||
Memory::Memory& System::ApplicationMemory() {
|
||||
return impl->memory;
|
||||
return impl->kernel.ApplicationProcess()->GetMemory();
|
||||
}
|
||||
|
||||
const Core::Memory::Memory& System::ApplicationMemory() const {
|
||||
return impl->memory;
|
||||
return impl->kernel.ApplicationProcess()->GetMemory();
|
||||
}
|
||||
|
||||
Tegra::GPU& System::GPU() {
|
||||
|
@ -116,7 +116,6 @@ class CpuManager;
|
||||
class Debugger;
|
||||
class DeviceMemory;
|
||||
class ExclusiveMonitor;
|
||||
class GPUDirtyMemoryManager;
|
||||
class PerfStats;
|
||||
class Reporter;
|
||||
class SpeedLimiter;
|
||||
@ -225,12 +224,6 @@ public:
|
||||
/// Prepare the core emulation for a reschedule
|
||||
void PrepareReschedule(u32 core_index);
|
||||
|
||||
/// Provides a reference to the gou dirty memory manager.
|
||||
[[nodiscard]] Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager();
|
||||
|
||||
/// Provides a constant reference to the current gou dirty memory manager.
|
||||
[[nodiscard]] const Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager() const;
|
||||
|
||||
void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
|
||||
|
||||
[[nodiscard]] size_t GetCurrentHostThreadID() const;
|
||||
@ -250,12 +243,6 @@ public:
|
||||
/// Gets a const reference to the underlying CPU manager
|
||||
[[nodiscard]] const CpuManager& GetCpuManager() const;
|
||||
|
||||
/// Gets a reference to the exclusive monitor
|
||||
[[nodiscard]] ExclusiveMonitor& Monitor();
|
||||
|
||||
/// Gets a constant reference to the exclusive monitor
|
||||
[[nodiscard]] const ExclusiveMonitor& Monitor() const;
|
||||
|
||||
/// Gets a mutable reference to the system memory instance.
|
||||
[[nodiscard]] Core::Memory::Memory& ApplicationMemory();
|
||||
|
||||
|
@ -166,6 +166,10 @@ u32 ProgramMetadata::GetSystemResourceSize() const {
|
||||
return npdm_header.system_resource_size;
|
||||
}
|
||||
|
||||
PoolPartition ProgramMetadata::GetPoolPartition() const {
|
||||
return acid_header.pool_partition;
|
||||
}
|
||||
|
||||
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
|
||||
return aci_kernel_capabilities;
|
||||
}
|
||||
@ -201,7 +205,7 @@ void ProgramMetadata::Print() const {
|
||||
// Begin ACID printing (potential perms, signed)
|
||||
LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data());
|
||||
LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags);
|
||||
LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO");
|
||||
LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.production_flag ? "YES" : "NO");
|
||||
LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
|
||||
LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
|
||||
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
|
||||
|
@ -34,6 +34,13 @@ enum class ProgramFilePermission : u64 {
|
||||
Everything = 1ULL << 63,
|
||||
};
|
||||
|
||||
enum class PoolPartition : u32 {
|
||||
Application = 0,
|
||||
Applet = 1,
|
||||
System = 2,
|
||||
SystemNonSecure = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper which implements an interface to parse Program Description Metadata (NPDM)
|
||||
* Data can either be loaded from a file path or with data and an offset into it.
|
||||
@ -72,6 +79,7 @@ public:
|
||||
u64 GetTitleID() const;
|
||||
u64 GetFilesystemPermissions() const;
|
||||
u32 GetSystemResourceSize() const;
|
||||
PoolPartition GetPoolPartition() const;
|
||||
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
|
||||
const std::array<u8, 0x10>& GetName() const {
|
||||
return npdm_header.application_name;
|
||||
@ -116,8 +124,9 @@ private:
|
||||
union {
|
||||
u32 flags;
|
||||
|
||||
BitField<0, 1, u32> is_retail;
|
||||
BitField<1, 31, u32> flags_unk;
|
||||
BitField<0, 1, u32> production_flag;
|
||||
BitField<1, 1, u32> unqualified_approval;
|
||||
BitField<2, 4, PoolPartition> pool_partition;
|
||||
};
|
||||
u64_le title_id_min;
|
||||
u64_le title_id_max;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_address_arbiter.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
@ -26,9 +27,9 @@ bool ReadFromUser(KernelCore& kernel, s32* out, KProcessAddress address) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address, s32 value) {
|
||||
auto& monitor = system.Monitor();
|
||||
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
|
||||
bool DecrementIfLessThan(KernelCore& kernel, s32* out, KProcessAddress address, s32 value) {
|
||||
auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
|
||||
const auto current_core = kernel.CurrentPhysicalCoreIndex();
|
||||
|
||||
// NOTE: If scheduler lock is not held here, interrupt disable is required.
|
||||
// KScopedInterruptDisable di;
|
||||
@ -66,10 +67,10 @@ bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateIfEqual(Core::System& system, s32* out, KProcessAddress address, s32 value,
|
||||
bool UpdateIfEqual(KernelCore& kernel, s32* out, KProcessAddress address, s32 value,
|
||||
s32 new_value) {
|
||||
auto& monitor = system.Monitor();
|
||||
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
|
||||
auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
|
||||
const auto current_core = kernel.CurrentPhysicalCoreIndex();
|
||||
|
||||
// NOTE: If scheduler lock is not held here, interrupt disable is required.
|
||||
// KScopedInterruptDisable di;
|
||||
@ -159,7 +160,7 @@ Result KAddressArbiter::SignalAndIncrementIfEqual(uint64_t addr, s32 value, s32
|
||||
|
||||
// Check the userspace value.
|
||||
s32 user_value{};
|
||||
R_UNLESS(UpdateIfEqual(m_system, std::addressof(user_value), addr, value, value + 1),
|
||||
R_UNLESS(UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, value + 1),
|
||||
ResultInvalidCurrentMemory);
|
||||
R_UNLESS(user_value == value, ResultInvalidState);
|
||||
|
||||
@ -219,7 +220,7 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32
|
||||
s32 user_value{};
|
||||
bool succeeded{};
|
||||
if (value != new_value) {
|
||||
succeeded = UpdateIfEqual(m_system, std::addressof(user_value), addr, value, new_value);
|
||||
succeeded = UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, new_value);
|
||||
} else {
|
||||
succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
|
||||
}
|
||||
@ -262,7 +263,7 @@ Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement,
|
||||
s32 user_value{};
|
||||
bool succeeded{};
|
||||
if (decrement) {
|
||||
succeeded = DecrementIfLessThan(m_system, std::addressof(user_value), addr, value);
|
||||
succeeded = DecrementIfLessThan(m_kernel, std::addressof(user_value), addr, value);
|
||||
} else {
|
||||
succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
|
||||
}
|
||||
|
@ -58,9 +58,8 @@ Result KClientPort::CreateSession(KClientSession** out) {
|
||||
KSession* session{};
|
||||
|
||||
// Reserve a new session from the resource limit.
|
||||
//! FIXME: we are reserving this from the wrong resource limit!
|
||||
KScopedResourceReservation session_reservation(
|
||||
m_kernel.ApplicationProcess()->GetResourceLimit(), LimitableResource::SessionCountMax);
|
||||
KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel),
|
||||
LimitableResource::SessionCountMax);
|
||||
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Allocate a session normally.
|
||||
|
@ -28,10 +28,10 @@ bool WriteToUser(KernelCore& kernel, KProcessAddress address, const u32* p) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateLockAtomic(Core::System& system, u32* out, KProcessAddress address, u32 if_zero,
|
||||
bool UpdateLockAtomic(KernelCore& kernel, u32* out, KProcessAddress address, u32 if_zero,
|
||||
u32 new_orr_mask) {
|
||||
auto& monitor = system.Monitor();
|
||||
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
|
||||
auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
|
||||
const auto current_core = kernel.CurrentPhysicalCoreIndex();
|
||||
|
||||
u32 expected{};
|
||||
|
||||
@ -208,7 +208,7 @@ void KConditionVariable::SignalImpl(KThread* thread) {
|
||||
// TODO(bunnei): We should call CanAccessAtomic(..) here.
|
||||
can_access = true;
|
||||
if (can_access) [[likely]] {
|
||||
UpdateLockAtomic(m_system, std::addressof(prev_tag), address, own_tag,
|
||||
UpdateLockAtomic(m_kernel, std::addressof(prev_tag), address, own_tag,
|
||||
Svc::HandleWaitMask);
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ public:
|
||||
public:
|
||||
explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
|
||||
|
||||
Result Initialize(s32 size) {
|
||||
Result Initialize(KProcess* owner, s32 size) {
|
||||
// Check that the table size is valid.
|
||||
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
|
||||
|
||||
@ -44,6 +44,7 @@ public:
|
||||
m_next_linear_id = MinLinearId;
|
||||
m_count = 0;
|
||||
m_free_head_index = -1;
|
||||
m_owner = owner;
|
||||
|
||||
// Free all entries.
|
||||
for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
|
||||
@ -90,8 +91,8 @@ public:
|
||||
// Handle pseudo-handles.
|
||||
if constexpr (std::derived_from<KProcess, T>) {
|
||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||
//! FIXME: this is the wrong process!
|
||||
auto* const cur_process = m_kernel.ApplicationProcess();
|
||||
// TODO: this should be the current process
|
||||
auto* const cur_process = m_owner;
|
||||
ASSERT(cur_process != nullptr);
|
||||
return cur_process;
|
||||
}
|
||||
@ -301,6 +302,7 @@ private:
|
||||
|
||||
private:
|
||||
KernelCore& m_kernel;
|
||||
KProcess* m_owner{};
|
||||
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
|
||||
std::array<KAutoObject*, MaxTableSize> m_objects{};
|
||||
mutable KSpinLock m_lock;
|
||||
|
@ -434,7 +434,7 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
|
||||
void KPageTableBase::Finalize() {
|
||||
auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
|
||||
if (Settings::IsFastmemEnabled()) {
|
||||
m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size);
|
||||
m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -5243,7 +5243,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
||||
// Unmap.
|
||||
R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
|
||||
cur_pages, 0, false, unmap_properties,
|
||||
OperationType::Unmap, true));
|
||||
OperationType::UnmapPhysical, true));
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
@ -5326,7 +5326,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
||||
// Map the papges.
|
||||
R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages,
|
||||
cur_pg, map_properties,
|
||||
OperationType::MapFirstGroup, false));
|
||||
OperationType::MapFirstGroupPhysical, false));
|
||||
}
|
||||
}
|
||||
|
||||
@ -5480,7 +5480,7 @@ Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size)
|
||||
|
||||
// Unmap.
|
||||
R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false,
|
||||
unmap_properties, OperationType::Unmap, false));
|
||||
unmap_properties, OperationType::UnmapPhysical, false));
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
@ -5655,7 +5655,10 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
|
||||
// or free them to the page list, and so it goes unused (along with page properties).
|
||||
|
||||
switch (operation) {
|
||||
case OperationType::Unmap: {
|
||||
case OperationType::Unmap:
|
||||
case OperationType::UnmapPhysical: {
|
||||
const bool separate_heap = operation == OperationType::UnmapPhysical;
|
||||
|
||||
// Ensure that any pages we track are closed on exit.
|
||||
KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
|
||||
SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
|
||||
@ -5664,7 +5667,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
|
||||
this->MakePageGroup(pages_to_close, virt_addr, num_pages);
|
||||
|
||||
// Unmap.
|
||||
m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize);
|
||||
m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize, separate_heap);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@ -5672,7 +5675,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
|
||||
ASSERT(virt_addr != 0);
|
||||
ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
|
||||
m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr,
|
||||
ConvertToMemoryPermission(properties.perm));
|
||||
ConvertToMemoryPermission(properties.perm), false);
|
||||
|
||||
// Open references to pages, if we should.
|
||||
if (this->IsHeapPhysicalAddress(phys_addr)) {
|
||||
@ -5711,16 +5714,19 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
|
||||
|
||||
switch (operation) {
|
||||
case OperationType::MapGroup:
|
||||
case OperationType::MapFirstGroup: {
|
||||
case OperationType::MapFirstGroup:
|
||||
case OperationType::MapFirstGroupPhysical: {
|
||||
const bool separate_heap = operation == OperationType::MapFirstGroupPhysical;
|
||||
|
||||
// We want to maintain a new reference to every page in the group.
|
||||
KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup);
|
||||
KScopedPageGroup spg(page_group, operation == OperationType::MapGroup);
|
||||
|
||||
for (const auto& node : page_group) {
|
||||
const size_t size{node.GetNumPages() * PageSize};
|
||||
|
||||
// Map the pages.
|
||||
m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
|
||||
ConvertToMemoryPermission(properties.perm));
|
||||
ConvertToMemoryPermission(properties.perm), separate_heap);
|
||||
|
||||
virt_addr += size;
|
||||
}
|
||||
|
@ -104,6 +104,9 @@ protected:
|
||||
ChangePermissionsAndRefresh = 5,
|
||||
ChangePermissionsAndRefreshAndFlush = 6,
|
||||
Separate = 7,
|
||||
|
||||
MapFirstGroupPhysical = 65000,
|
||||
UnmapPhysical = 65001,
|
||||
};
|
||||
|
||||
static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;
|
||||
|
@ -306,12 +306,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
|
||||
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
|
||||
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
|
||||
params.code_address, params.code_num_pages * PageSize,
|
||||
m_system_resource, res_limit, this->GetMemory(), 0));
|
||||
m_system_resource, res_limit, m_memory, 0));
|
||||
}
|
||||
ON_RESULT_FAILURE_2 {
|
||||
m_page_table.Finalize();
|
||||
};
|
||||
|
||||
// Ensure our memory is initialized.
|
||||
m_memory.SetCurrentPageTable(*this);
|
||||
m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
|
||||
|
||||
// Ensure we can insert the code region.
|
||||
R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize,
|
||||
KMemoryState::Code),
|
||||
@ -399,12 +403,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
|
||||
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
|
||||
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
|
||||
params.code_address, code_size, m_system_resource, res_limit,
|
||||
this->GetMemory(), aslr_space_start));
|
||||
m_memory, aslr_space_start));
|
||||
}
|
||||
ON_RESULT_FAILURE_2 {
|
||||
m_page_table.Finalize();
|
||||
};
|
||||
|
||||
// Ensure our memory is initialized.
|
||||
m_memory.SetCurrentPageTable(*this);
|
||||
m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
|
||||
|
||||
// Ensure we can insert the code region.
|
||||
R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code),
|
||||
ResultInvalidMemoryRegion);
|
||||
@ -1094,8 +1102,7 @@ void KProcess::UnpinThread(KThread* thread) {
|
||||
|
||||
Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids,
|
||||
s32 max_out_count) {
|
||||
// TODO: use current memory reference
|
||||
auto& memory = m_kernel.System().ApplicationMemory();
|
||||
auto& memory = this->GetMemory();
|
||||
|
||||
// Lock the list.
|
||||
KScopedLightLock lk(m_list_lock);
|
||||
@ -1128,14 +1135,15 @@ void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
|
||||
KProcess::KProcess(KernelCore& kernel)
|
||||
: KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
|
||||
m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
|
||||
m_handle_table{kernel} {}
|
||||
m_handle_table{kernel}, m_dirty_memory_managers{},
|
||||
m_exclusive_monitor{}, m_memory{kernel.System()} {}
|
||||
KProcess::~KProcess() = default;
|
||||
|
||||
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
|
||||
KProcessAddress aslr_space_start, bool is_hbl) {
|
||||
// Create a resource limit for the process.
|
||||
const auto physical_memory_size =
|
||||
m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
|
||||
const auto pool = static_cast<KMemoryManager::Pool>(metadata.GetPoolPartition());
|
||||
const auto physical_memory_size = m_kernel.MemoryManager().GetSize(pool);
|
||||
auto* res_limit =
|
||||
Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
|
||||
|
||||
@ -1146,8 +1154,10 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
Svc::CreateProcessFlag flag{};
|
||||
u64 code_address{};
|
||||
|
||||
// We are an application.
|
||||
flag |= Svc::CreateProcessFlag::IsApplication;
|
||||
// Determine if we are an application.
|
||||
if (pool == KMemoryManager::Pool::Application) {
|
||||
flag |= Svc::CreateProcessFlag::IsApplication;
|
||||
}
|
||||
|
||||
// If we are 64-bit, create as such.
|
||||
if (metadata.Is64BitProgram()) {
|
||||
@ -1196,8 +1206,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
std::memcpy(params.name.data(), name.data(), sizeof(params.name));
|
||||
|
||||
// Initialize for application process.
|
||||
R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit,
|
||||
KMemoryManager::Pool::Application, aslr_space_start));
|
||||
R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, pool,
|
||||
aslr_space_start));
|
||||
|
||||
// Assign remaining properties.
|
||||
m_is_hbl = is_hbl;
|
||||
@ -1223,22 +1233,25 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
|
||||
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
|
||||
|
||||
#ifdef HAS_NCE
|
||||
if (Settings::IsNceEnabled()) {
|
||||
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
||||
auto& buffer = m_kernel.System().DeviceMemory().buffer;
|
||||
const auto& code = code_set.CodeSegment();
|
||||
const auto& patch = code_set.PatchSegment();
|
||||
buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true);
|
||||
buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true);
|
||||
buffer.Protect(GetInteger(base_addr + code.addr), code.size,
|
||||
Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
|
||||
buffer.Protect(GetInteger(base_addr + patch.addr), patch.size,
|
||||
Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
|
||||
ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void KProcess::InitializeInterfaces() {
|
||||
this->GetMemory().SetCurrentPageTable(*this);
|
||||
m_exclusive_monitor =
|
||||
Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES);
|
||||
|
||||
#ifdef HAS_NCE
|
||||
if (this->Is64Bit() && Settings::IsNceEnabled()) {
|
||||
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
|
||||
}
|
||||
@ -1248,13 +1261,13 @@ void KProcess::InitializeInterfaces() {
|
||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic64>(
|
||||
m_kernel.System(), m_kernel.IsMulticore(), this,
|
||||
static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i);
|
||||
static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic32>(
|
||||
m_kernel.System(), m_kernel.IsMulticore(), this,
|
||||
static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i);
|
||||
static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1305,9 +1318,10 @@ bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointT
|
||||
return true;
|
||||
}
|
||||
|
||||
Core::Memory::Memory& KProcess::GetMemory() const {
|
||||
// TODO: per-process memory
|
||||
return m_kernel.System().ApplicationMemory();
|
||||
void KProcess::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
|
||||
for (auto& manager : m_dirty_memory_managers) {
|
||||
manager.Gather(callback);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/gpu_dirty_memory_manager.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/k_address_arbiter.h"
|
||||
#include "core/hle/kernel/k_capabilities.h"
|
||||
@ -17,6 +18,7 @@
|
||||
#include "core/hle/kernel/k_system_resource.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_local_page.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -126,6 +128,9 @@ private:
|
||||
#ifdef HAS_NCE
|
||||
std::unordered_map<u64, u64> m_post_handlers{};
|
||||
#endif
|
||||
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> m_dirty_memory_managers;
|
||||
std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor;
|
||||
Core::Memory::Memory m_memory;
|
||||
|
||||
private:
|
||||
Result StartTermination();
|
||||
@ -502,7 +507,15 @@ public:
|
||||
|
||||
void InitializeInterfaces();
|
||||
|
||||
Core::Memory::Memory& GetMemory() const;
|
||||
Core::Memory::Memory& GetMemory() {
|
||||
return m_memory;
|
||||
}
|
||||
|
||||
void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
|
||||
|
||||
Core::ExclusiveMonitor& GetExclusiveMonitor() const {
|
||||
return *m_exclusive_monitor;
|
||||
}
|
||||
|
||||
public:
|
||||
// Overridden parent functions.
|
||||
@ -539,7 +552,7 @@ private:
|
||||
|
||||
Result InitializeHandleTable(s32 size) {
|
||||
// Try to initialize the handle table.
|
||||
R_TRY(m_handle_table.Initialize(size));
|
||||
R_TRY(m_handle_table.Initialize(this, size));
|
||||
|
||||
// We succeeded, so note that we did.
|
||||
m_is_handle_table_initialized = true;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -49,14 +49,21 @@ public:
|
||||
bool IsSignaled() const override;
|
||||
void OnClientClosed();
|
||||
|
||||
/// TODO: flesh these out to match the real kernel
|
||||
Result OnRequest(KSessionRequest* request);
|
||||
Result SendReply(bool is_hle = false);
|
||||
Result ReceiveRequest(std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
|
||||
Result SendReply(uintptr_t server_message, uintptr_t server_buffer_size,
|
||||
KPhysicalAddress server_message_paddr, bool is_hle = false);
|
||||
Result ReceiveRequest(uintptr_t server_message, uintptr_t server_buffer_size,
|
||||
KPhysicalAddress server_message_paddr,
|
||||
std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
|
||||
std::weak_ptr<Service::SessionRequestManager> manager = {});
|
||||
|
||||
Result SendReplyHLE() {
|
||||
return SendReply(true);
|
||||
R_RETURN(this->SendReply(0, 0, 0, true));
|
||||
}
|
||||
|
||||
Result ReceiveRequestHLE(std::shared_ptr<Service::HLERequestContext>* out_context,
|
||||
std::weak_ptr<Service::SessionRequestManager> manager) {
|
||||
R_RETURN(this->ReceiveRequest(0, 0, 0, out_context, manager));
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -33,8 +33,7 @@ void KSession::Initialize(KClientPort* client_port, uintptr_t name) {
|
||||
m_name = name;
|
||||
|
||||
// Set our owner process.
|
||||
//! FIXME: this is the wrong process!
|
||||
m_process = m_kernel.ApplicationProcess();
|
||||
m_process = GetCurrentProcessPointer(m_kernel);
|
||||
m_process->Open();
|
||||
|
||||
// Set our port.
|
||||
|
@ -1422,8 +1422,7 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
|
||||
}
|
||||
|
||||
Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) {
|
||||
// TODO: per-process memory
|
||||
return kernel.System().ApplicationMemory();
|
||||
return GetCurrentProcess(kernel).GetMemory();
|
||||
}
|
||||
|
||||
KScopedDisableDispatch::~KScopedDisableDispatch() {
|
||||
|
@ -314,11 +314,7 @@ public:
|
||||
m_current_core_id = core;
|
||||
}
|
||||
|
||||
KProcess* GetOwnerProcess() {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
const KProcess* GetOwnerProcess() const {
|
||||
KProcess* GetOwnerProcess() const {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
|
@ -68,8 +68,6 @@ struct KernelCore::Impl {
|
||||
|
||||
global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
|
||||
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
||||
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
|
||||
global_handle_table->Initialize(KHandleTable::MaxTableSize);
|
||||
|
||||
is_phantom_mode_for_singlecore = false;
|
||||
|
||||
@ -121,13 +119,8 @@ struct KernelCore::Impl {
|
||||
next_user_process_id = KProcess::ProcessIdMin;
|
||||
next_thread_id = 1;
|
||||
|
||||
global_handle_table->Finalize();
|
||||
global_handle_table.reset();
|
||||
|
||||
preemption_event = nullptr;
|
||||
|
||||
exclusive_monitor.reset();
|
||||
|
||||
// Cleanup persistent kernel objects
|
||||
auto CleanupObject = [](KAutoObject* obj) {
|
||||
if (obj) {
|
||||
@ -191,8 +184,6 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
void InitializePhysicalCores() {
|
||||
exclusive_monitor =
|
||||
Core::MakeExclusiveMonitor(system.ApplicationMemory(), Core::Hardware::NUM_CPU_CORES);
|
||||
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
const s32 core{static_cast<s32>(i)};
|
||||
|
||||
@ -791,10 +782,6 @@ struct KernelCore::Impl {
|
||||
|
||||
std::shared_ptr<Core::Timing::EventType> preemption_event;
|
||||
|
||||
// This is the kernel's handle table or supervisor handle table which
|
||||
// stores all the objects in place.
|
||||
std::unique_ptr<KHandleTable> global_handle_table;
|
||||
|
||||
std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
|
||||
|
||||
std::unique_ptr<KObjectNameGlobalData> object_name_global_data;
|
||||
@ -805,7 +792,6 @@ struct KernelCore::Impl {
|
||||
std::mutex server_lock;
|
||||
std::vector<std::unique_ptr<Service::ServerManager>> server_managers;
|
||||
|
||||
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
|
||||
std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
|
||||
|
||||
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
|
||||
@ -882,10 +868,6 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() {
|
||||
return impl->system_resource_limit;
|
||||
}
|
||||
|
||||
KScopedAutoObject<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
|
||||
return impl->global_handle_table->GetObject<KThread>(handle);
|
||||
}
|
||||
|
||||
void KernelCore::AppendNewProcess(KProcess* process) {
|
||||
impl->process_list.push_back(process);
|
||||
}
|
||||
@ -959,14 +941,6 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
|
||||
return *impl->hardware_timer;
|
||||
}
|
||||
|
||||
Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
|
||||
return *impl->exclusive_monitor;
|
||||
}
|
||||
|
||||
const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
|
||||
return *impl->exclusive_monitor;
|
||||
}
|
||||
|
||||
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
|
||||
return *impl->global_object_list_container;
|
||||
}
|
||||
@ -1030,14 +1004,6 @@ u64 KernelCore::CreateNewUserProcessID() {
|
||||
return impl->next_user_process_id++;
|
||||
}
|
||||
|
||||
KHandleTable& KernelCore::GlobalHandleTable() {
|
||||
return *impl->global_handle_table;
|
||||
}
|
||||
|
||||
const KHandleTable& KernelCore::GlobalHandleTable() const {
|
||||
return *impl->global_handle_table;
|
||||
}
|
||||
|
||||
void KernelCore::RegisterCoreThread(std::size_t core_id) {
|
||||
impl->RegisterCoreThread(core_id);
|
||||
}
|
||||
|
@ -116,9 +116,6 @@ public:
|
||||
/// Retrieves a shared pointer to the system resource limit instance.
|
||||
KResourceLimit* GetSystemResourceLimit();
|
||||
|
||||
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
|
||||
KScopedAutoObject<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
|
||||
|
||||
/// Adds the given shared pointer to an internal list of active processes.
|
||||
void AppendNewProcess(KProcess* process);
|
||||
|
||||
@ -170,10 +167,6 @@ public:
|
||||
/// Stops execution of 'id' core, in order to reschedule a new thread.
|
||||
void PrepareReschedule(std::size_t id);
|
||||
|
||||
Core::ExclusiveMonitor& GetExclusiveMonitor();
|
||||
|
||||
const Core::ExclusiveMonitor& GetExclusiveMonitor() const;
|
||||
|
||||
KAutoObjectWithListContainer& ObjectListContainer();
|
||||
|
||||
const KAutoObjectWithListContainer& ObjectListContainer() const;
|
||||
|
@ -18,13 +18,13 @@ public:
|
||||
static constexpr inline u64 NullTag = 0;
|
||||
|
||||
public:
|
||||
enum class ReceiveListCountType : u32 {
|
||||
None = 0,
|
||||
ToMessageBuffer = 1,
|
||||
ToSingleBuffer = 2,
|
||||
enum ReceiveListCountType : u32 {
|
||||
ReceiveListCountType_None = 0,
|
||||
ReceiveListCountType_ToMessageBuffer = 1,
|
||||
ReceiveListCountType_ToSingleBuffer = 2,
|
||||
|
||||
CountOffset = 2,
|
||||
CountMax = 13,
|
||||
ReceiveListCountType_CountOffset = 2,
|
||||
ReceiveListCountType_CountMax = 13,
|
||||
};
|
||||
|
||||
private:
|
||||
@ -591,16 +591,16 @@ public:
|
||||
// Add the size of the receive list.
|
||||
const auto count = hdr.GetReceiveListCount();
|
||||
switch (count) {
|
||||
case MessageHeader::ReceiveListCountType::None:
|
||||
case MessageHeader::ReceiveListCountType_None:
|
||||
break;
|
||||
case MessageHeader::ReceiveListCountType::ToMessageBuffer:
|
||||
case MessageHeader::ReceiveListCountType_ToMessageBuffer:
|
||||
break;
|
||||
case MessageHeader::ReceiveListCountType::ToSingleBuffer:
|
||||
case MessageHeader::ReceiveListCountType_ToSingleBuffer:
|
||||
msg_size += ReceiveListEntry::GetDataSize();
|
||||
break;
|
||||
default:
|
||||
msg_size += (static_cast<s32>(count) -
|
||||
static_cast<s32>(MessageHeader::ReceiveListCountType::CountOffset)) *
|
||||
static_cast<s32>(MessageHeader::ReceiveListCountType_CountOffset)) *
|
||||
ReceiveListEntry::GetDataSize();
|
||||
break;
|
||||
}
|
||||
|
@ -118,7 +118,6 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::IsApplication:
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) Assuming process is application");
|
||||
*result = process->IsApplication();
|
||||
R_SUCCEED();
|
||||
|
||||
|
@ -48,8 +48,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
|
||||
};
|
||||
|
||||
// Send the reply.
|
||||
R_TRY(session->SendReply());
|
||||
// R_TRY(session->SendReply(message, buffer_size, message_paddr));
|
||||
R_TRY(session->SendReply(message, buffer_size, message_paddr));
|
||||
}
|
||||
|
||||
// Receive a message.
|
||||
@ -85,8 +84,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
|
||||
if (R_SUCCEEDED(result)) {
|
||||
KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
|
||||
if (session != nullptr) {
|
||||
// result = session->ReceiveRequest(message, buffer_size, message_paddr);
|
||||
result = session->ReceiveRequest();
|
||||
result = session->ReceiveRequest(message, buffer_size, message_paddr);
|
||||
if (ResultNotFound == result) {
|
||||
continue;
|
||||
}
|
||||
|
@ -38,7 +38,9 @@ constexpr Result ResultInvalidState{ErrorModule::Kernel, 125};
|
||||
constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126};
|
||||
constexpr Result ResultPortClosed{ErrorModule::Kernel, 131};
|
||||
constexpr Result ResultLimitReached{ErrorModule::Kernel, 132};
|
||||
constexpr Result ResultReceiveListBroken{ErrorModule::Kernel, 258};
|
||||
constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259};
|
||||
constexpr Result ResultMessageTooLarge{ErrorModule::Kernel, 260};
|
||||
constexpr Result ResultInvalidId{ErrorModule::Kernel, 519};
|
||||
|
||||
} // namespace Kernel
|
||||
|
@ -89,7 +89,7 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
|
||||
crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
|
||||
crash_report += fmt::format(" FAR: {:016x}\n", info.far);
|
||||
crash_report += "\nBacktrace:\n";
|
||||
for (size_t i = 0; i < info.backtrace_size; i++) {
|
||||
for (u32 i = 0; i < std::min<u32>(info.backtrace_size, 32); i++) {
|
||||
crash_report +=
|
||||
fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
|
||||
}
|
||||
|
@ -151,8 +151,8 @@ public:
|
||||
if (manager->IsDomain()) {
|
||||
context->AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
kernel.ApplicationProcess()->GetResourceLimit()->Reserve(
|
||||
Kernel::LimitableResource::SessionCountMax, 1);
|
||||
ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(
|
||||
Kernel::LimitableResource::SessionCountMax, 1));
|
||||
|
||||
auto* session = Kernel::KSession::Create(kernel);
|
||||
session->Initialize(nullptr, 0);
|
||||
|
@ -47,7 +47,7 @@ ServerManager::~ServerManager() {
|
||||
m_stopped.Wait();
|
||||
m_threads.clear();
|
||||
|
||||
// Clean up ports.
|
||||
// Clean up server ports.
|
||||
for (const auto& [port, handler] : m_ports) {
|
||||
port->Close();
|
||||
}
|
||||
@ -97,22 +97,15 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
|
||||
u32 max_sessions) {
|
||||
ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
|
||||
|
||||
// Add the new server to sm:.
|
||||
ASSERT(R_SUCCEEDED(
|
||||
m_system.ServiceManager().RegisterService(service_name, max_sessions, handler_factory)));
|
||||
|
||||
// Get the registered port.
|
||||
Kernel::KPort* port{};
|
||||
ASSERT(
|
||||
R_SUCCEEDED(m_system.ServiceManager().GetServicePort(std::addressof(port), service_name)));
|
||||
|
||||
// Open a new reference to the server port.
|
||||
port->GetServerPort().Open();
|
||||
// Add the new server to sm: and get the moved server port.
|
||||
Kernel::KServerPort* server_port{};
|
||||
R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name,
|
||||
max_sessions, handler_factory));
|
||||
|
||||
// Begin tracking the server port.
|
||||
{
|
||||
std::scoped_lock ll{m_list_mutex};
|
||||
m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory));
|
||||
m_ports.emplace(server_port, std::move(handler_factory));
|
||||
}
|
||||
|
||||
// Signal the wakeup event.
|
||||
@ -372,7 +365,7 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
|
||||
|
||||
// Try to receive a message.
|
||||
std::shared_ptr<HLERequestContext> context;
|
||||
rc = session->ReceiveRequest(&context, manager);
|
||||
rc = session->ReceiveRequestHLE(&context, manager);
|
||||
|
||||
// If the session has been closed, we're done.
|
||||
if (rc == Kernel::ResultSessionClosed) {
|
||||
|
@ -507,6 +507,14 @@ void SET_SYS::SetTvSettings(HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void SET_SYS::GetDebugModeFlag(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
void SET_SYS::GetQuestFlag(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_SET, "(STUBBED) called");
|
||||
|
||||
@ -926,7 +934,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"},
|
||||
{59, &SET_SYS::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"},
|
||||
{60, &SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{61, &SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{62, nullptr, "GetDebugModeFlag"},
|
||||
{62, &SET_SYS::GetDebugModeFlag, "GetDebugModeFlag"},
|
||||
{63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"},
|
||||
{64, nullptr, "SetPrimaryAlbumStorage"},
|
||||
{65, nullptr, "GetUsb30EnableFlag"},
|
||||
@ -1143,6 +1151,8 @@ void SET_SYS::StoreSettings() {
|
||||
}
|
||||
|
||||
void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("SettingsStore");
|
||||
|
||||
while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) {
|
||||
std::scoped_lock l{m_save_needed_mutex};
|
||||
if (!std::exchange(m_save_needed, false)) {
|
||||
|
@ -98,6 +98,7 @@ private:
|
||||
void GetSettingsItemValue(HLERequestContext& ctx);
|
||||
void GetTvSettings(HLERequestContext& ctx);
|
||||
void SetTvSettings(HLERequestContext& ctx);
|
||||
void GetDebugModeFlag(HLERequestContext& ctx);
|
||||
void GetQuestFlag(HLERequestContext& ctx);
|
||||
void GetDeviceTimeZoneLocationName(HLERequestContext& ctx);
|
||||
void SetDeviceTimeZoneLocationName(HLERequestContext& ctx);
|
||||
|
@ -29,8 +29,7 @@ ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {
|
||||
|
||||
ServiceManager::~ServiceManager() {
|
||||
for (auto& [name, port] : service_ports) {
|
||||
port->GetClientPort().Close();
|
||||
port->GetServerPort().Close();
|
||||
port->Close();
|
||||
}
|
||||
|
||||
if (deferral_event) {
|
||||
@ -50,8 +49,8 @@ static Result ValidateServiceName(const std::string& name) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
|
||||
SessionRequestHandlerFactory handler) {
|
||||
Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, std::string name,
|
||||
u32 max_sessions, SessionRequestHandlerFactory handler) {
|
||||
R_TRY(ValidateServiceName(name));
|
||||
|
||||
std::scoped_lock lk{lock};
|
||||
@ -66,13 +65,17 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
|
||||
// Register the port.
|
||||
Kernel::KPort::Register(kernel, port);
|
||||
|
||||
service_ports.emplace(name, port);
|
||||
service_ports.emplace(name, std::addressof(port->GetClientPort()));
|
||||
registered_services.emplace(name, handler);
|
||||
if (deferral_event) {
|
||||
deferral_event->Signal();
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
// Set our output.
|
||||
*out_server_port = std::addressof(port->GetServerPort());
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ServiceManager::UnregisterService(const std::string& name) {
|
||||
@ -91,7 +94,8 @@ Result ServiceManager::UnregisterService(const std::string& name) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::string& name) {
|
||||
Result ServiceManager::GetServicePort(Kernel::KClientPort** out_client_port,
|
||||
const std::string& name) {
|
||||
R_TRY(ValidateServiceName(name));
|
||||
|
||||
std::scoped_lock lk{lock};
|
||||
@ -101,7 +105,7 @@ Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::strin
|
||||
return Service::SM::ResultNotRegistered;
|
||||
}
|
||||
|
||||
*out_port = it->second;
|
||||
*out_client_port = it->second;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@ -172,8 +176,8 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
|
||||
std::string name(PopServiceName(rp));
|
||||
|
||||
// Find the named port.
|
||||
Kernel::KPort* port{};
|
||||
auto port_result = service_manager.GetServicePort(&port, name);
|
||||
Kernel::KClientPort* client_port{};
|
||||
auto port_result = service_manager.GetServicePort(&client_port, name);
|
||||
if (port_result == Service::SM::ResultInvalidServiceName) {
|
||||
LOG_ERROR(Service_SM, "Invalid service name '{}'", name);
|
||||
return Service::SM::ResultInvalidServiceName;
|
||||
@ -187,7 +191,7 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
|
||||
|
||||
// Create a new session.
|
||||
Kernel::KClientSession* session{};
|
||||
if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) {
|
||||
if (const auto result = client_port->CreateSession(&session); result.IsError()) {
|
||||
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
|
||||
return result;
|
||||
}
|
||||
@ -221,7 +225,9 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
|
||||
LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
|
||||
max_session_count, is_light);
|
||||
|
||||
if (const auto result = service_manager.RegisterService(name, max_session_count, nullptr);
|
||||
Kernel::KServerPort* server_port{};
|
||||
if (const auto result = service_manager.RegisterService(std::addressof(server_port), name,
|
||||
max_session_count, nullptr);
|
||||
result.IsError()) {
|
||||
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@ -229,13 +235,9 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
|
||||
return;
|
||||
}
|
||||
|
||||
auto* port = Kernel::KPort::Create(kernel);
|
||||
port->Initialize(ServerSessionCountMax, is_light, 0);
|
||||
SCOPE_EXIT({ port->GetClientPort().Close(); });
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushMoveObjects(port->GetServerPort());
|
||||
rb.PushMoveObjects(server_port);
|
||||
}
|
||||
|
||||
void SM::UnregisterService(HLERequestContext& ctx) {
|
||||
|
@ -56,10 +56,10 @@ public:
|
||||
explicit ServiceManager(Kernel::KernelCore& kernel_);
|
||||
~ServiceManager();
|
||||
|
||||
Result RegisterService(std::string name, u32 max_sessions,
|
||||
SessionRequestHandlerFactory handler_factory);
|
||||
Result RegisterService(Kernel::KServerPort** out_server_port, std::string name,
|
||||
u32 max_sessions, SessionRequestHandlerFactory handler_factory);
|
||||
Result UnregisterService(const std::string& name);
|
||||
Result GetServicePort(Kernel::KPort** out_port, const std::string& name);
|
||||
Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
|
||||
|
||||
template <Common::DerivedFrom<SessionRequestHandler> T>
|
||||
std::shared_ptr<T> GetService(const std::string& service_name) const {
|
||||
@ -84,7 +84,7 @@ private:
|
||||
/// Map of registered services, retrieved using GetServicePort.
|
||||
std::mutex lock;
|
||||
std::unordered_map<std::string, SessionRequestHandlerFactory> registered_services;
|
||||
std::unordered_map<std::string, Kernel::KPort*> service_ports;
|
||||
std::unordered_map<std::string, Kernel::KClientPort*> service_ports;
|
||||
|
||||
/// Kernel context
|
||||
Kernel::KernelCore& kernel;
|
||||
|
@ -28,7 +28,6 @@ void Controller::ConvertCurrentObjectToDomain(HLERequestContext& ctx) {
|
||||
void Controller::CloneCurrentObject(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service, "called");
|
||||
|
||||
auto& process = *ctx.GetThread().GetOwnerProcess();
|
||||
auto session_manager = ctx.GetManager();
|
||||
|
||||
// FIXME: this is duplicated from the SVC, it should just call it instead
|
||||
@ -36,11 +35,11 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
|
||||
|
||||
// Reserve a new session from the process resource limit.
|
||||
Kernel::KScopedResourceReservation session_reservation(
|
||||
&process, Kernel::LimitableResource::SessionCountMax);
|
||||
Kernel::GetCurrentProcessPointer(kernel), Kernel::LimitableResource::SessionCountMax);
|
||||
ASSERT(session_reservation.Succeeded());
|
||||
|
||||
// Create the session.
|
||||
Kernel::KSession* session = Kernel::KSession::Create(system.Kernel());
|
||||
Kernel::KSession* session = Kernel::KSession::Create(kernel);
|
||||
ASSERT(session != nullptr);
|
||||
|
||||
// Initialize the session.
|
||||
@ -50,7 +49,7 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
|
||||
session_reservation.Commit();
|
||||
|
||||
// Register the session.
|
||||
Kernel::KSession::Register(system.Kernel(), session);
|
||||
Kernel::KSession::Register(kernel, session);
|
||||
|
||||
// Register with server manager.
|
||||
session_manager->GetServerManager().RegisterSession(&session->GetServerSession(),
|
||||
|
@ -129,9 +129,10 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
||||
}
|
||||
metadata.Print();
|
||||
|
||||
// Enable NCE only for programs with 39-bit address space.
|
||||
// Enable NCE only for applications with 39-bit address space.
|
||||
const bool is_39bit =
|
||||
metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit;
|
||||
const bool is_application = metadata.GetPoolPartition() == FileSys::PoolPartition::Application;
|
||||
Settings::SetNceEnabled(is_39bit);
|
||||
|
||||
const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
|
||||
@ -147,7 +148,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
||||
|
||||
const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* {
|
||||
#ifdef HAS_NCE
|
||||
if (Settings::IsNceEnabled()) {
|
||||
if (is_application && Settings::IsNceEnabled()) {
|
||||
return &module_patchers[i];
|
||||
}
|
||||
#endif
|
||||
@ -175,7 +176,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
||||
|
||||
// Enable direct memory mapping in case of NCE.
|
||||
const u64 fastmem_base = [&]() -> size_t {
|
||||
if (Settings::IsNceEnabled()) {
|
||||
if (is_application && Settings::IsNceEnabled()) {
|
||||
auto& buffer = system.DeviceMemory().buffer;
|
||||
buffer.EnableDirectMappedAddress();
|
||||
return reinterpret_cast<u64>(buffer.VirtualBasePointer());
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/atomic_ops.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/heap_tracker.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/page_table.h"
|
||||
#include "common/scope_exit.h"
|
||||
@ -45,11 +46,25 @@ struct Memory::Impl {
|
||||
|
||||
void SetCurrentPageTable(Kernel::KProcess& process) {
|
||||
current_page_table = &process.GetPageTable().GetImpl();
|
||||
current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
|
||||
|
||||
if (std::addressof(process) == system.ApplicationProcess() &&
|
||||
Settings::IsFastmemEnabled()) {
|
||||
current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
|
||||
} else {
|
||||
current_page_table->fastmem_arena = nullptr;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
heap_tracker.emplace(system.DeviceMemory().buffer);
|
||||
buffer = std::addressof(*heap_tracker);
|
||||
#else
|
||||
buffer = std::addressof(system.DeviceMemory().buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
||||
Common::PhysicalAddress target, Common::MemoryPermission perms) {
|
||||
Common::PhysicalAddress target, Common::MemoryPermission perms,
|
||||
bool separate_heap) {
|
||||
ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
|
||||
ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
|
||||
ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}",
|
||||
@ -57,20 +72,21 @@ struct Memory::Impl {
|
||||
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target,
|
||||
Common::PageType::Memory);
|
||||
|
||||
if (Settings::IsFastmemEnabled()) {
|
||||
system.DeviceMemory().buffer.Map(GetInteger(base),
|
||||
GetInteger(target) - DramMemoryMap::Base, size, perms);
|
||||
if (current_page_table->fastmem_arena) {
|
||||
buffer->Map(GetInteger(base), GetInteger(target) - DramMemoryMap::Base, size, perms,
|
||||
separate_heap);
|
||||
}
|
||||
}
|
||||
|
||||
void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) {
|
||||
void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
||||
bool separate_heap) {
|
||||
ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
|
||||
ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
|
||||
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
|
||||
Common::PageType::Unmapped);
|
||||
|
||||
if (Settings::IsFastmemEnabled()) {
|
||||
system.DeviceMemory().buffer.Unmap(GetInteger(base), size);
|
||||
if (current_page_table->fastmem_arena) {
|
||||
buffer->Unmap(GetInteger(base), size, separate_heap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,17 +95,7 @@ struct Memory::Impl {
|
||||
ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
|
||||
ASSERT_MSG((vaddr & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", vaddr);
|
||||
|
||||
if (!Settings::IsFastmemEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool is_r = True(perms & Common::MemoryPermission::Read);
|
||||
const bool is_w = True(perms & Common::MemoryPermission::Write);
|
||||
const bool is_x =
|
||||
True(perms & Common::MemoryPermission::Execute) && Settings::IsNceEnabled();
|
||||
|
||||
if (!current_page_table) {
|
||||
system.DeviceMemory().buffer.Protect(vaddr, size, is_r, is_w, is_x);
|
||||
if (!current_page_table->fastmem_arena) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -101,8 +107,7 @@ struct Memory::Impl {
|
||||
switch (page_type) {
|
||||
case Common::PageType::RasterizerCachedMemory:
|
||||
if (protect_bytes > 0) {
|
||||
system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w,
|
||||
is_x);
|
||||
buffer->Protect(protect_begin, protect_bytes, perms);
|
||||
protect_bytes = 0;
|
||||
}
|
||||
break;
|
||||
@ -115,7 +120,7 @@ struct Memory::Impl {
|
||||
}
|
||||
|
||||
if (protect_bytes > 0) {
|
||||
system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, is_x);
|
||||
buffer->Protect(protect_begin, protect_bytes, perms);
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,7 +244,7 @@ struct Memory::Impl {
|
||||
|
||||
bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped,
|
||||
auto on_memory, auto on_rasterizer, auto increment) {
|
||||
const auto& page_table = system.ApplicationProcess()->GetPageTable().GetImpl();
|
||||
const auto& page_table = *current_page_table;
|
||||
std::size_t remaining_size = size;
|
||||
std::size_t page_index = addr >> YUZU_PAGEBITS;
|
||||
std::size_t page_offset = addr & YUZU_PAGEMASK;
|
||||
@ -484,8 +489,10 @@ struct Memory::Impl {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Settings::IsFastmemEnabled()) {
|
||||
system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug);
|
||||
if (current_page_table->fastmem_arena) {
|
||||
const auto perm{debug ? Common::MemoryPermission{}
|
||||
: Common::MemoryPermission::ReadWrite};
|
||||
buffer->Protect(vaddr, size, perm);
|
||||
}
|
||||
|
||||
// Iterate over a contiguous CPU address space, marking/unmarking the region.
|
||||
@ -541,10 +548,15 @@ struct Memory::Impl {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Settings::IsFastmemEnabled()) {
|
||||
const bool is_read_enable =
|
||||
!Settings::values.use_reactive_flushing.GetValue() || !cached;
|
||||
system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
|
||||
if (current_page_table->fastmem_arena) {
|
||||
Common::MemoryPermission perm{};
|
||||
if (!Settings::values.use_reactive_flushing.GetValue() || !cached) {
|
||||
perm |= Common::MemoryPermission::Read;
|
||||
}
|
||||
if (!cached) {
|
||||
perm |= Common::MemoryPermission::Write;
|
||||
}
|
||||
buffer->Protect(vaddr, size, perm);
|
||||
}
|
||||
|
||||
// Iterate over a contiguous CPU address space, which corresponds to the specified GPU
|
||||
@ -718,6 +730,17 @@ struct Memory::Impl {
|
||||
GetInteger(vaddr), []() {}, []() {});
|
||||
}
|
||||
|
||||
void FixPageProtection(u64 vaddr) {
|
||||
vaddr = Common::AlignDown(vaddr, YUZU_PAGESIZE);
|
||||
|
||||
if (!AddressSpaceContains(*current_page_table, vaddr, 1)) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
ProtectRegion(*current_page_table, vaddr, YUZU_PAGESIZE,
|
||||
Common::MemoryPermission::ReadWrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a particular data type out of memory at the given virtual address.
|
||||
*
|
||||
@ -855,6 +878,13 @@ struct Memory::Impl {
|
||||
std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
|
||||
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
|
||||
std::mutex sys_core_guard;
|
||||
|
||||
std::optional<Common::HeapTracker> heap_tracker;
|
||||
#ifdef __linux__
|
||||
Common::HeapTracker* buffer{};
|
||||
#else
|
||||
Common::HostMemory* buffer{};
|
||||
#endif
|
||||
};
|
||||
|
||||
Memory::Memory(Core::System& system_) : system{system_} {
|
||||
@ -872,12 +902,14 @@ void Memory::SetCurrentPageTable(Kernel::KProcess& process) {
|
||||
}
|
||||
|
||||
void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
||||
Common::PhysicalAddress target, Common::MemoryPermission perms) {
|
||||
impl->MapMemoryRegion(page_table, base, size, target, perms);
|
||||
Common::PhysicalAddress target, Common::MemoryPermission perms,
|
||||
bool separate_heap) {
|
||||
impl->MapMemoryRegion(page_table, base, size, target, perms, separate_heap);
|
||||
}
|
||||
|
||||
void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) {
|
||||
impl->UnmapRegion(page_table, base, size);
|
||||
void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
||||
bool separate_heap) {
|
||||
impl->UnmapRegion(page_table, base, size, separate_heap);
|
||||
}
|
||||
|
||||
void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size,
|
||||
@ -886,8 +918,7 @@ void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress
|
||||
}
|
||||
|
||||
bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
|
||||
const Kernel::KProcess& process = *system.ApplicationProcess();
|
||||
const auto& page_table = process.GetPageTable().GetImpl();
|
||||
const auto& page_table = *impl->current_page_table;
|
||||
const size_t page = vaddr >> YUZU_PAGEBITS;
|
||||
if (page >= page_table.pointers.size()) {
|
||||
return false;
|
||||
@ -1048,7 +1079,9 @@ void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
|
||||
}
|
||||
|
||||
bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
||||
bool mapped = true;
|
||||
[[maybe_unused]] bool mapped = true;
|
||||
[[maybe_unused]] bool rasterizer = false;
|
||||
|
||||
u8* const ptr = impl->GetPointerImpl(
|
||||
GetInteger(vaddr),
|
||||
[&] {
|
||||
@ -1056,8 +1089,31 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
||||
GetInteger(vaddr));
|
||||
mapped = false;
|
||||
},
|
||||
[&] { impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); });
|
||||
return mapped && ptr != nullptr;
|
||||
[&] {
|
||||
impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size);
|
||||
rasterizer = true;
|
||||
});
|
||||
|
||||
const bool mapping_exists = mapped && ptr != nullptr;
|
||||
|
||||
#ifdef __linux__
|
||||
if (mapping_exists && !rasterizer) {
|
||||
if (!impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr))) {
|
||||
// GPU may have raced reprotecting this page, try to fix it.
|
||||
impl->FixPageProtection(GetInteger(vaddr));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return mapping_exists;
|
||||
}
|
||||
|
||||
bool Memory::InvalidateSeparateHeap(void* fault_address) {
|
||||
#ifdef __linux__
|
||||
return impl->buffer->DeferredMapSeparateHeap(static_cast<u8*>(fault_address));
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Core::Memory
|
||||
|
@ -86,7 +86,8 @@ public:
|
||||
* @param perms The permissions to map the memory with.
|
||||
*/
|
||||
void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
||||
Common::PhysicalAddress target, Common::MemoryPermission perms);
|
||||
Common::PhysicalAddress target, Common::MemoryPermission perms,
|
||||
bool separate_heap);
|
||||
|
||||
/**
|
||||
* Unmaps a region of the emulated process address space.
|
||||
@ -95,7 +96,8 @@ public:
|
||||
* @param base The address to begin unmapping at.
|
||||
* @param size The amount of bytes to unmap.
|
||||
*/
|
||||
void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size);
|
||||
void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
||||
bool separate_heap);
|
||||
|
||||
/**
|
||||
* Protects a region of the emulated process address space with the new permissions.
|
||||
@ -486,6 +488,7 @@ public:
|
||||
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
|
||||
void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
|
||||
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
|
||||
bool InvalidateSeparateHeap(void* fault_address);
|
||||
void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
|
||||
|
||||
private:
|
||||
|
@ -74,11 +74,6 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
||||
case IR::Attribute::ClipDistance7: {
|
||||
const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||
const u32 index{static_cast<u32>(attr) - base};
|
||||
if (index >= ctx.profile.max_user_clip_distances) {
|
||||
LOG_WARNING(Shader, "Ignoring clip distance store {} >= {} supported", index,
|
||||
ctx.profile.max_user_clip_distances);
|
||||
return std::nullopt;
|
||||
}
|
||||
const Id clip_num{ctx.Const(index)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
|
||||
}
|
||||
|
@ -1532,8 +1532,7 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
||||
if (stage == Stage::Fragment) {
|
||||
throw NotImplementedException("Storing ClipDistance in fragment stage");
|
||||
}
|
||||
const Id type{TypeArray(
|
||||
F32[1], Const(std::min(info.used_clip_distances, profile.max_user_clip_distances)))};
|
||||
const Id type{TypeArray(F32[1], Const(8U))};
|
||||
clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
|
||||
}
|
||||
if (info.stores[IR::Attribute::Layer] &&
|
||||
|
@ -913,11 +913,7 @@ void GatherInfoFromHeader(Environment& env, Info& info) {
|
||||
}
|
||||
for (size_t index = 0; index < 8; ++index) {
|
||||
const u16 mask{header.vtg.omap_systemc.clip_distances};
|
||||
const bool used{((mask >> index) & 1) != 0};
|
||||
info.stores.Set(IR::Attribute::ClipDistance0 + index, used);
|
||||
if (used) {
|
||||
info.used_clip_distances = static_cast<u32>(index) + 1;
|
||||
}
|
||||
info.stores.Set(IR::Attribute::ClipDistance0 + index, ((mask >> index) & 1) != 0);
|
||||
}
|
||||
info.stores.Set(IR::Attribute::PrimitiveId,
|
||||
header.vtg.omap_systemb.primitive_array_id != 0);
|
||||
|
@ -87,8 +87,6 @@ struct Profile {
|
||||
bool has_broken_robust{};
|
||||
|
||||
u64 min_ssbo_alignment{};
|
||||
|
||||
u32 max_user_clip_distances{};
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
@ -324,8 +324,6 @@ struct Info {
|
||||
bool requires_layer_emulation{};
|
||||
IR::Attribute emulated_layer{};
|
||||
|
||||
u32 used_clip_distances{};
|
||||
|
||||
boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
|
||||
constant_buffer_descriptors;
|
||||
boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors;
|
||||
|
@ -12,6 +12,7 @@ using namespace Common::Literals;
|
||||
static constexpr size_t VIRTUAL_SIZE = 1ULL << 39;
|
||||
static constexpr size_t BACKING_SIZE = 4_GiB;
|
||||
static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
|
||||
static constexpr auto HEAP = false;
|
||||
|
||||
TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
|
||||
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
|
||||
@ -20,7 +21,7 @@ TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Simple map", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x5000, 0x8000, 0x1000, PERMS);
|
||||
mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||
data[0] = 50;
|
||||
@ -29,8 +30,8 @@ TEST_CASE("HostMemory: Simple map", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Simple mirror map", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS);
|
||||
mem.Map(0x8000, 0x4000, 0x1000, PERMS);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
|
||||
mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000;
|
||||
volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000;
|
||||
@ -40,116 +41,116 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Simple unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||
data[75] = 50;
|
||||
REQUIRE(data[75] == 50);
|
||||
|
||||
mem.Unmap(0x5000, 0x2000);
|
||||
mem.Unmap(0x5000, 0x2000, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||
data[0] = 50;
|
||||
REQUIRE(data[0] == 50);
|
||||
|
||||
mem.Unmap(0x5000, 0x2000);
|
||||
mem.Unmap(0x5000, 0x2000, HEAP);
|
||||
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS);
|
||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
|
||||
REQUIRE(data[0] == 50);
|
||||
|
||||
mem.Map(0x7000, 0x2000, 0x5000, PERMS);
|
||||
mem.Map(0x7000, 0x2000, 0x5000, PERMS, HEAP);
|
||||
REQUIRE(data[0x3000] == 50);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Nieche allocation", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x0000, 0, 0x20000, PERMS);
|
||||
mem.Unmap(0x0000, 0x4000);
|
||||
mem.Map(0x1000, 0, 0x2000, PERMS);
|
||||
mem.Map(0x3000, 0, 0x1000, PERMS);
|
||||
mem.Map(0, 0, 0x1000, PERMS);
|
||||
mem.Map(0x0000, 0, 0x20000, PERMS, HEAP);
|
||||
mem.Unmap(0x0000, 0x4000, HEAP);
|
||||
mem.Map(0x1000, 0, 0x2000, PERMS, HEAP);
|
||||
mem.Map(0x3000, 0, 0x1000, PERMS, HEAP);
|
||||
mem.Map(0, 0, 0x1000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Full unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS);
|
||||
mem.Unmap(0x8000, 0x4000);
|
||||
mem.Map(0x6000, 0, 0x16000, PERMS);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x8000, 0x4000, HEAP);
|
||||
mem.Map(0x6000, 0, 0x16000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS);
|
||||
mem.Unmap(0x2000, 0x4000);
|
||||
mem.Map(0x2000, 0x80000, 0x4000, PERMS);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x2000, 0x4000, HEAP);
|
||||
mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS);
|
||||
mem.Unmap(0x6000, 0x4000);
|
||||
mem.Map(0x8000, 0, 0x2000, PERMS);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x6000, 0x4000, HEAP);
|
||||
mem.Map(0x8000, 0, 0x2000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS);
|
||||
mem.Map(0x4000, 0, 0x1b000, PERMS);
|
||||
mem.Unmap(0x3000, 0x1c000);
|
||||
mem.Map(0x3000, 0, 0x20000, PERMS);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP);
|
||||
mem.Unmap(0x3000, 0x1c000, HEAP);
|
||||
mem.Map(0x3000, 0, 0x20000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS);
|
||||
mem.Unmap(0x2000, 0x4000);
|
||||
mem.Map(0x2000, 0, 0x4000, PERMS);
|
||||
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x2000, 0x4000, HEAP);
|
||||
mem.Map(0x2000, 0, 0x4000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Unmap to origin", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS);
|
||||
mem.Unmap(0x4000, 0x4000);
|
||||
mem.Map(0, 0, 0x4000, PERMS);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x4000, 0x4000, HEAP);
|
||||
mem.Map(0, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Unmap to right", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS);
|
||||
mem.Unmap(0x8000, 0x4000);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS);
|
||||
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
mem.Unmap(0x8000, 0x4000, HEAP);
|
||||
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||
ptr[0x1000] = 17;
|
||||
|
||||
mem.Unmap(0x6000, 0x2000);
|
||||
mem.Unmap(0x6000, 0x2000, HEAP);
|
||||
|
||||
REQUIRE(ptr[0x1000] == 17);
|
||||
}
|
||||
|
||||
TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||
ptr[0x3000] = 19;
|
||||
ptr[0x3fff] = 12;
|
||||
|
||||
mem.Unmap(0x4000, 0x2000);
|
||||
mem.Unmap(0x4000, 0x2000, HEAP);
|
||||
|
||||
REQUIRE(ptr[0x3000] == 19);
|
||||
REQUIRE(ptr[0x3fff] == 12);
|
||||
@ -157,13 +158,13 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS);
|
||||
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||
ptr[0x0000] = 19;
|
||||
ptr[0x3fff] = 12;
|
||||
|
||||
mem.Unmap(0x1000, 0x2000);
|
||||
mem.Unmap(0x1000, 0x2000, HEAP);
|
||||
|
||||
REQUIRE(ptr[0x0000] == 19);
|
||||
REQUIRE(ptr[0x3fff] == 12);
|
||||
@ -171,14 +172,14 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
|
||||
|
||||
TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
|
||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||
mem.Map(0x4000, 0x10000, 0x2000, PERMS);
|
||||
mem.Map(0x6000, 0x20000, 0x2000, PERMS);
|
||||
mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP);
|
||||
mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP);
|
||||
|
||||
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||
ptr[0x0000] = 19;
|
||||
ptr[0x3fff] = 12;
|
||||
|
||||
mem.Unmap(0x5000, 0x2000);
|
||||
mem.Unmap(0x5000, 0x2000, HEAP);
|
||||
|
||||
REQUIRE(ptr[0x0000] == 19);
|
||||
REQUIRE(ptr[0x3fff] == 12);
|
||||
|
@ -233,7 +233,6 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
|
||||
.ignore_nan_fp_comparisons = true,
|
||||
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
|
||||
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
|
||||
.max_user_clip_distances = 8,
|
||||
},
|
||||
host_info{
|
||||
.support_float64 = true,
|
||||
|
@ -374,7 +374,6 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
|
||||
.has_broken_robust =
|
||||
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
|
||||
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
|
||||
.max_user_clip_distances = device.GetMaxUserClipDistances(),
|
||||
};
|
||||
|
||||
host_info = Shader::HostTranslateInfo{
|
||||
|
@ -755,10 +755,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
|
||||
// The wanted format is not supported by hardware, search for alternatives
|
||||
const VkFormat* alternatives = GetFormatAlternatives(wanted_format);
|
||||
if (alternatives == nullptr) {
|
||||
ASSERT_MSG(false,
|
||||
"Format={} with usage={} and type={} has no defined alternatives and host "
|
||||
"hardware does not support it",
|
||||
wanted_format, wanted_usage, format_type);
|
||||
LOG_ERROR(Render_Vulkan,
|
||||
"Format={} with usage={} and type={} has no defined alternatives and host "
|
||||
"hardware does not support it",
|
||||
wanted_format, wanted_usage, format_type);
|
||||
return wanted_format;
|
||||
}
|
||||
|
||||
@ -774,10 +774,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
|
||||
}
|
||||
|
||||
// No alternatives found, panic
|
||||
ASSERT_MSG(false,
|
||||
"Format={} with usage={} and type={} is not supported by the host hardware and "
|
||||
"doesn't support any of the alternatives",
|
||||
wanted_format, wanted_usage, format_type);
|
||||
LOG_ERROR(Render_Vulkan,
|
||||
"Format={} with usage={} and type={} is not supported by the host hardware and "
|
||||
"doesn't support any of the alternatives",
|
||||
wanted_format, wanted_usage, format_type);
|
||||
return wanted_format;
|
||||
}
|
||||
|
||||
|
@ -665,10 +665,6 @@ public:
|
||||
return properties.properties.limits.maxViewports;
|
||||
}
|
||||
|
||||
u32 GetMaxUserClipDistances() const {
|
||||
return properties.properties.limits.maxClipDistances;
|
||||
}
|
||||
|
||||
bool SupportsConditionalBarriers() const {
|
||||
return supports_conditional_barriers;
|
||||
}
|
||||
|
@ -246,7 +246,9 @@ void SetObjectName(const DeviceDispatch* dld, VkDevice device, T handle, VkObjec
|
||||
.objectHandle = reinterpret_cast<u64>(handle),
|
||||
.pObjectName = name,
|
||||
};
|
||||
Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info));
|
||||
if (dld->vkSetDebugUtilsObjectNameEXT) {
|
||||
Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info));
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
Reference in New Issue
Block a user