mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 00:49:02 -06:00 
			
		
		
		
	@@ -182,6 +182,8 @@ add_library(core STATIC
 | 
			
		||||
    hle/kernel/k_auto_object_container.cpp
 | 
			
		||||
    hle/kernel/k_auto_object_container.h
 | 
			
		||||
    hle/kernel/k_affinity_mask.h
 | 
			
		||||
    hle/kernel/k_capabilities.cpp
 | 
			
		||||
    hle/kernel/k_capabilities.h
 | 
			
		||||
    hle/kernel/k_class_token.cpp
 | 
			
		||||
    hle/kernel/k_class_token.h
 | 
			
		||||
    hle/kernel/k_client_port.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,26 @@ constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
 | 
			
		||||
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
 | 
			
		||||
 | 
			
		||||
static constexpr inline u64 VirtualCoreMask = [] {
 | 
			
		||||
    u64 mask = 0;
 | 
			
		||||
    for (size_t i = 0; i < NumVirtualCores; ++i) {
 | 
			
		||||
        mask |= (UINT64_C(1) << i);
 | 
			
		||||
    }
 | 
			
		||||
    return mask;
 | 
			
		||||
}();
 | 
			
		||||
 | 
			
		||||
static constexpr inline u64 ConvertVirtualCoreMaskToPhysical(u64 v_core_mask) {
 | 
			
		||||
    u64 p_core_mask = 0;
 | 
			
		||||
    while (v_core_mask != 0) {
 | 
			
		||||
        const u64 next = std::countr_zero(v_core_mask);
 | 
			
		||||
        v_core_mask &= ~(static_cast<u64>(1) << next);
 | 
			
		||||
        p_core_mask |= (static_cast<u64>(1) << VirtualToPhysicalCoreMap[next]);
 | 
			
		||||
    }
 | 
			
		||||
    return p_core_mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cortex-A57 supports 4 memory watchpoints
 | 
			
		||||
constexpr u64 NUM_WATCHPOINTS = 4;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										358
									
								
								src/core/hle/kernel/k_capabilities.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								src/core/hle/kernel/k_capabilities.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,358 @@
 | 
			
		||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
#include "core/hardware_properties.h"
 | 
			
		||||
#include "core/hle/kernel/k_capabilities.h"
 | 
			
		||||
#include "core/hle/kernel/k_memory_layout.h"
 | 
			
		||||
#include "core/hle/kernel/k_page_table.h"
 | 
			
		||||
#include "core/hle/kernel/kernel.h"
 | 
			
		||||
#include "core/hle/kernel/svc_results.h"
 | 
			
		||||
#include "core/hle/kernel/svc_version.h"
 | 
			
		||||
 | 
			
		||||
namespace Kernel {
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::InitializeForKIP(std::span<const u32> kern_caps, KPageTable* page_table) {
 | 
			
		||||
    // We're initializing an initial process.
 | 
			
		||||
    m_svc_access_flags.reset();
 | 
			
		||||
    m_irq_access_flags.reset();
 | 
			
		||||
    m_debug_capabilities = 0;
 | 
			
		||||
    m_handle_table_size = 0;
 | 
			
		||||
    m_intended_kernel_version = 0;
 | 
			
		||||
    m_program_type = 0;
 | 
			
		||||
 | 
			
		||||
    // Initial processes may run on all cores.
 | 
			
		||||
    constexpr u64 VirtMask = Core::Hardware::VirtualCoreMask;
 | 
			
		||||
    constexpr u64 PhysMask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(VirtMask);
 | 
			
		||||
 | 
			
		||||
    m_core_mask = VirtMask;
 | 
			
		||||
    m_phys_core_mask = PhysMask;
 | 
			
		||||
 | 
			
		||||
    // Initial processes may use any user priority they like.
 | 
			
		||||
    m_priority_mask = ~0xFULL;
 | 
			
		||||
 | 
			
		||||
    // Here, Nintendo sets the kernel version to the current kernel version.
 | 
			
		||||
    // We will follow suit and set the version to the highest supported kernel version.
 | 
			
		||||
    KernelVersion intended_kernel_version{};
 | 
			
		||||
    intended_kernel_version.major_version.Assign(Svc::SupportedKernelMajorVersion);
 | 
			
		||||
    intended_kernel_version.minor_version.Assign(Svc::SupportedKernelMinorVersion);
 | 
			
		||||
    m_intended_kernel_version = intended_kernel_version.raw;
 | 
			
		||||
 | 
			
		||||
    // Parse the capabilities array.
 | 
			
		||||
    R_RETURN(this->SetCapabilities(kern_caps, page_table));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table) {
 | 
			
		||||
    // We're initializing a user process.
 | 
			
		||||
    m_svc_access_flags.reset();
 | 
			
		||||
    m_irq_access_flags.reset();
 | 
			
		||||
    m_debug_capabilities = 0;
 | 
			
		||||
    m_handle_table_size = 0;
 | 
			
		||||
    m_intended_kernel_version = 0;
 | 
			
		||||
    m_program_type = 0;
 | 
			
		||||
 | 
			
		||||
    // User processes must specify what cores/priorities they can use.
 | 
			
		||||
    m_core_mask = 0;
 | 
			
		||||
    m_priority_mask = 0;
 | 
			
		||||
 | 
			
		||||
    // Parse the user capabilities array.
 | 
			
		||||
    R_RETURN(this->SetCapabilities(user_caps, page_table));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::SetCorePriorityCapability(const u32 cap) {
 | 
			
		||||
    // We can't set core/priority if we've already set them.
 | 
			
		||||
    R_UNLESS(m_core_mask == 0, ResultInvalidArgument);
 | 
			
		||||
    R_UNLESS(m_priority_mask == 0, ResultInvalidArgument);
 | 
			
		||||
 | 
			
		||||
    // Validate the core/priority.
 | 
			
		||||
    CorePriority pack{cap};
 | 
			
		||||
    const u32 min_core = pack.minimum_core_id;
 | 
			
		||||
    const u32 max_core = pack.maximum_core_id;
 | 
			
		||||
    const u32 max_prio = pack.lowest_thread_priority;
 | 
			
		||||
    const u32 min_prio = pack.highest_thread_priority;
 | 
			
		||||
 | 
			
		||||
    R_UNLESS(min_core <= max_core, ResultInvalidCombination);
 | 
			
		||||
    R_UNLESS(min_prio <= max_prio, ResultInvalidCombination);
 | 
			
		||||
    R_UNLESS(max_core < Core::Hardware::NumVirtualCores, ResultInvalidCoreId);
 | 
			
		||||
 | 
			
		||||
    ASSERT(max_prio < Common::BitSize<u64>());
 | 
			
		||||
 | 
			
		||||
    // Set core mask.
 | 
			
		||||
    for (auto core_id = min_core; core_id <= max_core; core_id++) {
 | 
			
		||||
        m_core_mask |= (1ULL << core_id);
 | 
			
		||||
    }
 | 
			
		||||
    ASSERT((m_core_mask & Core::Hardware::VirtualCoreMask) == m_core_mask);
 | 
			
		||||
 | 
			
		||||
    // Set physical core mask.
 | 
			
		||||
    m_phys_core_mask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(m_core_mask);
 | 
			
		||||
 | 
			
		||||
    // Set priority mask.
 | 
			
		||||
    for (auto prio = min_prio; prio <= max_prio; prio++) {
 | 
			
		||||
        m_priority_mask |= (1ULL << prio);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // We must have some core/priority we can use.
 | 
			
		||||
    R_UNLESS(m_core_mask != 0, ResultInvalidArgument);
 | 
			
		||||
    R_UNLESS(m_priority_mask != 0, ResultInvalidArgument);
 | 
			
		||||
 | 
			
		||||
    // Processes must not have access to kernel thread priorities.
 | 
			
		||||
    R_UNLESS((m_priority_mask & 0xF) == 0, ResultInvalidArgument);
 | 
			
		||||
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
 | 
			
		||||
    // Validate the index.
 | 
			
		||||
    SyscallMask pack{cap};
 | 
			
		||||
    const u32 mask = pack.mask;
 | 
			
		||||
    const u32 index = pack.index;
 | 
			
		||||
 | 
			
		||||
    const u32 index_flag = (1U << index);
 | 
			
		||||
    R_UNLESS((set_svc & index_flag) == 0, ResultInvalidCombination);
 | 
			
		||||
    set_svc |= index_flag;
 | 
			
		||||
 | 
			
		||||
    // Set SVCs.
 | 
			
		||||
    for (size_t i = 0; i < decltype(SyscallMask::mask)::bits; i++) {
 | 
			
		||||
        const u32 svc_id = static_cast<u32>(decltype(SyscallMask::mask)::bits * index + i);
 | 
			
		||||
        if (mask & (1U << i)) {
 | 
			
		||||
            R_UNLESS(this->SetSvcAllowed(svc_id), ResultOutOfRange);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table) {
 | 
			
		||||
    const auto range_pack = MapRange{cap};
 | 
			
		||||
    const auto size_pack = MapRangeSize{size_cap};
 | 
			
		||||
 | 
			
		||||
    // Get/validate address/size
 | 
			
		||||
    const u64 phys_addr = range_pack.address.Value() * PageSize;
 | 
			
		||||
 | 
			
		||||
    // Validate reserved bits are unused.
 | 
			
		||||
    R_UNLESS(size_pack.reserved.Value() == 0, ResultOutOfRange);
 | 
			
		||||
 | 
			
		||||
    const size_t num_pages = size_pack.pages;
 | 
			
		||||
    const size_t size = num_pages * PageSize;
 | 
			
		||||
    R_UNLESS(num_pages != 0, ResultInvalidSize);
 | 
			
		||||
    R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
 | 
			
		||||
    R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
 | 
			
		||||
 | 
			
		||||
    // Do the mapping.
 | 
			
		||||
    [[maybe_unused]] const KMemoryPermission perm = range_pack.read_only.Value()
 | 
			
		||||
                                                        ? KMemoryPermission::UserRead
 | 
			
		||||
                                                        : KMemoryPermission::UserReadWrite;
 | 
			
		||||
    if (MapRangeSize{size_cap}.normal) {
 | 
			
		||||
        // R_RETURN(page_table->MapStatic(phys_addr, size, perm));
 | 
			
		||||
    } else {
 | 
			
		||||
        // R_RETURN(page_table->MapIo(phys_addr, size, perm));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    UNIMPLEMENTED();
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
 | 
			
		||||
    // Get/validate address/size
 | 
			
		||||
    const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
 | 
			
		||||
    const size_t num_pages = 1;
 | 
			
		||||
    const size_t size = num_pages * PageSize;
 | 
			
		||||
    R_UNLESS(num_pages != 0, ResultInvalidSize);
 | 
			
		||||
    R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
 | 
			
		||||
    R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
 | 
			
		||||
 | 
			
		||||
    // Do the mapping.
 | 
			
		||||
    // R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite));
 | 
			
		||||
 | 
			
		||||
    UNIMPLEMENTED();
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename F>
 | 
			
		||||
Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
 | 
			
		||||
    // Define the allowed memory regions.
 | 
			
		||||
    constexpr std::array<KMemoryRegionType, 4> MemoryRegions{
 | 
			
		||||
        KMemoryRegionType_None,
 | 
			
		||||
        KMemoryRegionType_KernelTraceBuffer,
 | 
			
		||||
        KMemoryRegionType_OnMemoryBootImage,
 | 
			
		||||
        KMemoryRegionType_DTB,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Extract regions/read only.
 | 
			
		||||
    const MapRegion pack{cap};
 | 
			
		||||
    const std::array<RegionType, 3> types{pack.region0, pack.region1, pack.region2};
 | 
			
		||||
    const std::array<u32, 3> ro{pack.read_only0, pack.read_only1, pack.read_only2};
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < types.size(); i++) {
 | 
			
		||||
        const auto type = types[i];
 | 
			
		||||
        const auto perm = ro[i] ? KMemoryPermission::UserRead : KMemoryPermission::UserReadWrite;
 | 
			
		||||
        switch (type) {
 | 
			
		||||
        case RegionType::NoMapping:
 | 
			
		||||
            break;
 | 
			
		||||
        case RegionType::KernelTraceBuffer:
 | 
			
		||||
        case RegionType::OnMemoryBootImage:
 | 
			
		||||
        case RegionType::DTB:
 | 
			
		||||
            R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm));
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            R_THROW(ResultNotFound);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) {
 | 
			
		||||
    // Map each region into the process's page table.
 | 
			
		||||
    R_RETURN(ProcessMapRegionCapability(
 | 
			
		||||
        cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
 | 
			
		||||
            // R_RETURN(page_table->MapRegion(region_type, perm));
 | 
			
		||||
            UNIMPLEMENTED();
 | 
			
		||||
            R_SUCCEED();
 | 
			
		||||
        }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::CheckMapRegion(KernelCore& kernel, const u32 cap) {
 | 
			
		||||
    // Check that each region has a physical backing store.
 | 
			
		||||
    R_RETURN(ProcessMapRegionCapability(
 | 
			
		||||
        cap, [&](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
 | 
			
		||||
            R_UNLESS(kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(
 | 
			
		||||
                         region_type) != nullptr,
 | 
			
		||||
                     ResultOutOfRange);
 | 
			
		||||
            R_SUCCEED();
 | 
			
		||||
        }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::SetInterruptPairCapability(const u32 cap) {
 | 
			
		||||
    // Extract interrupts.
 | 
			
		||||
    const InterruptPair pack{cap};
 | 
			
		||||
    const std::array<u32, 2> ids{pack.interrupt_id0, pack.interrupt_id1};
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < ids.size(); i++) {
 | 
			
		||||
        if (ids[i] != PaddingInterruptId) {
 | 
			
		||||
            UNIMPLEMENTED();
 | 
			
		||||
            // R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(ids[i]), ResultOutOfRange);
 | 
			
		||||
            // R_UNLESS(this->SetInterruptPermitted(ids[i]), ResultOutOfRange);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::SetProgramTypeCapability(const u32 cap) {
 | 
			
		||||
    // Validate.
 | 
			
		||||
    const ProgramType pack{cap};
 | 
			
		||||
    R_UNLESS(pack.reserved == 0, ResultReservedUsed);
 | 
			
		||||
 | 
			
		||||
    m_program_type = pack.type;
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::SetKernelVersionCapability(const u32 cap) {
 | 
			
		||||
    // Ensure we haven't set our version before.
 | 
			
		||||
    R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version == 0, ResultInvalidArgument);
 | 
			
		||||
 | 
			
		||||
    // Set, ensure that we set a valid version.
 | 
			
		||||
    m_intended_kernel_version = cap;
 | 
			
		||||
    R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version != 0, ResultInvalidArgument);
 | 
			
		||||
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::SetHandleTableCapability(const u32 cap) {
 | 
			
		||||
    // Validate.
 | 
			
		||||
    const HandleTable pack{cap};
 | 
			
		||||
    R_UNLESS(pack.reserved == 0, ResultReservedUsed);
 | 
			
		||||
 | 
			
		||||
    m_handle_table_size = pack.size;
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
 | 
			
		||||
    // Validate.
 | 
			
		||||
    const DebugFlags pack{cap};
 | 
			
		||||
    R_UNLESS(pack.reserved == 0, ResultReservedUsed);
 | 
			
		||||
 | 
			
		||||
    DebugFlags debug_capabilities{m_debug_capabilities};
 | 
			
		||||
    debug_capabilities.allow_debug.Assign(pack.allow_debug);
 | 
			
		||||
    debug_capabilities.force_debug.Assign(pack.force_debug);
 | 
			
		||||
    m_debug_capabilities = debug_capabilities.raw;
 | 
			
		||||
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
 | 
			
		||||
                                    KPageTable* page_table) {
 | 
			
		||||
    // Validate this is a capability we can act on.
 | 
			
		||||
    const auto type = GetCapabilityType(cap);
 | 
			
		||||
    R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);
 | 
			
		||||
 | 
			
		||||
    // If the type is padding, we have no work to do.
 | 
			
		||||
    R_SUCCEED_IF(type == CapabilityType::Padding);
 | 
			
		||||
 | 
			
		||||
    // Check that we haven't already processed this capability.
 | 
			
		||||
    const auto flag = GetCapabilityFlag(type);
 | 
			
		||||
    R_UNLESS(((set_flags & InitializeOnceFlags) & flag) == 0, ResultInvalidCombination);
 | 
			
		||||
    set_flags |= flag;
 | 
			
		||||
 | 
			
		||||
    // Process the capability.
 | 
			
		||||
    switch (type) {
 | 
			
		||||
    case CapabilityType::CorePriority:
 | 
			
		||||
        R_RETURN(this->SetCorePriorityCapability(cap));
 | 
			
		||||
    case CapabilityType::SyscallMask:
 | 
			
		||||
        R_RETURN(this->SetSyscallMaskCapability(cap, set_svc));
 | 
			
		||||
    case CapabilityType::MapIoPage:
 | 
			
		||||
        R_RETURN(this->MapIoPage_(cap, page_table));
 | 
			
		||||
    case CapabilityType::MapRegion:
 | 
			
		||||
        R_RETURN(this->MapRegion_(cap, page_table));
 | 
			
		||||
    case CapabilityType::InterruptPair:
 | 
			
		||||
        R_RETURN(this->SetInterruptPairCapability(cap));
 | 
			
		||||
    case CapabilityType::ProgramType:
 | 
			
		||||
        R_RETURN(this->SetProgramTypeCapability(cap));
 | 
			
		||||
    case CapabilityType::KernelVersion:
 | 
			
		||||
        R_RETURN(this->SetKernelVersionCapability(cap));
 | 
			
		||||
    case CapabilityType::HandleTable:
 | 
			
		||||
        R_RETURN(this->SetHandleTableCapability(cap));
 | 
			
		||||
    case CapabilityType::DebugFlags:
 | 
			
		||||
        R_RETURN(this->SetDebugFlagsCapability(cap));
 | 
			
		||||
    default:
 | 
			
		||||
        R_THROW(ResultInvalidArgument);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* page_table) {
 | 
			
		||||
    u32 set_flags = 0, set_svc = 0;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < caps.size(); i++) {
 | 
			
		||||
        const u32 cap{caps[i]};
 | 
			
		||||
 | 
			
		||||
        if (GetCapabilityType(cap) == CapabilityType::MapRange) {
 | 
			
		||||
            // Check that the pair cap exists.
 | 
			
		||||
            R_UNLESS((++i) < caps.size(), ResultInvalidCombination);
 | 
			
		||||
 | 
			
		||||
            // Check the pair cap is a map range cap.
 | 
			
		||||
            const u32 size_cap{caps[i]};
 | 
			
		||||
            R_UNLESS(GetCapabilityType(size_cap) == CapabilityType::MapRange,
 | 
			
		||||
                     ResultInvalidCombination);
 | 
			
		||||
 | 
			
		||||
            // Map the range.
 | 
			
		||||
            R_TRY(this->MapRange_(cap, size_cap, page_table));
 | 
			
		||||
        } else {
 | 
			
		||||
            R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KCapabilities::CheckCapabilities(KernelCore& kernel, std::span<const u32> caps) {
 | 
			
		||||
    for (auto cap : caps) {
 | 
			
		||||
        // Check the capability refers to a valid region.
 | 
			
		||||
        if (GetCapabilityType(cap) == CapabilityType::MapRegion) {
 | 
			
		||||
            R_TRY(CheckMapRegion(kernel, cap));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    R_SUCCEED();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Kernel
 | 
			
		||||
							
								
								
									
										295
									
								
								src/core/hle/kernel/k_capabilities.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										295
									
								
								src/core/hle/kernel/k_capabilities.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,295 @@
 | 
			
		||||
 | 
			
		||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <bitset>
 | 
			
		||||
#include <span>
 | 
			
		||||
 | 
			
		||||
#include "common/bit_field.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
#include "core/hle/kernel/svc_types.h"
 | 
			
		||||
#include "core/hle/result.h"
 | 
			
		||||
 | 
			
		||||
namespace Kernel {
 | 
			
		||||
 | 
			
		||||
class KPageTable;
 | 
			
		||||
class KernelCore;
 | 
			
		||||
 | 
			
		||||
class KCapabilities {
 | 
			
		||||
public:
 | 
			
		||||
    constexpr explicit KCapabilities() = default;
 | 
			
		||||
 | 
			
		||||
    Result InitializeForKIP(std::span<const u32> kern_caps, KPageTable* page_table);
 | 
			
		||||
    Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table);
 | 
			
		||||
 | 
			
		||||
    static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
 | 
			
		||||
 | 
			
		||||
    constexpr u64 GetCoreMask() const {
 | 
			
		||||
        return m_core_mask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr u64 GetPhysicalCoreMask() const {
 | 
			
		||||
        return m_phys_core_mask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr u64 GetPriorityMask() const {
 | 
			
		||||
        return m_priority_mask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr s32 GetHandleTableSize() const {
 | 
			
		||||
        return m_handle_table_size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr const Svc::SvcAccessFlagSet& GetSvcPermissions() const {
 | 
			
		||||
        return m_svc_access_flags;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr bool IsPermittedSvc(u32 id) const {
 | 
			
		||||
        return (id < m_svc_access_flags.size()) && m_svc_access_flags[id];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr bool IsPermittedInterrupt(u32 id) const {
 | 
			
		||||
        return (id < m_irq_access_flags.size()) && m_irq_access_flags[id];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr bool IsPermittedDebug() const {
 | 
			
		||||
        return DebugFlags{m_debug_capabilities}.allow_debug.Value() != 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr bool CanForceDebug() const {
 | 
			
		||||
        return DebugFlags{m_debug_capabilities}.force_debug.Value() != 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr u32 GetIntendedKernelMajorVersion() const {
 | 
			
		||||
        return KernelVersion{m_intended_kernel_version}.major_version;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr u32 GetIntendedKernelMinorVersion() const {
 | 
			
		||||
        return KernelVersion{m_intended_kernel_version}.minor_version;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static constexpr size_t InterruptIdCount = 0x400;
 | 
			
		||||
    using InterruptFlagSet = std::bitset<InterruptIdCount>;
 | 
			
		||||
 | 
			
		||||
    enum class CapabilityType : u32 {
 | 
			
		||||
        CorePriority = (1U << 3) - 1,
 | 
			
		||||
        SyscallMask = (1U << 4) - 1,
 | 
			
		||||
        MapRange = (1U << 6) - 1,
 | 
			
		||||
        MapIoPage = (1U << 7) - 1,
 | 
			
		||||
        MapRegion = (1U << 10) - 1,
 | 
			
		||||
        InterruptPair = (1U << 11) - 1,
 | 
			
		||||
        ProgramType = (1U << 13) - 1,
 | 
			
		||||
        KernelVersion = (1U << 14) - 1,
 | 
			
		||||
        HandleTable = (1U << 15) - 1,
 | 
			
		||||
        DebugFlags = (1U << 16) - 1,
 | 
			
		||||
 | 
			
		||||
        Invalid = 0U,
 | 
			
		||||
        Padding = ~0U,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    using RawCapabilityValue = u32;
 | 
			
		||||
 | 
			
		||||
    static constexpr CapabilityType GetCapabilityType(const RawCapabilityValue value) {
 | 
			
		||||
        return static_cast<CapabilityType>((~value & (value + 1)) - 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static constexpr u32 GetCapabilityFlag(CapabilityType type) {
 | 
			
		||||
        return static_cast<u32>(type) + 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <CapabilityType Type>
 | 
			
		||||
    static constexpr inline u32 CapabilityFlag = static_cast<u32>(Type) + 1;
 | 
			
		||||
 | 
			
		||||
    template <CapabilityType Type>
 | 
			
		||||
    static constexpr inline u32 CapabilityId = std::countr_zero(CapabilityFlag<Type>);
 | 
			
		||||
 | 
			
		||||
    union CorePriority {
 | 
			
		||||
        static_assert(CapabilityId<CapabilityType::CorePriority> + 1 == 4);
 | 
			
		||||
 | 
			
		||||
        RawCapabilityValue raw;
 | 
			
		||||
        BitField<0, 4, CapabilityType> id;
 | 
			
		||||
        BitField<4, 6, u32> lowest_thread_priority;
 | 
			
		||||
        BitField<10, 6, u32> highest_thread_priority;
 | 
			
		||||
        BitField<16, 8, u32> minimum_core_id;
 | 
			
		||||
        BitField<24, 8, u32> maximum_core_id;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    union SyscallMask {
 | 
			
		||||
        static_assert(CapabilityId<CapabilityType::SyscallMask> + 1 == 5);
 | 
			
		||||
 | 
			
		||||
        RawCapabilityValue raw;
 | 
			
		||||
        BitField<0, 5, CapabilityType> id;
 | 
			
		||||
        BitField<5, 24, u32> mask;
 | 
			
		||||
        BitField<29, 3, u32> index;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // #undef MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES
 | 
			
		||||
    static constexpr u64 PhysicalMapAllowedMask = (1ULL << 36) - 1;
 | 
			
		||||
 | 
			
		||||
    union MapRange {
 | 
			
		||||
        static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);
 | 
			
		||||
 | 
			
		||||
        RawCapabilityValue raw;
 | 
			
		||||
        BitField<0, 7, CapabilityType> id;
 | 
			
		||||
        BitField<7, 24, u32> address;
 | 
			
		||||
        BitField<31, 1, u32> read_only;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    union MapRangeSize {
 | 
			
		||||
        static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);
 | 
			
		||||
 | 
			
		||||
        RawCapabilityValue raw;
 | 
			
		||||
        BitField<0, 7, CapabilityType> id;
 | 
			
		||||
        BitField<7, 20, u32> pages;
 | 
			
		||||
        BitField<27, 4, u32> reserved;
 | 
			
		||||
        BitField<31, 1, u32> normal;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    union MapIoPage {
 | 
			
		||||
        static_assert(CapabilityId<CapabilityType::MapIoPage> + 1 == 8);
 | 
			
		||||
 | 
			
		||||
        RawCapabilityValue raw;
 | 
			
		||||
        BitField<0, 8, CapabilityType> id;
 | 
			
		||||
        BitField<8, 24, u32> address;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    enum class RegionType : u32 {
 | 
			
		||||
        NoMapping = 0,
 | 
			
		||||
        KernelTraceBuffer = 1,
 | 
			
		||||
        OnMemoryBootImage = 2,
 | 
			
		||||
        DTB = 3,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    union MapRegion {
 | 
			
		||||
        static_assert(CapabilityId<CapabilityType::MapRegion> + 1 == 11);
 | 
			
		||||
 | 
			
		||||
        RawCapabilityValue raw;
 | 
			
		||||
        BitField<0, 11, CapabilityType> id;
 | 
			
		||||
        BitField<11, 6, RegionType> region0;
 | 
			
		||||
        BitField<17, 1, u32> read_only0;
 | 
			
		||||
        BitField<18, 6, RegionType> region1;
 | 
			
		||||
        BitField<24, 1, u32> read_only1;
 | 
			
		||||
        BitField<25, 6, RegionType> region2;
 | 
			
		||||
        BitField<31, 1, u32> read_only2;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    union InterruptPair {
 | 
			
		||||
        static_assert(CapabilityId<CapabilityType::InterruptPair> + 1 == 12);
 | 
			
		||||
 | 
			
		||||
        RawCapabilityValue raw;
 | 
			
		||||
        BitField<0, 12, CapabilityType> id;
 | 
			
		||||
        BitField<12, 10, u32> interrupt_id0;
 | 
			
		||||
        BitField<22, 10, u32> interrupt_id1;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    union ProgramType {
 | 
			
		||||
        static_assert(CapabilityId<CapabilityType::ProgramType> + 1 == 14);
 | 
			
		||||
 | 
			
		||||
        RawCapabilityValue raw;
 | 
			
		||||
        BitField<0, 14, CapabilityType> id;
 | 
			
		||||
        BitField<14, 3, u32> type;
 | 
			
		||||
        BitField<17, 15, u32> reserved;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    union KernelVersion {
 | 
			
		||||
        static_assert(CapabilityId<CapabilityType::KernelVersion> + 1 == 15);
 | 
			
		||||
 | 
			
		||||
        RawCapabilityValue raw;
 | 
			
		||||
        BitField<0, 15, CapabilityType> id;
 | 
			
		||||
        BitField<15, 4, u32> major_version;
 | 
			
		||||
        BitField<19, 13, u32> minor_version;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    union HandleTable {
 | 
			
		||||
        static_assert(CapabilityId<CapabilityType::HandleTable> + 1 == 16);
 | 
			
		||||
 | 
			
		||||
        RawCapabilityValue raw;
 | 
			
		||||
        BitField<0, 16, CapabilityType> id;
 | 
			
		||||
        BitField<16, 10, u32> size;
 | 
			
		||||
        BitField<26, 6, u32> reserved;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    union DebugFlags {
 | 
			
		||||
        static_assert(CapabilityId<CapabilityType::DebugFlags> + 1 == 17);
 | 
			
		||||
 | 
			
		||||
        RawCapabilityValue raw;
 | 
			
		||||
        BitField<0, 17, CapabilityType> id;
 | 
			
		||||
        BitField<17, 1, u32> allow_debug;
 | 
			
		||||
        BitField<18, 1, u32> force_debug;
 | 
			
		||||
        BitField<19, 13, u32> reserved;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    static_assert(sizeof(CorePriority) == 4);
 | 
			
		||||
    static_assert(sizeof(SyscallMask) == 4);
 | 
			
		||||
    static_assert(sizeof(MapRange) == 4);
 | 
			
		||||
    static_assert(sizeof(MapRangeSize) == 4);
 | 
			
		||||
    static_assert(sizeof(MapIoPage) == 4);
 | 
			
		||||
    static_assert(sizeof(MapRegion) == 4);
 | 
			
		||||
    static_assert(sizeof(InterruptPair) == 4);
 | 
			
		||||
    static_assert(sizeof(ProgramType) == 4);
 | 
			
		||||
    static_assert(sizeof(KernelVersion) == 4);
 | 
			
		||||
    static_assert(sizeof(HandleTable) == 4);
 | 
			
		||||
    static_assert(sizeof(DebugFlags) == 4);
 | 
			
		||||
 | 
			
		||||
    static constexpr u32 InitializeOnceFlags =
 | 
			
		||||
        CapabilityFlag<CapabilityType::CorePriority> | CapabilityFlag<CapabilityType::ProgramType> |
 | 
			
		||||
        CapabilityFlag<CapabilityType::KernelVersion> |
 | 
			
		||||
        CapabilityFlag<CapabilityType::HandleTable> | CapabilityFlag<CapabilityType::DebugFlags>;
 | 
			
		||||
 | 
			
		||||
    static const u32 PaddingInterruptId = 0x3FF;
 | 
			
		||||
    static_assert(PaddingInterruptId < InterruptIdCount);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    constexpr bool SetSvcAllowed(u32 id) {
 | 
			
		||||
        if (id < m_svc_access_flags.size()) [[likely]] {
 | 
			
		||||
            m_svc_access_flags[id] = true;
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr bool SetInterruptPermitted(u32 id) {
 | 
			
		||||
        if (id < m_irq_access_flags.size()) [[likely]] {
 | 
			
		||||
            m_irq_access_flags[id] = true;
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Result SetCorePriorityCapability(const u32 cap);
 | 
			
		||||
    Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
 | 
			
		||||
    Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table);
 | 
			
		||||
    Result MapIoPage_(const u32 cap, KPageTable* page_table);
 | 
			
		||||
    Result MapRegion_(const u32 cap, KPageTable* page_table);
 | 
			
		||||
    Result SetInterruptPairCapability(const u32 cap);
 | 
			
		||||
    Result SetProgramTypeCapability(const u32 cap);
 | 
			
		||||
    Result SetKernelVersionCapability(const u32 cap);
 | 
			
		||||
    Result SetHandleTableCapability(const u32 cap);
 | 
			
		||||
    Result SetDebugFlagsCapability(const u32 cap);
 | 
			
		||||
 | 
			
		||||
    template <typename F>
 | 
			
		||||
    static Result ProcessMapRegionCapability(const u32 cap, F f);
 | 
			
		||||
    static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
 | 
			
		||||
 | 
			
		||||
    Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table);
 | 
			
		||||
    Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Svc::SvcAccessFlagSet m_svc_access_flags{};
 | 
			
		||||
    InterruptFlagSet m_irq_access_flags{};
 | 
			
		||||
    u64 m_core_mask{};
 | 
			
		||||
    u64 m_phys_core_mask{};
 | 
			
		||||
    u64 m_priority_mask{};
 | 
			
		||||
    u32 m_debug_capabilities{};
 | 
			
		||||
    s32 m_handle_table_size{};
 | 
			
		||||
    u32 m_intended_kernel_version{};
 | 
			
		||||
    u32 m_program_type{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Kernel
 | 
			
		||||
@@ -3,6 +3,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <bitset>
 | 
			
		||||
 | 
			
		||||
#include "common/common_funcs.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
@@ -592,4 +594,7 @@ struct CreateProcessParameter {
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(CreateProcessParameter) == 0x30);
 | 
			
		||||
 | 
			
		||||
constexpr size_t NumSupervisorCalls = 0xC0;
 | 
			
		||||
using SvcAccessFlagSet = std::bitset<NumSupervisorCalls>;
 | 
			
		||||
 | 
			
		||||
} // namespace Kernel::Svc
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										58
									
								
								src/core/hle/kernel/svc_version.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/core/hle/kernel/svc_version.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "common/bit_field.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/literals.h"
 | 
			
		||||
 | 
			
		||||
namespace Kernel::Svc {
 | 
			
		||||
 | 
			
		||||
constexpr inline u32 ConvertToSvcMajorVersion(u32 sdk) {
 | 
			
		||||
    return sdk + 4;
 | 
			
		||||
}
 | 
			
		||||
constexpr inline u32 ConvertToSdkMajorVersion(u32 svc) {
 | 
			
		||||
    return svc - 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
constexpr inline u32 ConvertToSvcMinorVersion(u32 sdk) {
 | 
			
		||||
    return sdk;
 | 
			
		||||
}
 | 
			
		||||
constexpr inline u32 ConvertToSdkMinorVersion(u32 svc) {
 | 
			
		||||
    return svc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
union KernelVersion {
 | 
			
		||||
    u32 value;
 | 
			
		||||
    BitField<0, 4, u32> minor_version;
 | 
			
		||||
    BitField<4, 13, u32> major_version;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
constexpr inline u32 EncodeKernelVersion(u32 major, u32 minor) {
 | 
			
		||||
    return decltype(KernelVersion::minor_version)::FormatValue(minor) |
 | 
			
		||||
           decltype(KernelVersion::major_version)::FormatValue(major);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
constexpr inline u32 GetKernelMajorVersion(u32 encoded) {
 | 
			
		||||
    return std::bit_cast<decltype(KernelVersion::major_version)>(encoded).Value();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
constexpr inline u32 GetKernelMinorVersion(u32 encoded) {
 | 
			
		||||
    return std::bit_cast<decltype(KernelVersion::minor_version)>(encoded).Value();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Nintendo doesn't support programs targeting SVC versions < 3.0.
 | 
			
		||||
constexpr inline u32 RequiredKernelMajorVersion = 3;
 | 
			
		||||
constexpr inline u32 RequiredKernelMinorVersion = 0;
 | 
			
		||||
constexpr inline u32 RequiredKernelVersion =
 | 
			
		||||
    EncodeKernelVersion(RequiredKernelMajorVersion, RequiredKernelMinorVersion);
 | 
			
		||||
 | 
			
		||||
// This is the highest SVC version supported, to be updated on new kernel releases.
 | 
			
		||||
// NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor.
 | 
			
		||||
constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(15);
 | 
			
		||||
constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(3);
 | 
			
		||||
constexpr inline u32 SupportedKernelVersion =
 | 
			
		||||
    EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);
 | 
			
		||||
 | 
			
		||||
} // namespace Kernel::Svc
 | 
			
		||||
		Reference in New Issue
	
	Block a user