early-access version 3962

This commit is contained in:
pineappleEA 2023-11-03 16:31:05 +01:00
parent 7ae770f1a9
commit 04780a2b94
59 changed files with 7431 additions and 822 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 3961. This is the source code for early-access 3962.
## Legal Notice ## Legal Notice

View file

@ -462,12 +462,12 @@ object NativeLibrary {
} }
fun setEmulationActivity(emulationActivity: EmulationActivity?) { fun setEmulationActivity(emulationActivity: EmulationActivity?) {
Log.verbose("[NativeLibrary] Registering EmulationActivity.") Log.debug("[NativeLibrary] Registering EmulationActivity.")
sEmulationActivity = WeakReference(emulationActivity) sEmulationActivity = WeakReference(emulationActivity)
} }
fun clearEmulationActivity() { fun clearEmulationActivity() {
Log.verbose("[NativeLibrary] Unregistering EmulationActivity.") Log.debug("[NativeLibrary] Unregistering EmulationActivity.")
sEmulationActivity.clear() sEmulationActivity.clear()
} }

View file

@ -47,6 +47,7 @@ import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ForegroundService import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.MemoryUtil import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.NfcReader import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ThemeHelper import org.yuzu.yuzu_emu.utils.ThemeHelper
@ -80,6 +81,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Log.gameLaunched = true
ThemeHelper.setTheme(this) ThemeHelper.setTheme(this)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View file

@ -42,6 +42,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
class HomeSettingsFragment : Fragment() { class HomeSettingsFragment : Fragment() {
private var _binding: FragmentHomeSettingsBinding? = null private var _binding: FragmentHomeSettingsBinding? = null
@ -312,19 +313,32 @@ class HomeSettingsFragment : Fragment() {
} }
} }
// Share the current log if we just returned from a game but share the old log
// if we just started the app and the old log exists.
private fun shareLog() { private fun shareLog() {
val file = DocumentFile.fromSingleUri( val currentLog = DocumentFile.fromSingleUri(
mainActivity, mainActivity,
DocumentsContract.buildDocumentUri( DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY, DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/log/yuzu_log.txt" "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt"
) )
)!! )!!
if (file.exists()) { val oldLog = DocumentFile.fromSingleUri(
mainActivity,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/log/yuzu_log.txt.old.txt"
)
)!!
val intent = Intent(Intent.ACTION_SEND) val intent = Intent(Intent.ACTION_SEND)
.setDataAndType(file.uri, FileUtil.TEXT_PLAIN) .setDataAndType(currentLog.uri, FileUtil.TEXT_PLAIN)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, file.uri) if (!Log.gameLaunched && oldLog.exists()) {
intent.putExtra(Intent.EXTRA_STREAM, oldLog.uri)
startActivity(Intent.createChooser(intent, getText(R.string.share_log)))
} else if (currentLog.exists()) {
intent.putExtra(Intent.EXTRA_STREAM, currentLog.uri)
startActivity(Intent.createChooser(intent, getText(R.string.share_log))) startActivity(Intent.createChooser(intent, getText(R.string.share_log)))
} else { } else {
Toast.makeText( Toast.makeText(

View file

@ -68,7 +68,7 @@ object InputHandler {
private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int { private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int {
var deviceIndex = index var deviceIndex = index
if (deviceId != -1) { if (deviceId != -1) {
deviceIndex = controllerIds[deviceId]!! deviceIndex = controllerIds[deviceId] ?: 0
} }
// TODO: Joycons are handled as different controllers. Find a way to merge them. // TODO: Joycons are handled as different controllers. Find a way to merge them.

View file

@ -3,38 +3,17 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import android.util.Log
import org.yuzu.yuzu_emu.BuildConfig
/**
* Contains methods that call through to [android.util.Log], but
* with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log
* levels in release builds.
*/
object Log { object Log {
private const val TAG = "Yuzu Frontend" // Tracks whether we should share the old log or the current log
var gameLaunched = false
fun verbose(message: String) { external fun debug(message: String)
if (BuildConfig.DEBUG) {
Log.v(TAG, message)
}
}
fun debug(message: String) { external fun warning(message: String)
if (BuildConfig.DEBUG) {
Log.d(TAG, message)
}
}
fun info(message: String) { external fun info(message: String)
Log.i(TAG, message)
}
fun warning(message: String) { external fun error(message: String)
Log.w(TAG, message)
}
fun error(message: String) { external fun critical(message: String)
Log.e(TAG, message)
}
} }

View file

@ -18,6 +18,7 @@ add_library(yuzu-android SHARED
native_config.cpp native_config.cpp
uisettings.cpp uisettings.cpp
game_metadata.cpp game_metadata.cpp
native_log.cpp
) )
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})

View file

@ -248,6 +248,11 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath)
} }
void EmulationSession::InitializeSystem() { void EmulationSession::InitializeSystem() {
// Initialize logging system
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();
// Initialize filesystem. // Initialize filesystem.
m_system.SetFilesystem(m_vfs); m_system.SetFilesystem(m_vfs);
m_system.GetUserChannel().clear(); m_system.GetUserChannel().clear();
@ -462,10 +467,6 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
} }
static Core::SystemResultStatus RunEmulation(const std::string& filepath) { static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();
MicroProfileOnThreadCreate("EmuThread"); MicroProfileOnThreadCreate("EmuThread");
SCOPE_EXIT({ MicroProfileShutdown(); }); SCOPE_EXIT({ MicroProfileShutdown(); });

View file

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <common/logging/log.h>
#include <jni.h>
#include "android_common/android_common.h"
extern "C" {
void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage));
}
void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_WARNING(Frontend, "{}", GetJString(env, jmessage));
}
void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_INFO(Frontend, "{}", GetJString(env, jmessage));
}
void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_ERROR(Frontend, "{}", GetJString(env, jmessage));
}
void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage));
}
} // extern "C"

View file

@ -9,12 +9,12 @@ PageTable::PageTable() = default;
PageTable::~PageTable() noexcept = default; PageTable::~PageTable() noexcept = default;
bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
u64 address) const { Common::ProcessAddress address) const {
// Setup invalid defaults. // Setup invalid defaults.
out_entry.phys_addr = 0; out_entry->phys_addr = 0;
out_entry.block_size = page_size; out_entry->block_size = page_size;
out_context.next_page = 0; out_context->next_page = 0;
// Validate that we can read the actual entry. // Validate that we can read the actual entry.
const auto page = address / page_size; const auto page = address / page_size;
@ -29,20 +29,20 @@ bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_
} }
// Populate the results. // Populate the results.
out_entry.phys_addr = phys_addr + address; out_entry->phys_addr = phys_addr + GetInteger(address);
out_context.next_page = page + 1; out_context->next_page = page + 1;
out_context.next_offset = address + page_size; out_context->next_offset = GetInteger(address) + page_size;
return true; return true;
} }
bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const { bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
// Setup invalid defaults. // Setup invalid defaults.
out_entry.phys_addr = 0; out_entry->phys_addr = 0;
out_entry.block_size = page_size; out_entry->block_size = page_size;
// Validate that we can read the actual entry. // Validate that we can read the actual entry.
const auto page = context.next_page; const auto page = context->next_page;
if (page >= backing_addr.size()) { if (page >= backing_addr.size()) {
return false; return false;
} }
@ -54,9 +54,9 @@ bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& c
} }
// Populate the results. // Populate the results.
out_entry.phys_addr = phys_addr + context.next_offset; out_entry->phys_addr = phys_addr + context->next_offset;
context.next_page = page + 1; context->next_page = page + 1;
context.next_offset += page_size; context->next_offset += page_size;
return true; return true;
} }

View file

@ -6,6 +6,7 @@
#include <atomic> #include <atomic>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/typed_address.h"
#include "common/virtual_buffer.h" #include "common/virtual_buffer.h"
namespace Common { namespace Common {
@ -100,9 +101,9 @@ struct PageTable {
PageTable(PageTable&&) noexcept = default; PageTable(PageTable&&) noexcept = default;
PageTable& operator=(PageTable&&) noexcept = default; PageTable& operator=(PageTable&&) noexcept = default;
bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
u64 address) const; Common::ProcessAddress address) const;
bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const; bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
/** /**
* Resizes the page table to be able to accommodate enough pages within * Resizes the page table to be able to accommodate enough pages within
@ -117,6 +118,16 @@ struct PageTable {
return current_address_space_width_in_bits; return current_address_space_width_in_bits;
} }
bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr,
Common::ProcessAddress virt_addr) const {
if (virt_addr > (1ULL << this->GetAddressSpaceBits())) {
return false;
}
*out_phys_addr = backing_addr[virt_addr / page_size] + GetInteger(virt_addr);
return true;
}
/** /**
* Vector of memory pointers backing each page. An entry can only be non-null if the * Vector of memory pointers backing each page. An entry can only be non-null if the
* corresponding attribute element is of type `Memory`. * corresponding attribute element is of type `Memory`.

View file

@ -271,8 +271,9 @@ add_library(core STATIC
hle/kernel/k_page_heap.h hle/kernel/k_page_heap.h
hle/kernel/k_page_group.cpp hle/kernel/k_page_group.cpp
hle/kernel/k_page_group.h hle/kernel/k_page_group.h
hle/kernel/k_page_table.cpp
hle/kernel/k_page_table.h hle/kernel/k_page_table.h
hle/kernel/k_page_table_base.cpp
hle/kernel/k_page_table_base.h
hle/kernel/k_page_table_manager.h hle/kernel/k_page_table_manager.h
hle/kernel/k_page_table_slab_heap.h hle/kernel/k_page_table_slab_heap.h
hle/kernel/k_port.cpp hle/kernel/k_port.cpp
@ -280,6 +281,7 @@ add_library(core STATIC
hle/kernel/k_priority_queue.h hle/kernel/k_priority_queue.h
hle/kernel/k_process.cpp hle/kernel/k_process.cpp
hle/kernel/k_process.h hle/kernel/k_process.h
hle/kernel/k_process_page_table.h
hle/kernel/k_readable_event.cpp hle/kernel/k_readable_event.cpp
hle/kernel/k_readable_event.h hle/kernel/k_readable_event.h
hle/kernel/k_resource_limit.cpp hle/kernel/k_resource_limit.cpp
@ -330,8 +332,6 @@ add_library(core STATIC
hle/kernel/physical_core.cpp hle/kernel/physical_core.cpp
hle/kernel/physical_core.h hle/kernel/physical_core.h
hle/kernel/physical_memory.h hle/kernel/physical_memory.h
hle/kernel/process_capability.cpp
hle/kernel/process_capability.h
hle/kernel/slab_helpers.h hle/kernel/slab_helpers.h
hle/kernel/svc.cpp hle/kernel/svc.cpp
hle/kernel/svc.h hle/kernel/svc.h
@ -715,6 +715,7 @@ add_library(core STATIC
hle/service/nvnflinger/producer_listener.h hle/service/nvnflinger/producer_listener.h
hle/service/nvnflinger/status.h hle/service/nvnflinger/status.h
hle/service/nvnflinger/ui/fence.h hle/service/nvnflinger/ui/fence.h
hle/service/nvnflinger/ui/graphic_buffer.cpp
hle/service/nvnflinger/ui/graphic_buffer.h hle/service/nvnflinger/ui/graphic_buffer.h
hle/service/nvnflinger/window.h hle/service/nvnflinger/window.h
hle/service/olsc/olsc.cpp hle/service/olsc/olsc.cpp

View file

@ -727,29 +727,34 @@ static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::Memory
} }
} }
static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) { static VAddr GetModuleEnd(Kernel::KProcessPageTable& page_table, VAddr base) {
Kernel::Svc::MemoryInfo mem_info; Kernel::KMemoryInfo mem_info;
Kernel::Svc::MemoryInfo svc_mem_info;
Kernel::Svc::PageInfo page_info;
VAddr cur_addr{base}; VAddr cur_addr{base};
// Expect: r-x Code (.text) // Expect: r-x Code (.text)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
cur_addr = mem_info.base_address + mem_info.size; svc_mem_info = mem_info.GetSvcMemoryInfo();
if (mem_info.state != Kernel::Svc::MemoryState::Code || cur_addr = svc_mem_info.base_address + svc_mem_info.size;
mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
return cur_addr - 1; return cur_addr - 1;
} }
// Expect: r-- Code (.rodata) // Expect: r-- Code (.rodata)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
cur_addr = mem_info.base_address + mem_info.size; svc_mem_info = mem_info.GetSvcMemoryInfo();
if (mem_info.state != Kernel::Svc::MemoryState::Code || cur_addr = svc_mem_info.base_address + svc_mem_info.size;
mem_info.permission != Kernel::Svc::MemoryPermission::Read) { if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
return cur_addr - 1; return cur_addr - 1;
} }
// Expect: rw- CodeData (.data) // Expect: rw- CodeData (.data)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
cur_addr = mem_info.base_address + mem_info.size; svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
return cur_addr - 1; return cur_addr - 1;
} }
@ -767,7 +772,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
if (command_str == "get fastmem") { if (command_str == "get fastmem") {
if (Settings::IsFastmemEnabled()) { if (Settings::IsFastmemEnabled()) {
const auto& impl = page_table.PageTableImpl(); const auto& impl = page_table.GetImpl();
const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena); const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena);
const auto region_bits = impl.current_address_space_width_in_bits; const auto region_bits = impl.current_address_space_width_in_bits;
const auto region_size = 1ULL << region_bits; const auto region_size = 1ULL << region_bits;
@ -785,20 +790,22 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
reply = fmt::format("Process: {:#x} ({})\n" reply = fmt::format("Process: {:#x} ({})\n"
"Program Id: {:#018x}\n", "Program Id: {:#018x}\n",
process->GetProcessId(), process->GetName(), process->GetProgramId()); process->GetProcessId(), process->GetName(), process->GetProgramId());
reply += fmt::format("Layout:\n" reply += fmt::format(
"Layout:\n"
" Alias: {:#012x} - {:#012x}\n" " Alias: {:#012x} - {:#012x}\n"
" Heap: {:#012x} - {:#012x}\n" " Heap: {:#012x} - {:#012x}\n"
" Aslr: {:#012x} - {:#012x}\n" " Aslr: {:#012x} - {:#012x}\n"
" Stack: {:#012x} - {:#012x}\n" " Stack: {:#012x} - {:#012x}\n"
"Modules:\n", "Modules:\n",
GetInteger(page_table.GetAliasRegionStart()), GetInteger(page_table.GetAliasRegionStart()),
GetInteger(page_table.GetAliasRegionEnd()), GetInteger(page_table.GetAliasRegionStart()) + page_table.GetAliasRegionSize() - 1,
GetInteger(page_table.GetHeapRegionStart()), GetInteger(page_table.GetHeapRegionStart()),
GetInteger(page_table.GetHeapRegionEnd()), GetInteger(page_table.GetHeapRegionStart()) + page_table.GetHeapRegionSize() - 1,
GetInteger(page_table.GetAliasCodeRegionStart()), GetInteger(page_table.GetAliasCodeRegionStart()),
GetInteger(page_table.GetAliasCodeRegionEnd()), GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize() -
1,
GetInteger(page_table.GetStackRegionStart()), GetInteger(page_table.GetStackRegionStart()),
GetInteger(page_table.GetStackRegionEnd())); GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1);
for (const auto& [vaddr, name] : modules) { for (const auto& [vaddr, name] : modules) {
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
@ -811,27 +818,34 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
while (true) { while (true) {
using MemoryAttribute = Kernel::Svc::MemoryAttribute; using MemoryAttribute = Kernel::Svc::MemoryAttribute;
auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); Kernel::KMemoryInfo mem_info{};
Kernel::Svc::PageInfo page_info{};
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info),
cur_addr));
auto svc_mem_info = mem_info.GetSvcMemoryInfo();
if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible || if (svc_mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) { svc_mem_info.base_address + svc_mem_info.size - 1 !=
const char* state = GetMemoryStateName(mem_info.state); std::numeric_limits<u64>::max()) {
const char* perm = GetMemoryPermissionString(mem_info); const char* state = GetMemoryStateName(svc_mem_info.state);
const char* perm = GetMemoryPermissionString(svc_mem_info);
const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; const char l = True(svc_mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; const char i =
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; True(svc_mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; const char d =
True(svc_mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
const char u = True(svc_mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
const char p = const char p =
True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-'; True(svc_mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", reply += fmt::format(
mem_info.base_address, " {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", svc_mem_info.base_address,
mem_info.base_address + mem_info.size - 1, perm, state, l, i, svc_mem_info.base_address + svc_mem_info.size - 1, perm, state, l, i, d, u, p,
d, u, p, mem_info.ipc_count, mem_info.device_count); svc_mem_info.ipc_count, svc_mem_info.device_count);
} }
const uintptr_t next_address = mem_info.base_address + mem_info.size; const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
if (next_address <= cur_addr) { if (next_address <= cur_addr) {
break; break;
} }

View file

@ -1091,30 +1091,30 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
bool is_charging = false; bool is_charging = false;
bool is_powered = false; bool is_powered = false;
NpadBatteryLevel battery_level = 0; NpadBatteryLevel battery_level = NpadBatteryLevel::Empty;
switch (controller.battery_values[index]) { switch (controller.battery_values[index]) {
case Common::Input::BatteryLevel::Charging: case Common::Input::BatteryLevel::Charging:
is_charging = true; is_charging = true;
is_powered = true; is_powered = true;
battery_level = 6; battery_level = NpadBatteryLevel::Full;
break; break;
case Common::Input::BatteryLevel::Medium: case Common::Input::BatteryLevel::Medium:
battery_level = 6; battery_level = NpadBatteryLevel::High;
break; break;
case Common::Input::BatteryLevel::Low: case Common::Input::BatteryLevel::Low:
battery_level = 4; battery_level = NpadBatteryLevel::Low;
break; break;
case Common::Input::BatteryLevel::Critical: case Common::Input::BatteryLevel::Critical:
battery_level = 2; battery_level = NpadBatteryLevel::Critical;
break; break;
case Common::Input::BatteryLevel::Empty: case Common::Input::BatteryLevel::Empty:
battery_level = 0; battery_level = NpadBatteryLevel::Empty;
break; break;
case Common::Input::BatteryLevel::None: case Common::Input::BatteryLevel::None:
case Common::Input::BatteryLevel::Full: case Common::Input::BatteryLevel::Full:
default: default:
is_powered = true; is_powered = true;
battery_level = 8; battery_level = NpadBatteryLevel::Full;
break; break;
} }

View file

@ -302,6 +302,15 @@ enum class TouchScreenModeForNx : u8 {
Heat2, Heat2,
}; };
// This is nn::hid::system::NpadBatteryLevel
enum class NpadBatteryLevel : u32 {
Empty,
Critical,
Low,
High,
Full,
};
// This is nn::hid::NpadStyleTag // This is nn::hid::NpadStyleTag
struct NpadStyleTag { struct NpadStyleTag {
union { union {
@ -385,16 +394,12 @@ struct NpadGcTriggerState {
}; };
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
// This is nn::hid::system::NpadBatteryLevel
using NpadBatteryLevel = u32;
static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size");
// This is nn::hid::system::NpadPowerInfo // This is nn::hid::system::NpadPowerInfo
struct NpadPowerInfo { struct NpadPowerInfo {
bool is_powered{}; bool is_powered{};
bool is_charging{}; bool is_charging{};
INSERT_PADDING_BYTES(0x6); INSERT_PADDING_BYTES(0x6);
NpadBatteryLevel battery_level{8}; NpadBatteryLevel battery_level{NpadBatteryLevel::Full};
}; };
static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");

View file

@ -222,7 +222,7 @@ Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress*
}; };
// We succeeded. // We succeeded.
*out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr); *out = KPageTable::GetHeapVirtualAddress(kernel, paddr);
R_SUCCEED(); R_SUCCEED();
} }
@ -238,8 +238,17 @@ void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress addres
ASSERT(Common::IsAligned(size, alignment)); ASSERT(Common::IsAligned(size, alignment));
// Close the secure region's pages. // Close the secure region's pages.
kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address), kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel, address),
size / PageSize); size / PageSize);
} }
// Insecure Memory.
KResourceLimit* KSystemControl::GetInsecureMemoryResourceLimit(KernelCore& kernel) {
return kernel.GetSystemResourceLimit();
}
u32 KSystemControl::GetInsecureMemoryPool() {
return static_cast<u32>(KMemoryManager::Pool::SystemNonSecure);
}
} // namespace Kernel::Board::Nintendo::Nx } // namespace Kernel::Board::Nintendo::Nx

View file

@ -8,7 +8,8 @@
namespace Kernel { namespace Kernel {
class KernelCore; class KernelCore;
} class KResourceLimit;
} // namespace Kernel
namespace Kernel::Board::Nintendo::Nx { namespace Kernel::Board::Nintendo::Nx {
@ -40,6 +41,10 @@ public:
u32 pool); u32 pool);
static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size, static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
u32 pool); u32 pool);
// Insecure Memory.
static KResourceLimit* GetInsecureMemoryResourceLimit(KernelCore& kernel);
static u32 GetInsecureMemoryPool();
}; };
} // namespace Kernel::Board::Nintendo::Nx } // namespace Kernel::Board::Nintendo::Nx

View file

@ -4,14 +4,15 @@
#include "core/hardware_properties.h" #include "core/hardware_properties.h"
#include "core/hle/kernel/k_capabilities.h" #include "core/hle/kernel/k_capabilities.h"
#include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_version.h" #include "core/hle/kernel/svc_version.h"
namespace Kernel { namespace Kernel {
Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table) { Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps,
KProcessPageTable* page_table) {
// We're initializing an initial process. // We're initializing an initial process.
m_svc_access_flags.reset(); m_svc_access_flags.reset();
m_irq_access_flags.reset(); m_irq_access_flags.reset();
@ -41,7 +42,8 @@ Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTabl
R_RETURN(this->SetCapabilities(kern_caps, page_table)); R_RETURN(this->SetCapabilities(kern_caps, page_table));
} }
Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table) { Result KCapabilities::InitializeForUser(std::span<const u32> user_caps,
KProcessPageTable* page_table) {
// We're initializing a user process. // We're initializing a user process.
m_svc_access_flags.reset(); m_svc_access_flags.reset();
m_irq_access_flags.reset(); m_irq_access_flags.reset();
@ -121,7 +123,7 @@ Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
R_SUCCEED(); R_SUCCEED();
} }
Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table) { Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table) {
const auto range_pack = MapRange{cap}; const auto range_pack = MapRange{cap};
const auto size_pack = MapRangeSize{size_cap}; const auto size_pack = MapRangeSize{size_cap};
@ -142,16 +144,13 @@ Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* p
? KMemoryPermission::UserRead ? KMemoryPermission::UserRead
: KMemoryPermission::UserReadWrite; : KMemoryPermission::UserReadWrite;
if (MapRangeSize{size_cap}.normal) { if (MapRangeSize{size_cap}.normal) {
// R_RETURN(page_table->MapStatic(phys_addr, size, perm)); R_RETURN(page_table->MapStatic(phys_addr, size, perm));
} else { } else {
// R_RETURN(page_table->MapIo(phys_addr, size, perm)); R_RETURN(page_table->MapIo(phys_addr, size, perm));
} }
UNIMPLEMENTED();
R_SUCCEED();
} }
Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) { Result KCapabilities::MapIoPage_(const u32 cap, KProcessPageTable* page_table) {
// Get/validate address/size // Get/validate address/size
const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize; const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
const size_t num_pages = 1; const size_t num_pages = 1;
@ -160,10 +159,7 @@ Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress); R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
// Do the mapping. // Do the mapping.
// R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite)); R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission::UserReadWrite));
UNIMPLEMENTED();
R_SUCCEED();
} }
template <typename F> template <typename F>
@ -200,13 +196,11 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
R_SUCCEED(); R_SUCCEED();
} }
Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) { Result KCapabilities::MapRegion_(const u32 cap, KProcessPageTable* page_table) {
// Map each region into the process's page table. // Map each region into the process's page table.
return ProcessMapRegionCapability( return ProcessMapRegionCapability(
cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result { cap, [page_table](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
// R_RETURN(page_table->MapRegion(region_type, perm)); R_RETURN(page_table->MapRegion(region_type, perm));
UNIMPLEMENTED();
R_SUCCEED();
}); });
} }
@ -280,7 +274,7 @@ Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
} }
Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc, Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
KPageTable* page_table) { KProcessPageTable* page_table) {
// Validate this is a capability we can act on. // Validate this is a capability we can act on.
const auto type = GetCapabilityType(cap); const auto type = GetCapabilityType(cap);
R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument); R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);
@ -318,7 +312,7 @@ Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
} }
} }
Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* page_table) { Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table) {
u32 set_flags = 0, set_svc = 0; u32 set_flags = 0, set_svc = 0;
for (size_t i = 0; i < caps.size(); i++) { for (size_t i = 0; i < caps.size(); i++) {

View file

@ -15,15 +15,15 @@
namespace Kernel { namespace Kernel {
class KPageTable; class KProcessPageTable;
class KernelCore; class KernelCore;
class KCapabilities { class KCapabilities {
public: public:
constexpr explicit KCapabilities() = default; constexpr explicit KCapabilities() = default;
Result InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table); Result InitializeForKip(std::span<const u32> kern_caps, KProcessPageTable* page_table);
Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table); Result InitializeForUser(std::span<const u32> user_caps, KProcessPageTable* page_table);
static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps); static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
@ -264,9 +264,9 @@ private:
Result SetCorePriorityCapability(const u32 cap); Result SetCorePriorityCapability(const u32 cap);
Result SetSyscallMaskCapability(const u32 cap, u32& set_svc); Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table); Result MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table);
Result MapIoPage_(const u32 cap, KPageTable* page_table); Result MapIoPage_(const u32 cap, KProcessPageTable* page_table);
Result MapRegion_(const u32 cap, KPageTable* page_table); Result MapRegion_(const u32 cap, KProcessPageTable* page_table);
Result SetInterruptPairCapability(const u32 cap); Result SetInterruptPairCapability(const u32 cap);
Result SetProgramTypeCapability(const u32 cap); Result SetProgramTypeCapability(const u32 cap);
Result SetKernelVersionCapability(const u32 cap); Result SetKernelVersionCapability(const u32 cap);
@ -277,8 +277,9 @@ private:
static Result ProcessMapRegionCapability(const u32 cap, F f); static Result ProcessMapRegionCapability(const u32 cap, F f);
static Result CheckMapRegion(KernelCore& kernel, const u32 cap); static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table); Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table); KProcessPageTable* page_table);
Result SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table);
private: private:
Svc::SvcAccessFlagSet m_svc_access_flags{}; Svc::SvcAccessFlagSet m_svc_access_flags{};

View file

@ -54,7 +54,7 @@ Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
R_SUCCEED(); R_SUCCEED();
} }
Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_address, Result KDeviceAddressSpace::Map(KProcessPageTable* page_table, KProcessAddress process_address,
size_t size, u64 device_address, u32 option, bool is_aligned) { size_t size, u64 device_address, u32 option, bool is_aligned) {
// Check that the address falls within the space. // Check that the address falls within the space.
R_UNLESS((m_space_address <= device_address && R_UNLESS((m_space_address <= device_address &&
@ -113,7 +113,7 @@ Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_
R_SUCCEED(); R_SUCCEED();
} }
Result KDeviceAddressSpace::Unmap(KPageTable* page_table, KProcessAddress process_address, Result KDeviceAddressSpace::Unmap(KProcessPageTable* page_table, KProcessAddress process_address,
size_t size, u64 device_address) { size_t size, u64 device_address) {
// Check that the address falls within the space. // Check that the address falls within the space.
R_UNLESS((m_space_address <= device_address && R_UNLESS((m_space_address <= device_address &&

View file

@ -5,7 +5,7 @@
#include <string> #include <string>
#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/k_typed_address.h" #include "core/hle/kernel/k_typed_address.h"
#include "core/hle/kernel/slab_helpers.h" #include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h" #include "core/hle/result.h"
@ -31,23 +31,23 @@ public:
Result Attach(Svc::DeviceName device_name); Result Attach(Svc::DeviceName device_name);
Result Detach(Svc::DeviceName device_name); Result Detach(Svc::DeviceName device_name);
Result MapByForce(KPageTable* page_table, KProcessAddress process_address, size_t size, Result MapByForce(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address, u32 option) { u64 device_address, u32 option) {
R_RETURN(this->Map(page_table, process_address, size, device_address, option, false)); R_RETURN(this->Map(page_table, process_address, size, device_address, option, false));
} }
Result MapAligned(KPageTable* page_table, KProcessAddress process_address, size_t size, Result MapAligned(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address, u32 option) { u64 device_address, u32 option) {
R_RETURN(this->Map(page_table, process_address, size, device_address, option, true)); R_RETURN(this->Map(page_table, process_address, size, device_address, option, true));
} }
Result Unmap(KPageTable* page_table, KProcessAddress process_address, size_t size, Result Unmap(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address); u64 device_address);
static void Initialize(); static void Initialize();
private: private:
Result Map(KPageTable* page_table, KProcessAddress process_address, size_t size, Result Map(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address, u32 option, bool is_aligned); u64 device_address, u32 option, bool is_aligned);
private: private:

View file

@ -394,6 +394,14 @@ private:
return region.GetEndAddress(); return region.GetEndAddress();
} }
public:
static const KMemoryRegion* Find(const KMemoryLayout& layout, KVirtualAddress address) {
return Find(address, layout.GetVirtualMemoryRegionTree());
}
static const KMemoryRegion* Find(const KMemoryLayout& layout, KPhysicalAddress address) {
return Find(address, layout.GetPhysicalMemoryRegionTree());
}
private: private:
u64 m_linear_phys_to_virt_diff{}; u64 m_linear_phys_to_virt_diff{};
u64 m_linear_virt_to_phys_diff{}; u64 m_linear_virt_to_phys_diff{};

View file

@ -456,8 +456,7 @@ size_t KMemoryManager::Impl::Initialize(KPhysicalAddress address, size_t size,
} }
void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) { void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
auto optimize_pa = auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize())); std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize()));
@ -465,8 +464,7 @@ void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages) { size_t num_pages) {
auto optimize_pa = auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
// Get the range we're tracking. // Get the range we're tracking.
@ -485,8 +483,7 @@ void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysi
void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages) { size_t num_pages) {
auto optimize_pa = auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
// Get the range we're tracking. // Get the range we're tracking.
@ -506,8 +503,7 @@ void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysica
bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages, u8 fill_pattern) { size_t num_pages, u8 fill_pattern) {
auto& device_memory = kernel.System().DeviceMemory(); auto& device_memory = kernel.System().DeviceMemory();
auto optimize_pa = auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa); auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa);
// We want to return whether any pages were newly allocated. // We want to return whether any pages were newly allocated.

View file

@ -3,548 +3,14 @@
#pragma once #pragma once
#include <memory> #include "core/hle/kernel/k_page_table_base.h"
#include "common/common_funcs.h"
#include "common/page_table.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/k_dynamic_resource_manager.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_block_manager.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Core {
class System;
}
namespace Kernel { namespace Kernel {
enum class DisableMergeAttribute : u8 { class KPageTable final : public KPageTableBase {
None = (0U << 0),
DisableHead = (1U << 0),
DisableHeadAndBody = (1U << 1),
EnableHeadAndBody = (1U << 2),
DisableTail = (1U << 3),
EnableTail = (1U << 4),
EnableAndMergeHeadBodyTail = (1U << 5),
EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
};
struct KPageProperties {
KMemoryPermission perm;
bool io;
bool uncached;
DisableMergeAttribute disable_merge_attributes;
};
static_assert(std::is_trivial_v<KPageProperties>);
static_assert(sizeof(KPageProperties) == sizeof(u32));
class KBlockInfoManager;
class KMemoryBlockManager;
class KResourceLimit;
class KSystemResource;
class KPageTable final {
protected:
struct PageLinkedList;
public: public:
enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; explicit KPageTable(KernelCore& kernel) : KPageTableBase(kernel) {}
~KPageTable() = default;
YUZU_NON_COPYABLE(KPageTable);
YUZU_NON_MOVEABLE(KPageTable);
explicit KPageTable(Core::System& system_);
~KPageTable();
Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
bool enable_das_merge, bool from_back, KMemoryManager::Pool pool,
KProcessAddress code_addr, size_t code_size,
KSystemResource* system_resource, KResourceLimit* resource_limit,
Core::Memory::Memory& memory);
void Finalize();
Result MapProcessCode(KProcessAddress addr, size_t pages_count, KMemoryState state,
KMemoryPermission perm);
Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
ICacheInvalidationStrategy icache_invalidation_strategy);
Result UnmapProcessMemory(KProcessAddress dst_addr, size_t size, KPageTable& src_page_table,
KProcessAddress src_addr);
Result MapPhysicalMemory(KProcessAddress addr, size_t size);
Result UnmapPhysicalMemory(KProcessAddress addr, size_t size);
Result MapMemory(KProcessAddress dst_addr, KProcessAddress src_addr, size_t size);
Result UnmapMemory(KProcessAddress dst_addr, KProcessAddress src_addr, size_t size);
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
Svc::MemoryPermission svc_perm);
KMemoryInfo QueryInfo(KProcessAddress addr);
Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm);
Result SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr);
Result SetMaxHeapSize(size_t size);
Result SetHeapSize(u64* out, size_t size);
Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
KMemoryPermission perm, bool is_aligned, bool check_heap);
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap);
Result UnlockForDeviceAddressSpace(KProcessAddress addr, size_t size);
Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size);
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size);
Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
KPageTable& src_page_table, KMemoryPermission test_perm,
KMemoryState dst_state, bool send);
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
KMemoryPermission perm);
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size);
Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg);
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr);
Common::PageTable& PageTableImpl() {
return *m_page_table_impl;
}
const Common::PageTable& PageTableImpl() const {
return *m_page_table_impl;
}
KBlockInfoManager* GetBlockInfoManager() {
return m_block_info_manager;
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, KProcessAddress region_start,
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
region_num_pages, state, perm));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
this->GetRegionAddress(state),
this->GetRegionSize(state) / PageSize, state, perm));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
this->GetRegionAddress(state),
this->GetRegionSize(state) / PageSize, state, perm));
}
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
KMemoryPermission perm);
Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
KMemoryPermission perm);
Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
KMemoryPermission perm);
Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
const KPageGroup& pg);
KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
size_t GetRegionSize(Svc::MemoryState state) const;
bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
KProcessAddress GetRegionAddress(KMemoryState state) const {
return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
size_t GetRegionSize(KMemoryState state) const {
return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
return this->CanContain(addr, size,
static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
protected:
struct PageLinkedList {
private:
struct Node {
Node* m_next;
std::array<u8, PageSize - sizeof(Node*)> m_buffer;
};
public:
constexpr PageLinkedList() = default;
void Push(Node* n) {
ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize));
n->m_next = m_root;
m_root = n;
}
void Push(Core::Memory::Memory& memory, KVirtualAddress addr) {
this->Push(memory.GetPointer<Node>(GetInteger(addr)));
}
Node* Peek() const {
return m_root;
}
Node* Pop() {
Node* const r = m_root;
m_root = r->m_next;
r->m_next = nullptr;
return r;
}
private:
Node* m_root{};
};
static_assert(std::is_trivially_destructible<PageLinkedList>::value);
private:
enum class OperationType : u32 {
Map = 0,
MapGroup = 1,
MapFirstGroup = 2,
Unmap = 3,
ChangePermissions = 4,
ChangePermissionsAndRefresh = 5,
ChangePermissionsAndRefreshAndFlush = 6,
Separate = 7,
};
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
bool IsRegionContiguous(KProcessAddress addr, u64 size) const;
void AddRegionToPages(KProcessAddress start, size_t num_pages, KPageGroup& page_linked_list);
KMemoryInfo QueryInfoImpl(KProcessAddress addr);
KProcessAddress AllocateVirtualMemory(KProcessAddress start, size_t region_num_pages,
u64 needed_num_pages, size_t align);
Result Operate(KProcessAddress addr, size_t num_pages, const KPageGroup& page_group,
OperationType operation);
Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
OperationType operation, KPhysicalAddress map_addr = 0);
void FinalizeUpdate(PageLinkedList* page_list);
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
size_t num_pages, size_t alignment, size_t offset,
size_t guard_pages);
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const {
R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr));
}
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr,
ignore_attr));
}
Result CheckMemoryState(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
attr_mask, attr, ignore_attr));
}
Result LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_KPhysicalAddress,
KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr, KMemoryPermission new_perm,
KMemoryAttribute lock_attr);
Result UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr,
const KPageGroup* pg);
Result MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages);
bool IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages);
bool IsLockedByCurrentThread() const {
return m_general_lock.IsLockedByCurrentThread();
}
bool IsHeapPhysicalAddress(const KMemoryLayout& layout, KPhysicalAddress phys_addr) {
ASSERT(this->IsLockedByCurrentThread());
return layout.IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr);
}
bool GetPhysicalAddressLocked(KPhysicalAddress* out, KProcessAddress virt_addr) const {
ASSERT(this->IsLockedByCurrentThread());
*out = GetPhysicalAddr(virt_addr);
return *out != 0;
}
Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
KProcessAddress address, size_t size, KMemoryPermission test_perm,
KMemoryState dst_state);
Result SetupForIpcServer(KProcessAddress* out_addr, size_t size, KProcessAddress src_addr,
KMemoryPermission test_perm, KMemoryState dst_state,
KPageTable& src_page_table, bool send);
void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, KProcessAddress address,
size_t size, KMemoryPermission prot_perm);
Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
size_t num_pages, KMemoryPermission perm);
Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
mutable KLightLock m_general_lock;
mutable KLightLock m_map_physical_memory_lock;
public:
constexpr KProcessAddress GetAddressSpaceStart() const {
return m_address_space_start;
}
constexpr KProcessAddress GetAddressSpaceEnd() const {
return m_address_space_end;
}
constexpr size_t GetAddressSpaceSize() const {
return m_address_space_end - m_address_space_start;
}
constexpr KProcessAddress GetHeapRegionStart() const {
return m_heap_region_start;
}
constexpr KProcessAddress GetHeapRegionEnd() const {
return m_heap_region_end;
}
constexpr size_t GetHeapRegionSize() const {
return m_heap_region_end - m_heap_region_start;
}
constexpr KProcessAddress GetAliasRegionStart() const {
return m_alias_region_start;
}
constexpr KProcessAddress GetAliasRegionEnd() const {
return m_alias_region_end;
}
constexpr size_t GetAliasRegionSize() const {
return m_alias_region_end - m_alias_region_start;
}
constexpr KProcessAddress GetStackRegionStart() const {
return m_stack_region_start;
}
constexpr KProcessAddress GetStackRegionEnd() const {
return m_stack_region_end;
}
constexpr size_t GetStackRegionSize() const {
return m_stack_region_end - m_stack_region_start;
}
constexpr KProcessAddress GetKernelMapRegionStart() const {
return m_kernel_map_region_start;
}
constexpr KProcessAddress GetKernelMapRegionEnd() const {
return m_kernel_map_region_end;
}
constexpr KProcessAddress GetCodeRegionStart() const {
return m_code_region_start;
}
constexpr KProcessAddress GetCodeRegionEnd() const {
return m_code_region_end;
}
constexpr KProcessAddress GetAliasCodeRegionStart() const {
return m_alias_code_region_start;
}
constexpr KProcessAddress GetAliasCodeRegionEnd() const {
return m_alias_code_region_end;
}
constexpr size_t GetAliasCodeRegionSize() const {
return m_alias_code_region_end - m_alias_code_region_start;
}
size_t GetNormalMemorySize() const {
KScopedLightLock lk(m_general_lock);
return GetHeapSize() + m_mapped_physical_memory_size;
}
constexpr size_t GetAddressSpaceWidth() const {
return m_address_space_width;
}
constexpr size_t GetHeapSize() const {
return m_current_heap_end - m_heap_region_start;
}
constexpr size_t GetNumGuardPages() const {
return IsKernel() ? 1 : 4;
}
KPhysicalAddress GetPhysicalAddr(KProcessAddress addr) const {
const auto backing_addr = m_page_table_impl->backing_addr[addr >> PageBits];
ASSERT(backing_addr);
return backing_addr + GetInteger(addr);
}
constexpr bool Contains(KProcessAddress addr) const {
return m_address_space_start <= addr && addr <= m_address_space_end - 1;
}
constexpr bool Contains(KProcessAddress addr, size_t size) const {
return m_address_space_start <= addr && addr < addr + size &&
addr + size - 1 <= m_address_space_end - 1;
}
constexpr bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
return this->Contains(addr, size) && m_alias_region_start <= addr &&
addr + size - 1 <= m_alias_region_end - 1;
}
constexpr bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
return this->Contains(addr, size) && m_heap_region_start <= addr &&
addr + size - 1 <= m_heap_region_end - 1;
}
public:
static KVirtualAddress GetLinearMappedVirtualAddress(const KMemoryLayout& layout,
KPhysicalAddress addr) {
return layout.GetLinearVirtualAddress(addr);
}
static KPhysicalAddress GetLinearMappedPhysicalAddress(const KMemoryLayout& layout,
KVirtualAddress addr) {
return layout.GetLinearPhysicalAddress(addr);
}
static KVirtualAddress GetHeapVirtualAddress(const KMemoryLayout& layout,
KPhysicalAddress addr) {
return GetLinearMappedVirtualAddress(layout, addr);
}
static KPhysicalAddress GetHeapPhysicalAddress(const KMemoryLayout& layout,
KVirtualAddress addr) {
return GetLinearMappedPhysicalAddress(layout, addr);
}
static KVirtualAddress GetPageTableVirtualAddress(const KMemoryLayout& layout,
KPhysicalAddress addr) {
return GetLinearMappedVirtualAddress(layout, addr);
}
static KPhysicalAddress GetPageTablePhysicalAddress(const KMemoryLayout& layout,
KVirtualAddress addr) {
return GetLinearMappedPhysicalAddress(layout, addr);
}
private:
constexpr bool IsKernel() const {
return m_is_kernel;
}
constexpr bool IsAslrEnabled() const {
return m_enable_aslr;
}
constexpr bool ContainsPages(KProcessAddress addr, size_t num_pages) const {
return (m_address_space_start <= addr) &&
(num_pages <= (m_address_space_end - m_address_space_start) / PageSize) &&
(addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
}
private:
class KScopedPageTableUpdater {
private:
KPageTable* m_pt{};
PageLinkedList m_ll;
public:
explicit KScopedPageTableUpdater(KPageTable* pt) : m_pt(pt) {}
explicit KScopedPageTableUpdater(KPageTable& pt) : KScopedPageTableUpdater(&pt) {}
~KScopedPageTableUpdater() {
m_pt->FinalizeUpdate(this->GetPageList());
}
PageLinkedList* GetPageList() {
return std::addressof(m_ll);
}
};
private:
KProcessAddress m_address_space_start{};
KProcessAddress m_address_space_end{};
KProcessAddress m_heap_region_start{};
KProcessAddress m_heap_region_end{};
KProcessAddress m_current_heap_end{};
KProcessAddress m_alias_region_start{};
KProcessAddress m_alias_region_end{};
KProcessAddress m_stack_region_start{};
KProcessAddress m_stack_region_end{};
KProcessAddress m_kernel_map_region_start{};
KProcessAddress m_kernel_map_region_end{};
KProcessAddress m_code_region_start{};
KProcessAddress m_code_region_end{};
KProcessAddress m_alias_code_region_start{};
KProcessAddress m_alias_code_region_end{};
size_t m_max_heap_size{};
size_t m_mapped_physical_memory_size{};
size_t m_mapped_unsafe_physical_memory{};
size_t m_mapped_insecure_memory{};
size_t m_mapped_ipc_server_memory{};
size_t m_address_space_width{};
KMemoryBlockManager m_memory_block_manager;
u32 m_allocate_option{};
bool m_is_kernel{};
bool m_enable_aslr{};
bool m_enable_device_address_space_merge{};
KMemoryBlockSlabManager* m_memory_block_slab_manager{};
KBlockInfoManager* m_block_info_manager{};
KResourceLimit* m_resource_limit{};
u32 m_heap_fill_value{};
u32 m_ipc_fill_value{};
u32 m_stack_fill_value{};
const KMemoryRegion* m_cached_physical_heap_region{};
KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application};
KMemoryManager::Direction m_allocation_option{KMemoryManager::Direction::FromFront};
std::unique_ptr<Common::PageTable> m_page_table_impl;
Core::System& m_system;
KernelCore& m_kernel;
Core::Memory::Memory* m_memory{};
}; };
} // namespace Kernel } // namespace Kernel

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,759 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/common_funcs.h"
#include "common/page_table.h"
#include "core/core.h"
#include "core/hle/kernel/k_dynamic_resource_manager.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_block_manager.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {
enum class DisableMergeAttribute : u8 {
None = (0U << 0),
DisableHead = (1U << 0),
DisableHeadAndBody = (1U << 1),
EnableHeadAndBody = (1U << 2),
DisableTail = (1U << 3),
EnableTail = (1U << 4),
EnableAndMergeHeadBodyTail = (1U << 5),
EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
};
DECLARE_ENUM_FLAG_OPERATORS(DisableMergeAttribute);
struct KPageProperties {
KMemoryPermission perm;
bool io;
bool uncached;
DisableMergeAttribute disable_merge_attributes;
};
static_assert(std::is_trivial_v<KPageProperties>);
static_assert(sizeof(KPageProperties) == sizeof(u32));
class KResourceLimit;
class KSystemResource;
class KPageTableBase {
YUZU_NON_COPYABLE(KPageTableBase);
YUZU_NON_MOVEABLE(KPageTableBase);
public:
using TraversalEntry = Common::PageTable::TraversalEntry;
using TraversalContext = Common::PageTable::TraversalContext;
class MemoryRange {
private:
KernelCore& m_kernel;
KPhysicalAddress m_address;
size_t m_size;
bool m_heap;
public:
explicit MemoryRange(KernelCore& kernel)
: m_kernel(kernel), m_address(0), m_size(0), m_heap(false) {}
void Set(KPhysicalAddress address, size_t size, bool heap) {
m_address = address;
m_size = size;
m_heap = heap;
}
KPhysicalAddress GetAddress() const {
return m_address;
}
size_t GetSize() const {
return m_size;
}
bool IsHeap() const {
return m_heap;
}
void Open();
void Close();
};
protected:
enum MemoryFillValue : u8 {
MemoryFillValue_Zero = 0,
MemoryFillValue_Stack = 'X',
MemoryFillValue_Ipc = 'Y',
MemoryFillValue_Heap = 'Z',
};
enum class OperationType {
Map = 0,
MapGroup = 1,
MapFirstGroup = 2,
Unmap = 3,
ChangePermissions = 4,
ChangePermissionsAndRefresh = 5,
ChangePermissionsAndRefreshAndFlush = 6,
Separate = 7,
};
static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;
static constexpr size_t RegionAlignment = 2_MiB;
static_assert(RegionAlignment == KernelAslrAlignment);
struct PageLinkedList {
private:
struct Node {
Node* m_next;
std::array<u8, PageSize - sizeof(Node*)> m_buffer;
};
static_assert(std::is_trivial_v<Node>);
private:
Node* m_root{};
public:
constexpr PageLinkedList() : m_root(nullptr) {}
void Push(Node* n) {
ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize));
n->m_next = m_root;
m_root = n;
}
Node* Peek() const {
return m_root;
}
Node* Pop() {
Node* const r = m_root;
m_root = r->m_next;
r->m_next = nullptr;
return r;
}
};
static_assert(std::is_trivially_destructible_v<PageLinkedList>);
static constexpr auto DefaultMemoryIgnoreAttr =
KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
static constexpr size_t GetAddressSpaceWidth(Svc::CreateProcessFlag as_type) {
switch (static_cast<Svc::CreateProcessFlag>(as_type &
Svc::CreateProcessFlag::AddressSpaceMask)) {
case Svc::CreateProcessFlag::AddressSpace64Bit:
return 39;
case Svc::CreateProcessFlag::AddressSpace64BitDeprecated:
return 36;
case Svc::CreateProcessFlag::AddressSpace32Bit:
case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias:
return 32;
default:
UNREACHABLE();
}
}
private:
class KScopedPageTableUpdater {
private:
KPageTableBase* m_pt;
PageLinkedList m_ll;
public:
explicit KScopedPageTableUpdater(KPageTableBase* pt) : m_pt(pt), m_ll() {}
explicit KScopedPageTableUpdater(KPageTableBase& pt)
: KScopedPageTableUpdater(std::addressof(pt)) {}
~KScopedPageTableUpdater() {
m_pt->FinalizeUpdate(this->GetPageList());
}
PageLinkedList* GetPageList() {
return std::addressof(m_ll);
}
};
private:
KernelCore& m_kernel;
Core::System& m_system;
KProcessAddress m_address_space_start{};
KProcessAddress m_address_space_end{};
KProcessAddress m_heap_region_start{};
KProcessAddress m_heap_region_end{};
KProcessAddress m_current_heap_end{};
KProcessAddress m_alias_region_start{};
KProcessAddress m_alias_region_end{};
KProcessAddress m_stack_region_start{};
KProcessAddress m_stack_region_end{};
KProcessAddress m_kernel_map_region_start{};
KProcessAddress m_kernel_map_region_end{};
KProcessAddress m_alias_code_region_start{};
KProcessAddress m_alias_code_region_end{};
KProcessAddress m_code_region_start{};
KProcessAddress m_code_region_end{};
size_t m_max_heap_size{};
size_t m_mapped_physical_memory_size{};
size_t m_mapped_unsafe_physical_memory{};
size_t m_mapped_insecure_memory{};
size_t m_mapped_ipc_server_memory{};
mutable KLightLock m_general_lock;
mutable KLightLock m_map_physical_memory_lock;
KLightLock m_device_map_lock;
std::unique_ptr<Common::PageTable> m_impl{};
Core::Memory::Memory* m_memory{};
KMemoryBlockManager m_memory_block_manager{};
u32 m_allocate_option{};
u32 m_address_space_width{};
bool m_is_kernel{};
bool m_enable_aslr{};
bool m_enable_device_address_space_merge{};
KMemoryBlockSlabManager* m_memory_block_slab_manager{};
KBlockInfoManager* m_block_info_manager{};
KResourceLimit* m_resource_limit{};
const KMemoryRegion* m_cached_physical_linear_region{};
const KMemoryRegion* m_cached_physical_heap_region{};
MemoryFillValue m_heap_fill_value{};
MemoryFillValue m_ipc_fill_value{};
MemoryFillValue m_stack_fill_value{};
public:
explicit KPageTableBase(KernelCore& kernel);
~KPageTableBase();
Result InitializeForKernel(bool is_64_bit, KVirtualAddress start, KVirtualAddress end,
Core::Memory::Memory& memory);
Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
bool enable_device_address_space_merge, bool from_back,
KMemoryManager::Pool pool, KProcessAddress code_address,
size_t code_size, KSystemResource* system_resource,
KResourceLimit* resource_limit, Core::Memory::Memory& memory);
void Finalize();
bool IsKernel() const {
return m_is_kernel;
}
bool IsAslrEnabled() const {
return m_enable_aslr;
}
bool Contains(KProcessAddress addr) const {
return m_address_space_start <= addr && addr <= m_address_space_end - 1;
}
bool Contains(KProcessAddress addr, size_t size) const {
return m_address_space_start <= addr && addr < addr + size &&
addr + size - 1 <= m_address_space_end - 1;
}
bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
return this->Contains(addr, size) && m_alias_region_start <= addr &&
addr + size - 1 <= m_alias_region_end - 1;
}
bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
return this->Contains(addr, size) && m_heap_region_start <= addr &&
addr + size - 1 <= m_heap_region_end - 1;
}
bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const {
// Even though Unsafe physical memory is KMemoryState_Normal, it must be mapped inside the
// alias code region.
return this->CanContain(addr, size, Svc::MemoryState::AliasCode);
}
KScopedLightLock AcquireDeviceMapLock() {
return KScopedLightLock(m_device_map_lock);
}
KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
size_t GetRegionSize(Svc::MemoryState state) const;
bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
KProcessAddress GetRegionAddress(KMemoryState state) const {
return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
size_t GetRegionSize(KMemoryState state) const {
return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
return this->CanContain(addr, size,
static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
public:
Core::Memory::Memory& GetMemory() {
return *m_memory;
}
Core::Memory::Memory& GetMemory() const {
return *m_memory;
}
Common::PageTable& GetImpl() {
return *m_impl;
}
Common::PageTable& GetImpl() const {
return *m_impl;
}
size_t GetNumGuardPages() const {
return this->IsKernel() ? 1 : 4;
}
protected:
// NOTE: These three functions (Operate, Operate, FinalizeUpdate) are virtual functions
// in Nintendo's kernel. We devirtualize them, since KPageTable is the only derived
// class, and this avoids unnecessary virtual function calls.
Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages,
KPhysicalAddress phys_addr, bool is_pa_valid, const KPageProperties properties,
OperationType operation, bool reuse_ll);
Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages,
const KPageGroup& page_group, const KPageProperties properties,
OperationType operation, bool reuse_ll);
void FinalizeUpdate(PageLinkedList* page_list);
bool IsLockedByCurrentThread() const {
return m_general_lock.IsLockedByCurrentThread();
}
bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress(
m_cached_physical_linear_region, phys_addr);
}
bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress(
m_cached_physical_linear_region, phys_addr, size);
}
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
phys_addr);
}
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
phys_addr, size);
}
bool IsHeapPhysicalAddressForFinalize(KPhysicalAddress phys_addr) {
ASSERT(!this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
phys_addr);
}
bool ContainsPages(KProcessAddress addr, size_t num_pages) const {
return (m_address_space_start <= addr) &&
(num_pages <= (m_address_space_end - m_address_space_start) / PageSize) &&
(addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
}
private:
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
size_t num_pages, size_t alignment, size_t offset,
size_t guard_pages) const;
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const {
R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr));
}
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(this->CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr,
ignore_attr));
}
Result CheckMemoryState(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
attr_mask, attr, ignore_attr));
}
Result LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_paddr, KProcessAddress addr,
size_t size, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr);
Result UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr,
const KPageGroup* pg);
Result QueryInfoImpl(KMemoryInfo* out_info, Svc::PageInfo* out_page,
KProcessAddress address) const;
Result QueryMappingImpl(KProcessAddress* out, KPhysicalAddress address, size_t size,
Svc::MemoryState state) const;
Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
size_t num_pages, KMemoryPermission perm);
Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
const KPageGroup& pg);
Result MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages);
bool IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages);
Result GetContiguousMemoryRangeWithState(MemoryRange* out, KProcessAddress address, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr);
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
Result MapIoImpl(KProcessAddress* out, PageLinkedList* page_list, KPhysicalAddress phys_addr,
size_t size, KMemoryState state, KMemoryPermission perm);
Result ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddress phys_addr, size_t size,
KMemoryState state);
Result WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAddress src_addr, size_t size,
KMemoryState state);
Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
KProcessAddress address, size_t size, KMemoryPermission test_perm,
KMemoryState dst_state);
Result SetupForIpcServer(KProcessAddress* out_addr, size_t size, KProcessAddress src_addr,
KMemoryPermission test_perm, KMemoryState dst_state,
KPageTableBase& src_page_table, bool send);
void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, KProcessAddress address,
size_t size, KMemoryPermission prot_perm);
size_t GetSize(KMemoryState state) const;
bool GetPhysicalAddressLocked(KPhysicalAddress* out, KProcessAddress virt_addr) const {
// Validate pre-conditions.
ASSERT(this->IsLockedByCurrentThread());
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
}
public:
bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress virt_addr) const {
// Validate pre-conditions.
ASSERT(!this->IsLockedByCurrentThread());
// Acquire exclusive access to the table while doing address translation.
KScopedLightLock lk(m_general_lock);
return this->GetPhysicalAddressLocked(out, virt_addr);
}
KBlockInfoManager* GetBlockInfoManager() const {
return m_block_info_manager;
}
Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm);
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
Svc::MemoryPermission perm);
Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask,
KMemoryAttribute attr);
Result SetHeapSize(KProcessAddress* out, size_t size);
Result SetMaxHeapSize(size_t size);
Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info,
KProcessAddress addr) const;
Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) const;
Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const {
R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Static));
}
Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const {
R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Io));
}
Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
Svc::MemoryMapping mapping, Svc::MemoryPermission perm);
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
Svc::MemoryMapping mapping);
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm);
Result MapInsecureMemory(KProcessAddress address, size_t size);
Result UnmapInsecureMemory(KProcessAddress address, size_t size);
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, KProcessAddress region_start,
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
region_num_pages, state, perm));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
this->GetRegionAddress(state),
this->GetRegionSize(state) / PageSize, state, perm));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
this->GetRegionAddress(state),
this->GetRegionSize(state) / PageSize, state, perm));
}
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
KMemoryPermission perm);
Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
KMemoryPermission perm);
Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
KMemoryPermission perm);
Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr);
Result InvalidateProcessDataCache(KProcessAddress address, size_t size);
Result InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size);
Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
KMemoryState state);
Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
KMemoryState state);
Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
KMemoryPermission perm, bool is_aligned, bool check_heap);
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap);
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size);
Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size);
Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
KProcessAddress address, size_t size,
KMemoryPermission perm, bool is_aligned);
Result OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange* out, KProcessAddress address,
size_t size);
Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size);
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size);
Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
KMemoryPermission perm);
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size);
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
Result OpenMemoryRangeForProcessCacheOperation(MemoryRange* out, KProcessAddress address,
size_t size);
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size,
KProcessAddress src_addr, KMemoryState src_state_mask,
KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
Result CopyMemoryFromLinearToKernel(void* buffer, size_t size, KProcessAddress src_addr,
KMemoryState src_state_mask, KMemoryState src_state,
KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state,
KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
KProcessAddress src_addr);
Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state,
KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
void* buffer);
Result CopyMemoryFromHeapToHeap(KPageTableBase& dst_page_table, KProcessAddress dst_addr,
size_t size, KMemoryState dst_state_mask,
KMemoryState dst_state, KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
KProcessAddress src_addr, KMemoryState src_state_mask,
KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
Result CopyMemoryFromHeapToHeapWithoutCheckDestination(
KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
KPageTableBase& src_page_table, KMemoryPermission test_perm,
KMemoryState dst_state, bool send);
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
Result MapPhysicalMemory(KProcessAddress address, size_t size);
Result UnmapPhysicalMemory(KProcessAddress address, size_t size);
Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KPageTableBase& src_pt,
KProcessAddress src_address);
public:
KProcessAddress GetAddressSpaceStart() const {
return m_address_space_start;
}
KProcessAddress GetHeapRegionStart() const {
return m_heap_region_start;
}
KProcessAddress GetAliasRegionStart() const {
return m_alias_region_start;
}
KProcessAddress GetStackRegionStart() const {
return m_stack_region_start;
}
KProcessAddress GetKernelMapRegionStart() const {
return m_kernel_map_region_start;
}
KProcessAddress GetCodeRegionStart() const {
return m_code_region_start;
}
KProcessAddress GetAliasCodeRegionStart() const {
return m_alias_code_region_start;
}
size_t GetAddressSpaceSize() const {
return m_address_space_end - m_address_space_start;
}
size_t GetHeapRegionSize() const {
return m_heap_region_end - m_heap_region_start;
}
size_t GetAliasRegionSize() const {
return m_alias_region_end - m_alias_region_start;
}
size_t GetStackRegionSize() const {
return m_stack_region_end - m_stack_region_start;
}
size_t GetKernelMapRegionSize() const {
return m_kernel_map_region_end - m_kernel_map_region_start;
}
size_t GetCodeRegionSize() const {
return m_code_region_end - m_code_region_start;
}
size_t GetAliasCodeRegionSize() const {
return m_alias_code_region_end - m_alias_code_region_start;
}
size_t GetNormalMemorySize() const {
// Lock the table.
KScopedLightLock lk(m_general_lock);
return (m_current_heap_end - m_heap_region_start) + m_mapped_physical_memory_size;
}
size_t GetCodeSize() const;
size_t GetCodeDataSize() const;
size_t GetAliasCodeSize() const;
size_t GetAliasCodeDataSize() const;
u32 GetAllocateOption() const {
return m_allocate_option;
}
u32 GetAddressSpaceWidth() const {
return m_address_space_width;
}
public:
// Linear mapped
static u8* GetLinearMappedVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) {
return kernel.System().DeviceMemory().GetPointer<u8>(addr);
}
static KPhysicalAddress GetLinearMappedPhysicalAddress(KernelCore& kernel,
KVirtualAddress addr) {
return kernel.MemoryLayout().GetLinearPhysicalAddress(addr);
}
static KVirtualAddress GetLinearMappedVirtualAddress(KernelCore& kernel,
KPhysicalAddress addr) {
return kernel.MemoryLayout().GetLinearVirtualAddress(addr);
}
// Heap
static u8* GetHeapVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) {
return kernel.System().DeviceMemory().GetPointer<u8>(addr);
}
static KPhysicalAddress GetHeapPhysicalAddress(KernelCore& kernel, KVirtualAddress addr) {
return GetLinearMappedPhysicalAddress(kernel, addr);
}
static KVirtualAddress GetHeapVirtualAddress(KernelCore& kernel, KPhysicalAddress addr) {
return GetLinearMappedVirtualAddress(kernel, addr);
}
// Member heap
u8* GetHeapVirtualPointer(KPhysicalAddress addr) {
return GetHeapVirtualPointer(m_kernel, addr);
}
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress addr) {
return GetHeapPhysicalAddress(m_kernel, addr);
}
KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) {
return GetHeapVirtualAddress(m_kernel, addr);
}
// TODO: GetPageTableVirtualAddress
// TODO: GetPageTablePhysicalAddress
};
} // namespace Kernel

View file

@ -298,9 +298,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr);
const bool enable_das_merge = const bool enable_das_merge =
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
R_TRY(m_page_table.InitializeForProcess( R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, params.code_address, params.code_num_pages * PageSize,
params.code_num_pages * PageSize, m_system_resource, res_limit, this->GetMemory())); m_system_resource, res_limit, this->GetMemory()));
} }
ON_RESULT_FAILURE_2 { ON_RESULT_FAILURE_2 {
m_page_table.Finalize(); m_page_table.Finalize();
@ -391,9 +391,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr);
const bool enable_das_merge = const bool enable_das_merge =
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
R_TRY(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge, R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
!enable_aslr, pool, params.code_address, code_size, params.code_address, code_size, m_system_resource, res_limit,
m_system_resource, res_limit, this->GetMemory())); this->GetMemory()));
} }
ON_RESULT_FAILURE_2 { ON_RESULT_FAILURE_2 {
m_page_table.Finalize(); m_page_table.Finalize();
@ -1122,9 +1122,9 @@ Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_
void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {} void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
KProcess::KProcess(KernelCore& kernel) KProcess::KProcess(KernelCore& kernel)
: KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel.System()}, : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
m_state_lock{kernel}, m_list_lock{kernel}, m_cond_var{kernel.System()}, m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
m_address_arbiter{kernel.System()}, m_handle_table{kernel} {} m_handle_table{kernel} {}
KProcess::~KProcess() = default; KProcess::~KProcess() = default;
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,

View file

@ -5,13 +5,14 @@
#include <map> #include <map>
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_capabilities.h" #include "core/hle/kernel/k_capabilities.h"
#include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_page_table_manager.h" #include "core/hle/kernel/k_page_table_manager.h"
#include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/k_system_resource.h" #include "core/hle/kernel/k_system_resource.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_local_page.h" #include "core/hle/kernel/k_thread_local_page.h"
@ -65,7 +66,7 @@ private:
using TLPIterator = TLPTree::iterator; using TLPIterator = TLPTree::iterator;
private: private:
KPageTable m_page_table; KProcessPageTable m_page_table;
std::atomic<size_t> m_used_kernel_memory_size{}; std::atomic<size_t> m_used_kernel_memory_size{};
TLPTree m_fully_used_tlp_tree{}; TLPTree m_fully_used_tlp_tree{};
TLPTree m_partially_used_tlp_tree{}; TLPTree m_partially_used_tlp_tree{};
@ -254,9 +255,8 @@ public:
return m_is_hbl; return m_is_hbl;
} }
Kernel::KMemoryManager::Direction GetAllocateOption() const { u32 GetAllocateOption() const {
// TODO: property of the KPageTableBase return m_page_table.GetAllocateOption();
return KMemoryManager::Direction::FromFront;
} }
ThreadList& GetThreadList() { ThreadList& GetThreadList() {
@ -295,10 +295,10 @@ public:
return m_list_lock; return m_list_lock;
} }
KPageTable& GetPageTable() { KProcessPageTable& GetPageTable() {
return m_page_table; return m_page_table;
} }
const KPageTable& GetPageTable() const { const KProcessPageTable& GetPageTable() const {
return m_page_table; return m_page_table;
} }

View file

@ -0,0 +1,480 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_scoped_lock.h"
#include "core/hle/kernel/svc_types.h"
namespace Core {
class ARM_Interface;
}
namespace Kernel {
class KProcessPageTable {
private:
KPageTable m_page_table;
public:
KProcessPageTable(KernelCore& kernel) : m_page_table(kernel) {}
Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge,
bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address,
size_t code_size, KSystemResource* system_resource,
KResourceLimit* resource_limit, Core::Memory::Memory& memory) {
R_RETURN(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge,
from_back, pool, code_address, code_size,
system_resource, resource_limit, memory));
}
void Finalize() {
m_page_table.Finalize();
}
Core::Memory::Memory& GetMemory() {
return m_page_table.GetMemory();
}
Core::Memory::Memory& GetMemory() const {
return m_page_table.GetMemory();
}
Common::PageTable& GetImpl() {
return m_page_table.GetImpl();
}
Common::PageTable& GetImpl() const {
return m_page_table.GetImpl();
}
size_t GetNumGuardPages() const {
return m_page_table.GetNumGuardPages();
}
KScopedLightLock AcquireDeviceMapLock() {
return m_page_table.AcquireDeviceMapLock();
}
Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm) {
R_RETURN(m_page_table.SetMemoryPermission(addr, size, perm));
}
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
Svc::MemoryPermission perm) {
R_RETURN(m_page_table.SetProcessMemoryPermission(addr, size, perm));
}
Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask,
KMemoryAttribute attr) {
R_RETURN(m_page_table.SetMemoryAttribute(addr, size, mask, attr));
}
Result SetHeapSize(KProcessAddress* out, size_t size) {
R_RETURN(m_page_table.SetHeapSize(out, size));
}
Result SetMaxHeapSize(size_t size) {
R_RETURN(m_page_table.SetMaxHeapSize(size));
}
Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info,
KProcessAddress addr) const {
R_RETURN(m_page_table.QueryInfo(out_info, out_page_info, addr));
}
Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) {
R_RETURN(m_page_table.QueryPhysicalAddress(out, address));
}
Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) {
R_RETURN(m_page_table.QueryStaticMapping(out, address, size));
}
Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) {
R_RETURN(m_page_table.QueryIoMapping(out, address, size));
}
Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.MapMemory(dst_address, src_address, size));
}
Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.UnmapMemory(dst_address, src_address, size));
}
Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.MapCodeMemory(dst_address, src_address, size));
}
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.UnmapCodeMemory(dst_address, src_address, size));
}
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
R_RETURN(m_page_table.MapIo(phys_addr, size, perm));
}
Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
Svc::MemoryMapping mapping, Svc::MemoryPermission perm) {
R_RETURN(m_page_table.MapIoRegion(dst_address, phys_addr, size, mapping, perm));
}
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
Svc::MemoryMapping mapping) {
R_RETURN(m_page_table.UnmapIoRegion(dst_address, phys_addr, size, mapping));
}
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
R_RETURN(m_page_table.MapStatic(phys_addr, size, perm));
}
Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) {
R_RETURN(m_page_table.MapRegion(region_type, perm));
}
Result MapInsecureMemory(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.MapInsecureMemory(address, size));
}
Result UnmapInsecureMemory(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnmapInsecureMemory(address, size));
}
Result MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
KMemoryPermission perm) {
R_RETURN(m_page_table.MapPageGroup(addr, pg, state, perm));
}
Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state) {
R_RETURN(m_page_table.UnmapPageGroup(address, pg, state));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
R_RETURN(m_page_table.MapPages(out_addr, num_pages, alignment, phys_addr, state, perm));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
KMemoryPermission perm) {
R_RETURN(m_page_table.MapPages(out_addr, num_pages, state, perm));
}
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
KMemoryPermission perm) {
R_RETURN(m_page_table.MapPages(address, num_pages, state, perm));
}
Result UnmapPages(KProcessAddress addr, size_t num_pages, KMemoryState state) {
R_RETURN(m_page_table.UnmapPages(addr, num_pages, state));
}
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) {
R_RETURN(m_page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state,
perm_mask, perm, attr_mask, attr));
}
Result InvalidateProcessDataCache(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.InvalidateProcessDataCache(address, size));
}
Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.ReadDebugMemory(dst_address, src_address, size));
}
Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
KMemoryState state) {
R_RETURN(m_page_table.ReadDebugIoMemory(dst_address, src_address, size, state));
}
Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.WriteDebugMemory(dst_address, src_address, size));
}
Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
KMemoryState state) {
R_RETURN(m_page_table.WriteDebugIoMemory(dst_address, src_address, size, state));
}
Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
KMemoryPermission perm, bool is_aligned, bool check_heap) {
R_RETURN(m_page_table.LockForMapDeviceAddressSpace(out_is_io, address, size, perm,
is_aligned, check_heap));
}
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap) {
R_RETURN(m_page_table.LockForUnmapDeviceAddressSpace(address, size, check_heap));
}
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnlockForDeviceAddressSpace(address, size));
}
Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnlockForDeviceAddressSpacePartialMap(address, size));
}
Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
KProcessAddress address, size_t size,
KMemoryPermission perm, bool is_aligned) {
R_RETURN(m_page_table.OpenMemoryRangeForMapDeviceAddressSpace(out, address, size, perm,
is_aligned));
}
Result OpenMemoryRangeForUnmapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
KProcessAddress address, size_t size) {
R_RETURN(m_page_table.OpenMemoryRangeForUnmapDeviceAddressSpace(out, address, size));
}
Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size) {
R_RETURN(m_page_table.LockForIpcUserBuffer(out, address, size));
}
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnlockForIpcUserBuffer(address, size));
}
Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
KMemoryPermission perm) {
R_RETURN(m_page_table.LockForTransferMemory(out, address, size, perm));
}
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg) {
R_RETURN(m_page_table.UnlockForTransferMemory(address, size, pg));
}
Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size) {
R_RETURN(m_page_table.LockForCodeMemory(out, address, size));
}
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg) {
R_RETURN(m_page_table.UnlockForCodeMemory(address, size, pg));
}
Result OpenMemoryRangeForProcessCacheOperation(KPageTableBase::MemoryRange* out,
KProcessAddress address, size_t size) {
R_RETURN(m_page_table.OpenMemoryRangeForProcessCacheOperation(out, address, size));
}
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size,
KProcessAddress src_addr, KMemoryState src_state_mask,
KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
R_RETURN(m_page_table.CopyMemoryFromLinearToUser(dst_addr, size, src_addr, src_state_mask,
src_state, src_test_perm, src_attr_mask,
src_attr));
}
Result CopyMemoryFromLinearToKernel(void* dst_addr, size_t size, KProcessAddress src_addr,
KMemoryState src_state_mask, KMemoryState src_state,
KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
R_RETURN(m_page_table.CopyMemoryFromLinearToKernel(dst_addr, size, src_addr, src_state_mask,
src_state, src_test_perm, src_attr_mask,
src_attr));
}
Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state,
KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
KProcessAddress src_addr) {
R_RETURN(m_page_table.CopyMemoryFromUserToLinear(dst_addr, size, dst_state_mask, dst_state,
dst_test_perm, dst_attr_mask, dst_attr,
src_addr));
}
Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state,
KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
void* src_addr) {
R_RETURN(m_page_table.CopyMemoryFromKernelToLinear(dst_addr, size, dst_state_mask,
dst_state, dst_test_perm, dst_attr_mask,
dst_attr, src_addr));
}
Result CopyMemoryFromHeapToHeap(KProcessPageTable& dst_page_table, KProcessAddress dst_addr,
size_t size, KMemoryState dst_state_mask,
KMemoryState dst_state, KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
KProcessAddress src_addr, KMemoryState src_state_mask,
KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
R_RETURN(m_page_table.CopyMemoryFromHeapToHeap(
dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm,
dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm,
src_attr_mask, src_attr));
}
Result CopyMemoryFromHeapToHeapWithoutCheckDestination(
KProcessPageTable& dst_page_table, KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
R_RETURN(m_page_table.CopyMemoryFromHeapToHeapWithoutCheckDestination(
dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm,
dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm,
src_attr_mask, src_attr));
}
Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
KProcessPageTable& src_page_table, KMemoryPermission test_perm,
KMemoryState dst_state, bool send) {
R_RETURN(m_page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.m_page_table,
test_perm, dst_state, send));
}
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) {
R_RETURN(m_page_table.CleanupForIpcServer(address, size, dst_state));
}
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) {
R_RETURN(m_page_table.CleanupForIpcClient(address, size, dst_state));
}
Result MapPhysicalMemory(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.MapPhysicalMemory(address, size));
}
Result UnmapPhysicalMemory(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnmapPhysicalMemory(address, size));
}
Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.MapPhysicalMemoryUnsafe(address, size));
}
Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnmapPhysicalMemoryUnsafe(address, size));
}
Result UnmapProcessMemory(KProcessAddress dst_address, size_t size,
KProcessPageTable& src_page_table, KProcessAddress src_address) {
R_RETURN(m_page_table.UnmapProcessMemory(dst_address, size, src_page_table.m_page_table,
src_address));
}
bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress address) {
return m_page_table.GetPhysicalAddress(out, address);
}
bool Contains(KProcessAddress addr, size_t size) const {
return m_page_table.Contains(addr, size);
}
bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
return m_page_table.IsInAliasRegion(addr, size);
}
bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
return m_page_table.IsInHeapRegion(addr, size);
}
bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const {
return m_page_table.IsInUnsafeAliasRegion(addr, size);
}
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
return m_page_table.CanContain(addr, size, state);
}
KProcessAddress GetAddressSpaceStart() const {
return m_page_table.GetAddressSpaceStart();
}
KProcessAddress GetHeapRegionStart() const {
return m_page_table.GetHeapRegionStart();
}
KProcessAddress GetAliasRegionStart() const {
return m_page_table.GetAliasRegionStart();
}
KProcessAddress GetStackRegionStart() const {
return m_page_table.GetStackRegionStart();
}
KProcessAddress GetKernelMapRegionStart() const {
return m_page_table.GetKernelMapRegionStart();
}
KProcessAddress GetCodeRegionStart() const {
return m_page_table.GetCodeRegionStart();
}
KProcessAddress GetAliasCodeRegionStart() const {
return m_page_table.GetAliasCodeRegionStart();
}
size_t GetAddressSpaceSize() const {
return m_page_table.GetAddressSpaceSize();
}
size_t GetHeapRegionSize() const {
return m_page_table.GetHeapRegionSize();
}
size_t GetAliasRegionSize() const {
return m_page_table.GetAliasRegionSize();
}
size_t GetStackRegionSize() const {
return m_page_table.GetStackRegionSize();
}
size_t GetKernelMapRegionSize() const {
return m_page_table.GetKernelMapRegionSize();
}
size_t GetCodeRegionSize() const {
return m_page_table.GetCodeRegionSize();
}
size_t GetAliasCodeRegionSize() const {
return m_page_table.GetAliasCodeRegionSize();
}
size_t GetNormalMemorySize() const {
return m_page_table.GetNormalMemorySize();
}
size_t GetCodeSize() const {
return m_page_table.GetCodeSize();
}
size_t GetCodeDataSize() const {
return m_page_table.GetCodeDataSize();
}
size_t GetAliasCodeSize() const {
return m_page_table.GetAliasCodeSize();
}
size_t GetAliasCodeDataSize() const {
return m_page_table.GetAliasCodeDataSize();
}
u32 GetAllocateOption() const {
return m_page_table.GetAllocateOption();
}
u32 GetAddressSpaceWidth() const {
return m_page_table.GetAddressSpaceWidth();
}
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) {
return m_page_table.GetHeapPhysicalAddress(address);
}
u8* GetHeapVirtualPointer(KPhysicalAddress address) {
return m_page_table.GetHeapVirtualPointer(address);
}
KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress address) {
return m_page_table.GetHeapVirtualAddress(address);
}
KBlockInfoManager* GetBlockInfoManager() {
return m_page_table.GetBlockInfoManager();
}
KPageTable& GetBasePageTable() {
return m_page_table;
}
const KPageTable& GetBasePageTable() const {
return m_page_table;
}
};
} // namespace Kernel

View file

@ -383,7 +383,7 @@ Result KServerSession::SendReply(bool is_hle) {
if (event != nullptr) { if (event != nullptr) {
// // Get the client process/page table. // // Get the client process/page table.
// KProcess *client_process = client_thread->GetOwnerProcess(); // KProcess *client_process = client_thread->GetOwnerProcess();
// KPageTable *client_page_table = std::addressof(client_process->PageTable()); // KProcessPageTable *client_page_table = std::addressof(client_process->PageTable());
// // If we need to, reply with an async error. // // If we need to, reply with an async error.
// if (R_FAILED(client_result)) { // if (R_FAILED(client_result)) {

View file

@ -40,7 +40,7 @@ Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_l
// Get resource pointer. // Get resource pointer.
KPhysicalAddress resource_paddr = KPhysicalAddress resource_paddr =
KPageTable::GetHeapPhysicalAddress(m_kernel.MemoryLayout(), m_resource_address); KPageTable::GetHeapPhysicalAddress(m_kernel, m_resource_address);
auto* resource = auto* resource =
m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr); m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr);

View file

@ -37,8 +37,8 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
Result KThreadLocalPage::Finalize() { Result KThreadLocalPage::Finalize() {
// Get the physical address of the page. // Get the physical address of the page.
const KPhysicalAddress phys_addr = m_owner->GetPageTable().GetPhysicalAddr(m_virt_addr); KPhysicalAddress phys_addr{};
ASSERT(phys_addr); ASSERT(m_owner->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), m_virt_addr));
// Unmap the page. // Unmap the page.
R_TRY(m_owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); R_TRY(m_owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal));

View file

@ -29,7 +29,8 @@ constexpr bool IsValidAddressRange(u64 address, u64 size) {
// Helper function that performs the common sanity checks for svcMapMemory // Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing // and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order. // in the same order.
Result MapUnmapMemorySanityChecks(const KPageTable& manager, u64 dst_addr, u64 src_addr, u64 size) { Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr, u64 src_addr,
u64 size) {
if (!Common::Is4KBAligned(dst_addr)) { if (!Common::Is4KBAligned(dst_addr)) {
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
R_THROW(ResultInvalidAddress); R_THROW(ResultInvalidAddress);
@ -123,7 +124,8 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask,
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
// Set the memory attribute. // Set the memory attribute.
R_RETURN(page_table.SetMemoryAttribute(address, size, mask, attr)); R_RETURN(page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask),
static_cast<KMemoryAttribute>(attr)));
} }
/// Maps a memory range into a different range. /// Maps a memory range into a different range.

View file

@ -16,7 +16,14 @@ Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
// Set the heap size. // Set the heap size.
R_RETURN(GetCurrentProcess(system.Kernel()).GetPageTable().SetHeapSize(out_address, size)); KProcessAddress address{};
R_TRY(GetCurrentProcess(system.Kernel())
.GetPageTable()
.SetHeapSize(std::addressof(address), size));
// We succeeded.
*out_address = GetInteger(address);
R_SUCCEED();
} }
/// Maps memory at a desired address /// Maps memory at a desired address

View file

@ -247,8 +247,7 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
R_THROW(ResultInvalidCurrentMemory); R_THROW(ResultInvalidCurrentMemory);
} }
R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size, R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size));
KPageTable::ICacheInvalidationStrategy::InvalidateAll));
} }
Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address, Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address,

View file

@ -31,12 +31,12 @@ Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageIn
} }
auto& current_memory{GetCurrentMemory(system.Kernel())}; auto& current_memory{GetCurrentMemory(system.Kernel())};
const auto memory_info{process->GetPageTable().QueryInfo(address).GetSvcMemoryInfo()};
current_memory.WriteBlock(out_memory_info, std::addressof(memory_info), sizeof(memory_info)); KMemoryInfo mem_info;
R_TRY(process->GetPageTable().QueryInfo(std::addressof(mem_info), out_page_info, address));
//! This is supposed to be part of the QueryInfo call. const auto svc_mem_info = mem_info.GetSvcMemoryInfo();
*out_page_info = {}; current_memory.WriteBlock(out_memory_info, std::addressof(svc_mem_info), sizeof(svc_mem_info));
R_SUCCEED(); R_SUCCEED();
} }

View file

@ -407,3 +407,34 @@ constexpr inline Result __TmpCurrentResultReference = ResultSuccess;
/// Evaluates a boolean expression, and succeeds if that expression is true. /// Evaluates a boolean expression, and succeeds if that expression is true.
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) #define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess)
#define R_TRY_CATCH(res_expr) \
{ \
const auto R_CURRENT_RESULT = (res_expr); \
if (R_FAILED(R_CURRENT_RESULT)) { \
if (false)
#define R_END_TRY_CATCH \
else if (R_FAILED(R_CURRENT_RESULT)) { \
R_THROW(R_CURRENT_RESULT); \
} \
} \
}
#define R_CATCH_ALL() \
} \
else if (R_FAILED(R_CURRENT_RESULT)) { \
if (true)
#define R_CATCH(res_expr) \
} \
else if ((res_expr) == (R_CURRENT_RESULT)) { \
if (true)
#define R_CONVERT(catch_type, convert_type) \
R_CATCH(catch_type) { R_THROW(static_cast<Result>(convert_type)); }
#define R_CONVERT_ALL(convert_type) \
R_CATCH_ALL() { R_THROW(static_cast<Result>(convert_type)); }
#define R_ASSERT(res_expr) ASSERT(R_SUCCEEDED(res_expr))

View file

@ -1108,9 +1108,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
shared_memory->sixaxis_dual_right_properties.raw = 0; shared_memory->sixaxis_dual_right_properties.raw = 0;
shared_memory->sixaxis_left_properties.raw = 0; shared_memory->sixaxis_left_properties.raw = 0;
shared_memory->sixaxis_right_properties.raw = 0; shared_memory->sixaxis_right_properties.raw = 0;
shared_memory->battery_level_dual = 0; shared_memory->battery_level_dual = Core::HID::NpadBatteryLevel::Empty;
shared_memory->battery_level_left = 0; shared_memory->battery_level_left = Core::HID::NpadBatteryLevel::Empty;
shared_memory->battery_level_right = 0; shared_memory->battery_level_right = Core::HID::NpadBatteryLevel::Empty;
shared_memory->fullkey_color = { shared_memory->fullkey_color = {
.attribute = ColorAttribute::NoController, .attribute = ColorAttribute::NoController,
.fullkey = {}, .fullkey = {},

View file

@ -32,15 +32,15 @@ struct Lifo {
} }
std::size_t GetPreviousEntryIndex() const { std::size_t GetPreviousEntryIndex() const {
return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count); return static_cast<size_t>((buffer_tail + max_buffer_size - 1) % max_buffer_size);
} }
std::size_t GetNextEntryIndex() const { std::size_t GetNextEntryIndex() const {
return static_cast<size_t>((buffer_tail + 1) % total_buffer_count); return static_cast<size_t>((buffer_tail + 1) % max_buffer_size);
} }
void WriteNextEntry(const State& new_state) { void WriteNextEntry(const State& new_state) {
if (buffer_count < total_buffer_count - 1) { if (buffer_count < static_cast<s64>(max_buffer_size) - 1) {
buffer_count++; buffer_count++;
} }
buffer_tail = GetNextEntryIndex(); buffer_tail = GetNextEntryIndex();

View file

@ -286,9 +286,14 @@ public:
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
} }
bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const { bool ValidateRegionForMap(Kernel::KProcessPageTable& page_table, VAddr start,
std::size_t size) const {
const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize}; const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
const auto start_info{page_table.QueryInfo(start - 1)};
Kernel::KMemoryInfo start_info;
Kernel::Svc::PageInfo page_info;
R_ASSERT(
page_table.QueryInfo(std::addressof(start_info), std::addressof(page_info), start - 1));
if (start_info.GetState() != Kernel::KMemoryState::Free) { if (start_info.GetState() != Kernel::KMemoryState::Free) {
return {}; return {};
@ -298,7 +303,9 @@ public:
return {}; return {};
} }
const auto end_info{page_table.QueryInfo(start + size)}; Kernel::KMemoryInfo end_info;
R_ASSERT(page_table.QueryInfo(std::addressof(end_info), std::addressof(page_info),
start + size));
if (end_info.GetState() != Kernel::KMemoryState::Free) { if (end_info.GetState() != Kernel::KMemoryState::Free) {
return {}; return {};
@ -307,7 +314,7 @@ public:
return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
} }
Result GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) { Result GetAvailableMapRegion(Kernel::KProcessPageTable& page_table, u64 size, VAddr& out_addr) {
size = Common::AlignUp(size, Kernel::PageSize); size = Common::AlignUp(size, Kernel::PageSize);
size += page_table.GetNumGuardPages() * Kernel::PageSize * 4; size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
@ -391,12 +398,8 @@ public:
if (bss_size) { if (bss_size) {
auto block_guard = detail::ScopeExit([&] { auto block_guard = detail::ScopeExit([&] {
page_table.UnmapCodeMemory( page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size);
addr + nro_size, bss_addr, bss_size, page_table.UnmapCodeMemory(addr, nro_addr, nro_size);
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
page_table.UnmapCodeMemory(
addr, nro_addr, nro_size,
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
}); });
const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)}; const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)};
@ -578,21 +581,17 @@ public:
auto& page_table{system.ApplicationProcess()->GetPageTable()}; auto& page_table{system.ApplicationProcess()->GetPageTable()};
if (info.bss_size != 0) { if (info.bss_size != 0) {
R_TRY(page_table.UnmapCodeMemory( R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size +
info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address, info.data_size,
info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); info.bss_address, info.bss_size));
} }
R_TRY(page_table.UnmapCodeMemory( R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size,
info.nro_address + info.text_size + info.ro_size, info.src_addr + info.text_size + info.ro_size,
info.src_addr + info.text_size + info.ro_size, info.data_size, info.data_size));
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size,
R_TRY(page_table.UnmapCodeMemory( info.src_addr + info.text_size, info.ro_size));
info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size, R_TRY(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size));
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
R_TRY(page_table.UnmapCodeMemory(
info.nro_address, info.src_addr, info.text_size,
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
return ResultSuccess; return ResultSuccess;
} }

View file

@ -15,7 +15,7 @@
namespace Service::android { namespace Service::android {
struct GraphicBuffer; class GraphicBuffer;
class BufferItem final { class BufferItem final {
public: public:

View file

@ -5,7 +5,6 @@
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp // https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvnflinger/buffer_item.h" #include "core/hle/service/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_queue_consumer.h" #include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h" #include "core/hle/service/nvnflinger/buffer_queue_core.h"
@ -14,9 +13,8 @@
namespace Service::android { namespace Service::android {
BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
Service::Nvidia::NvCore::NvMap& nvmap_) : core{std::move(core_)}, slots{core->slots} {}
: core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
BufferQueueConsumer::~BufferQueueConsumer() = default; BufferQueueConsumer::~BufferQueueConsumer() = default;
@ -136,8 +134,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
slots[slot].buffer_state = BufferState::Free; slots[slot].buffer_state = BufferState::Free;
nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true);
listener = core->connected_producer_listener; listener = core->connected_producer_listener;
LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot); LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot);
@ -175,6 +171,25 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
return Status::NoError; return Status::NoError;
} }
Status BufferQueueConsumer::Disconnect() {
LOG_DEBUG(Service_Nvnflinger, "called");
std::scoped_lock lock{core->mutex};
if (core->consumer_listener == nullptr) {
LOG_ERROR(Service_Nvnflinger, "no consumer is connected");
return Status::BadValue;
}
core->is_abandoned = true;
core->consumer_listener = nullptr;
core->queue.clear();
core->FreeAllBuffersLocked();
core->SignalDequeueCondition();
return Status::NoError;
}
Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
if (out_slot_mask == nullptr) { if (out_slot_mask == nullptr) {
LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr"); LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr");

View file

@ -13,10 +13,6 @@
#include "core/hle/service/nvnflinger/buffer_queue_defs.h" #include "core/hle/service/nvnflinger/buffer_queue_defs.h"
#include "core/hle/service/nvnflinger/status.h" #include "core/hle/service/nvnflinger/status.h"
namespace Service::Nvidia::NvCore {
class NvMap;
} // namespace Service::Nvidia::NvCore
namespace Service::android { namespace Service::android {
class BufferItem; class BufferItem;
@ -25,19 +21,18 @@ class IConsumerListener;
class BufferQueueConsumer final { class BufferQueueConsumer final {
public: public:
explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
Service::Nvidia::NvCore::NvMap& nvmap_);
~BufferQueueConsumer(); ~BufferQueueConsumer();
Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
Status Disconnect();
Status GetReleasedBuffers(u64* out_slot_mask); Status GetReleasedBuffers(u64* out_slot_mask);
private: private:
std::shared_ptr<BufferQueueCore> core; std::shared_ptr<BufferQueueCore> core;
BufferQueueDefs::SlotsType& slots; BufferQueueDefs::SlotsType& slots;
Service::Nvidia::NvCore::NvMap& nvmap;
}; };
} // namespace Service::android } // namespace Service::android

View file

@ -14,24 +14,12 @@ BufferQueueCore::BufferQueueCore() = default;
BufferQueueCore::~BufferQueueCore() = default; BufferQueueCore::~BufferQueueCore() = default;
void BufferQueueCore::NotifyShutdown() {
std::scoped_lock lock{mutex};
is_shutting_down = true;
SignalDequeueCondition();
}
void BufferQueueCore::SignalDequeueCondition() { void BufferQueueCore::SignalDequeueCondition() {
dequeue_possible.store(true); dequeue_possible.store(true);
dequeue_condition.notify_all(); dequeue_condition.notify_all();
} }
bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) { bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
if (is_shutting_down) {
return false;
}
dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
dequeue_possible.store(false); dequeue_possible.store(false);

View file

@ -34,8 +34,6 @@ public:
BufferQueueCore(); BufferQueueCore();
~BufferQueueCore(); ~BufferQueueCore();
void NotifyShutdown();
private: private:
void SignalDequeueCondition(); void SignalDequeueCondition();
bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk); bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
@ -74,7 +72,6 @@ private:
u32 transform_hint{}; u32 transform_hint{};
bool is_allocating{}; bool is_allocating{};
mutable std::condition_variable_any is_allocating_condition; mutable std::condition_variable_any is_allocating_condition;
bool is_shutting_down{};
}; };
} // namespace Service::android } // namespace Service::android

View file

@ -13,7 +13,6 @@
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/service/hle_ipc.h" #include "core/hle/service/hle_ipc.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h" #include "core/hle/service/nvnflinger/buffer_queue_core.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h" #include "core/hle/service/nvnflinger/buffer_queue_producer.h"
#include "core/hle/service/nvnflinger/consumer_listener.h" #include "core/hle/service/nvnflinger/consumer_listener.h"
@ -533,8 +532,6 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
item.is_droppable = core->dequeue_buffer_cannot_block || async; item.is_droppable = core->dequeue_buffer_cannot_block || async;
item.swap_interval = swap_interval; item.swap_interval = swap_interval;
nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true);
sticky_transform = sticky_transform_; sticky_transform = sticky_transform_;
if (core->queue.empty()) { if (core->queue.empty()) {
@ -744,19 +741,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
return Status::NoError; return Status::NoError;
} }
// HACK: We are not Android. Remove handle for items in queue, and clear queue.
// Allows synchronous destruction of nvmap handles.
for (auto& item : core->queue) {
nvmap.FreeHandle(item.graphic_buffer->BufferId(), true);
}
core->queue.clear();
switch (api) { switch (api) {
case NativeWindowApi::Egl: case NativeWindowApi::Egl:
case NativeWindowApi::Cpu: case NativeWindowApi::Cpu:
case NativeWindowApi::Media: case NativeWindowApi::Media:
case NativeWindowApi::Camera: case NativeWindowApi::Camera:
if (core->connected_api == api) { if (core->connected_api == api) {
core->queue.clear();
core->FreeAllBuffersLocked(); core->FreeAllBuffersLocked();
core->connected_producer_listener = nullptr; core->connected_producer_listener = nullptr;
core->connected_api = NativeWindowApi::NoConnectedApi; core->connected_api = NativeWindowApi::NoConnectedApi;
@ -785,7 +776,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
} }
Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
const std::shared_ptr<GraphicBuffer>& buffer) { const std::shared_ptr<NvGraphicBuffer>& buffer) {
LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); LOG_DEBUG(Service_Nvnflinger, "slot {}", slot);
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
@ -796,7 +787,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
slots[slot] = {}; slots[slot] = {};
slots[slot].fence = Fence::NoFence(); slots[slot].fence = Fence::NoFence();
slots[slot].graphic_buffer = buffer; slots[slot].graphic_buffer = std::make_shared<GraphicBuffer>(nvmap, buffer);
slots[slot].frame_number = 0; slots[slot].frame_number = 0;
// Most games preallocate a buffer and pass a valid buffer here. However, it is possible for // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
@ -839,7 +830,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
} }
case TransactionId::SetPreallocatedBuffer: { case TransactionId::SetPreallocatedBuffer: {
const auto slot = parcel_in.Read<s32>(); const auto slot = parcel_in.Read<s32>();
const auto buffer = parcel_in.ReadObject<GraphicBuffer>(); const auto buffer = parcel_in.ReadObject<NvGraphicBuffer>();
status = SetPreallocatedBuffer(slot, buffer); status = SetPreallocatedBuffer(slot, buffer);
break; break;
@ -867,7 +858,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
status = RequestBuffer(slot, &buf); status = RequestBuffer(slot, &buf);
parcel_out.WriteFlattenedObject(buf); parcel_out.WriteFlattenedObject<NvGraphicBuffer>(buf.get());
break; break;
} }
case TransactionId::QueueBuffer: { case TransactionId::QueueBuffer: {

View file

@ -38,6 +38,7 @@ namespace Service::android {
class BufferQueueCore; class BufferQueueCore;
class IProducerListener; class IProducerListener;
struct NvGraphicBuffer;
class BufferQueueProducer final : public IBinder { class BufferQueueProducer final : public IBinder {
public: public:
@ -65,7 +66,7 @@ public:
bool producer_controlled_by_app, QueueBufferOutput* output); bool producer_controlled_by_app, QueueBufferOutput* output);
Status Disconnect(NativeWindowApi api); Status Disconnect(NativeWindowApi api);
Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer); Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<NvGraphicBuffer>& buffer);
private: private:
BufferQueueProducer(const BufferQueueProducer&) = delete; BufferQueueProducer(const BufferQueueProducer&) = delete;

View file

@ -13,7 +13,7 @@
namespace Service::android { namespace Service::android {
struct GraphicBuffer; class GraphicBuffer;
enum class BufferState : u32 { enum class BufferState : u32 {
Free = 0, Free = 0,

View file

@ -27,6 +27,26 @@ void ConsumerBase::Connect(bool controlled_by_app) {
consumer->Connect(shared_from_this(), controlled_by_app); consumer->Connect(shared_from_this(), controlled_by_app);
} }
void ConsumerBase::Abandon() {
LOG_DEBUG(Service_Nvnflinger, "called");
std::scoped_lock lock{mutex};
if (!is_abandoned) {
this->AbandonLocked();
is_abandoned = true;
}
}
void ConsumerBase::AbandonLocked() {
for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
this->FreeBufferLocked(i);
}
// disconnect from the BufferQueue
consumer->Disconnect();
consumer = nullptr;
}
void ConsumerBase::FreeBufferLocked(s32 slot_index) { void ConsumerBase::FreeBufferLocked(s32 slot_index) {
LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index); LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index);

View file

@ -24,6 +24,7 @@ class BufferQueueConsumer;
class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> { class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
public: public:
void Connect(bool controlled_by_app); void Connect(bool controlled_by_app);
void Abandon();
protected: protected:
explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
@ -34,6 +35,7 @@ protected:
void OnBuffersReleased() override; void OnBuffersReleased() override;
void OnSidebandStreamChanged() override; void OnSidebandStreamChanged() override;
void AbandonLocked();
void FreeBufferLocked(s32 slot_index); void FreeBufferLocked(s32 slot_index);
Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer); Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer);

View file

@ -166,7 +166,7 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
}(); }();
void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
auto buffer = std::make_shared<android::GraphicBuffer>(); auto buffer = std::make_shared<android::NvGraphicBuffer>();
buffer->width = SharedBufferWidth; buffer->width = SharedBufferWidth;
buffer->height = SharedBufferHeight; buffer->height = SharedBufferHeight;
buffer->stride = SharedBufferBlockLinearStride; buffer->stride = SharedBufferBlockLinearStride;

View file

@ -47,8 +47,11 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) {
vsync_signal.Wait(); vsync_signal.Wait();
const auto lock_guard = Lock(); const auto lock_guard = Lock();
if (!is_abandoned) {
Compose(); Compose();
} }
}
} }
Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_) Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
@ -98,7 +101,6 @@ Nvnflinger::~Nvnflinger() {
} }
ShutdownLayers(); ShutdownLayers();
vsync_thread = {};
if (nvdrv) { if (nvdrv) {
nvdrv->Close(disp_fd); nvdrv->Close(disp_fd);
@ -106,12 +108,20 @@ Nvnflinger::~Nvnflinger() {
} }
void Nvnflinger::ShutdownLayers() { void Nvnflinger::ShutdownLayers() {
// Abandon consumers.
{
const auto lock_guard = Lock(); const auto lock_guard = Lock();
for (auto& display : displays) { for (auto& display : displays) {
for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
display.GetLayer(layer).Core().NotifyShutdown(); display.GetLayer(layer).GetConsumer().Abandon();
} }
} }
is_abandoned = true;
}
// Join the vsync thread, if it exists.
vsync_thread = {};
} }
void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {

View file

@ -140,6 +140,8 @@ private:
s32 swap_interval = 1; s32 swap_interval = 1;
bool is_abandoned = false;
/// Event that handles screen composition. /// Event that handles screen composition.
std::shared_ptr<Core::Timing::EventType> multi_composition_event; std::shared_ptr<Core::Timing::EventType> multi_composition_event;
std::shared_ptr<Core::Timing::EventType> single_composition_event; std::shared_ptr<Core::Timing::EventType> single_composition_event;

View file

@ -19,7 +19,7 @@ enum class Status : s32 {
Busy = -16, Busy = -16,
NoInit = -19, NoInit = -19,
BadValue = -22, BadValue = -22,
InvalidOperation = -37, InvalidOperation = -38,
BufferNeedsReallocation = 1, BufferNeedsReallocation = 1,
ReleaseAllBuffers = 2, ReleaseAllBuffers = 2,
}; };

View file

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
namespace Service::android {
static NvGraphicBuffer GetBuffer(std::shared_ptr<NvGraphicBuffer>& buffer) {
if (buffer) {
return *buffer;
} else {
return {};
}
}
GraphicBuffer::GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
: NvGraphicBuffer(width_, height_, format_, usage_), m_nvmap(nullptr) {}
GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
std::shared_ptr<NvGraphicBuffer> buffer)
: NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) {
if (this->BufferId() > 0) {
m_nvmap->DuplicateHandle(this->BufferId(), true);
}
}
GraphicBuffer::~GraphicBuffer() {
if (m_nvmap != nullptr && this->BufferId() > 0) {
m_nvmap->FreeHandle(this->BufferId(), true);
}
}
} // namespace Service::android

View file

@ -6,16 +6,22 @@
#pragma once #pragma once
#include <memory>
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/service/nvnflinger/pixel_format.h" #include "core/hle/service/nvnflinger/pixel_format.h"
namespace Service::Nvidia::NvCore {
class NvMap;
} // namespace Service::Nvidia::NvCore
namespace Service::android { namespace Service::android {
struct GraphicBuffer final { struct NvGraphicBuffer {
constexpr GraphicBuffer() = default; constexpr NvGraphicBuffer() = default;
constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) constexpr NvGraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
: width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_}, : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
usage{static_cast<s32>(usage_)} {} usage{static_cast<s32>(usage_)} {}
@ -93,6 +99,17 @@ struct GraphicBuffer final {
u32 offset{}; u32 offset{};
INSERT_PADDING_WORDS(60); INSERT_PADDING_WORDS(60);
}; };
static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); static_assert(sizeof(NvGraphicBuffer) == 0x16C, "NvGraphicBuffer has wrong size");
class GraphicBuffer final : public NvGraphicBuffer {
public:
explicit GraphicBuffer(u32 width, u32 height, PixelFormat format, u32 usage);
explicit GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
std::shared_ptr<NvGraphicBuffer> buffer);
~GraphicBuffer();
private:
Service::Nvidia::NvCore::NvMap* m_nvmap{};
};
} // namespace Service::android } // namespace Service::android

View file

@ -35,7 +35,7 @@ static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_cont
return { return {
buffer_queue_core, buffer_queue_core,
std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)}; std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
} }
Display::Display(u64 id, std::string name_, Display::Display(u64 id, std::string name_,

View file

@ -41,7 +41,7 @@ struct Memory::Impl {
explicit Impl(Core::System& system_) : system{system_} {} explicit Impl(Core::System& system_) : system{system_} {}
void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) { void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) {
current_page_table = &process.GetPageTable().PageTableImpl(); current_page_table = &process.GetPageTable().GetImpl();
current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer(); current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
const std::size_t address_space_width = process.GetPageTable().GetAddressSpaceWidth(); const std::size_t address_space_width = process.GetPageTable().GetAddressSpaceWidth();
@ -195,7 +195,7 @@ struct Memory::Impl {
bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped, bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped,
auto on_memory, auto on_rasterizer, auto increment) { auto on_memory, auto on_rasterizer, auto increment) {
const auto& page_table = system.ApplicationProcess()->GetPageTable().PageTableImpl(); const auto& page_table = system.ApplicationProcess()->GetPageTable().GetImpl();
std::size_t remaining_size = size; std::size_t remaining_size = size;
std::size_t page_index = addr >> YUZU_PAGEBITS; std::size_t page_index = addr >> YUZU_PAGEBITS;
std::size_t page_offset = addr & YUZU_PAGEMASK; std::size_t page_offset = addr & YUZU_PAGEMASK;
@ -826,7 +826,7 @@ void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress b
bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
const Kernel::KProcess& process = *system.ApplicationProcess(); const Kernel::KProcess& process = *system.ApplicationProcess();
const auto& page_table = process.GetPageTable().PageTableImpl(); const auto& page_table = process.GetPageTable().GetImpl();
const size_t page = vaddr >> YUZU_PAGEBITS; const size_t page = vaddr >> YUZU_PAGEBITS;
if (page >= page_table.pointers.size()) { if (page >= page_table.pointers.size()) {
return false; return false;

View file

@ -924,9 +924,13 @@ void RasterizerVulkan::UpdateDynamicStates() {
} }
void RasterizerVulkan::HandleTransformFeedback() { void RasterizerVulkan::HandleTransformFeedback() {
static std::once_flag warn_unsupported;
const auto& regs = maxwell3d->regs; const auto& regs = maxwell3d->regs;
if (!device.IsExtTransformFeedbackSupported()) { if (!device.IsExtTransformFeedbackSupported()) {
std::call_once(warn_unsupported, [&] {
LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported");
});
return; return;
} }
query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount, query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount,