diff options
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/hardware_properties.h | 20 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_capabilities.cpp | 358 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_capabilities.h | 295 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc_types.h | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc_version.h | 58 | 
6 files changed, 738 insertions, 0 deletions
| diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5afdeb5ff..3eee1cfbe 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h index 13cbdb734..45567b840 100644 --- a/src/core/hardware_properties.h +++ b/src/core/hardware_properties.h @@ -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; diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp new file mode 100644 index 000000000..64f1d7371 --- /dev/null +++ b/src/core/hle/kernel/k_capabilities.cpp @@ -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 diff --git a/src/core/hle/kernel/k_capabilities.h b/src/core/hle/kernel/k_capabilities.h new file mode 100644 index 000000000..cd96f8d23 --- /dev/null +++ b/src/core/hle/kernel/k_capabilities.h @@ -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 diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 33eebcef6..9c2f9998a 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -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 diff --git a/src/core/hle/kernel/svc_version.h b/src/core/hle/kernel/svc_version.h new file mode 100644 index 000000000..e4f47b34b --- /dev/null +++ b/src/core/hle/kernel/svc_version.h @@ -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 | 
