mirror of
				https://git.tardis.systems/mirrors/yuzu
				synced 2025-10-31 10:44:49 +01:00 
			
		
		
		
	
						commit
						de28cd0c2d
					
				| @ -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
 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user