committed by
GitHub
6 changed files with 738 additions and 0 deletions
-
2src/core/CMakeLists.txt
-
20src/core/hardware_properties.h
-
358src/core/hle/kernel/k_capabilities.cpp
-
295src/core/hle/kernel/k_capabilities.h
-
5src/core/hle/kernel/svc_types.h
-
58src/core/hle/kernel/svc_version.h
@ -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
|
|||
@ -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 |
|||
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue