Browse Source
Merge pull request #1928 from lioncash/caps
Merge pull request #1928 from lioncash/caps
kernel: Handle kernel capability descriptorsnce_cpp
committed by
GitHub
14 changed files with 732 additions and 125 deletions
-
1src/common/CMakeLists.txt
-
61src/common/bit_util.h
-
2src/core/CMakeLists.txt
-
11src/core/file_sys/program_metadata.cpp
-
6src/core/file_sys/program_metadata.h
-
2src/core/hle/kernel/errors.h
-
6src/core/hle/kernel/handle_table.h
-
80src/core/hle/kernel/process.cpp
-
58src/core/hle/kernel/process.h
-
355src/core/hle/kernel/process_capability.cpp
-
264src/core/hle/kernel/process_capability.h
-
5src/core/loader/deconstructed_rom_directory.cpp
-
4src/core/loader/loader.cpp
-
2src/core/loader/loader.h
@ -0,0 +1,61 @@ |
|||||
|
// Copyright 2018 yuzu emulator team |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <climits> |
||||
|
#include <cstddef> |
||||
|
|
||||
|
#ifdef _MSC_VER |
||||
|
#include <intrin.h> |
||||
|
#endif |
||||
|
|
||||
|
#include "common/common_types.h" |
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
/// Gets the size of a specified type T in bits. |
||||
|
template <typename T> |
||||
|
constexpr std::size_t BitSize() { |
||||
|
return sizeof(T) * CHAR_BIT; |
||||
|
} |
||||
|
|
||||
|
#ifdef _MSC_VER |
||||
|
inline u32 CountLeadingZeroes32(u32 value) { |
||||
|
unsigned long leading_zero = 0; |
||||
|
|
||||
|
if (_BitScanReverse(&leading_zero, value) != 0) { |
||||
|
return 31 - leading_zero; |
||||
|
} |
||||
|
|
||||
|
return 32; |
||||
|
} |
||||
|
|
||||
|
inline u64 CountLeadingZeroes64(u64 value) { |
||||
|
unsigned long leading_zero = 0; |
||||
|
|
||||
|
if (_BitScanReverse64(&leading_zero, value) != 0) { |
||||
|
return 63 - leading_zero; |
||||
|
} |
||||
|
|
||||
|
return 64; |
||||
|
} |
||||
|
#else |
||||
|
inline u32 CountLeadingZeroes32(u32 value) { |
||||
|
if (value == 0) { |
||||
|
return 32; |
||||
|
} |
||||
|
|
||||
|
return __builtin_clz(value); |
||||
|
} |
||||
|
|
||||
|
inline u64 CountLeadingZeroes64(u64 value) { |
||||
|
if (value == 0) { |
||||
|
return 64; |
||||
|
} |
||||
|
|
||||
|
return __builtin_clzll(value); |
||||
|
} |
||||
|
#endif |
||||
|
} // namespace Common |
||||
@ -0,0 +1,355 @@ |
|||||
|
// Copyright 2018 yuzu emulator team
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include "common/bit_util.h"
|
||||
|
#include "core/hle/kernel/errors.h"
|
||||
|
#include "core/hle/kernel/handle_table.h"
|
||||
|
#include "core/hle/kernel/process_capability.h"
|
||||
|
#include "core/hle/kernel/vm_manager.h"
|
||||
|
|
||||
|
namespace Kernel { |
||||
|
namespace { |
||||
|
|
||||
|
// clang-format off
|
||||
|
|
||||
|
// Shift offsets for kernel capability types.
|
||||
|
enum : u32 { |
||||
|
CapabilityOffset_PriorityAndCoreNum = 3, |
||||
|
CapabilityOffset_Syscall = 4, |
||||
|
CapabilityOffset_MapPhysical = 6, |
||||
|
CapabilityOffset_MapIO = 7, |
||||
|
CapabilityOffset_Interrupt = 11, |
||||
|
CapabilityOffset_ProgramType = 13, |
||||
|
CapabilityOffset_KernelVersion = 14, |
||||
|
CapabilityOffset_HandleTableSize = 15, |
||||
|
CapabilityOffset_Debug = 16, |
||||
|
}; |
||||
|
|
||||
|
// Combined mask of all parameters that may be initialized only once.
|
||||
|
constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) | |
||||
|
(1U << CapabilityOffset_ProgramType) | |
||||
|
(1U << CapabilityOffset_KernelVersion) | |
||||
|
(1U << CapabilityOffset_HandleTableSize) | |
||||
|
(1U << CapabilityOffset_Debug); |
||||
|
|
||||
|
// Packed kernel version indicating 10.4.0
|
||||
|
constexpr u32 PackedKernelVersion = 0x520000; |
||||
|
|
||||
|
// Indicates possible types of capabilities that can be specified.
|
||||
|
enum class CapabilityType : u32 { |
||||
|
Unset = 0U, |
||||
|
PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1, |
||||
|
Syscall = (1U << CapabilityOffset_Syscall) - 1, |
||||
|
MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1, |
||||
|
MapIO = (1U << CapabilityOffset_MapIO) - 1, |
||||
|
Interrupt = (1U << CapabilityOffset_Interrupt) - 1, |
||||
|
ProgramType = (1U << CapabilityOffset_ProgramType) - 1, |
||||
|
KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1, |
||||
|
HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1, |
||||
|
Debug = (1U << CapabilityOffset_Debug) - 1, |
||||
|
Ignorable = 0xFFFFFFFFU, |
||||
|
}; |
||||
|
|
||||
|
// clang-format on
|
||||
|
|
||||
|
constexpr CapabilityType GetCapabilityType(u32 value) { |
||||
|
return static_cast<CapabilityType>((~value & (value + 1)) - 1); |
||||
|
} |
||||
|
|
||||
|
u32 GetFlagBitOffset(CapabilityType type) { |
||||
|
const auto value = static_cast<u32>(type); |
||||
|
return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value)); |
||||
|
} |
||||
|
|
||||
|
} // Anonymous namespace
|
||||
|
|
||||
|
ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities, |
||||
|
std::size_t num_capabilities, |
||||
|
VMManager& vm_manager) { |
||||
|
Clear(); |
||||
|
|
||||
|
// Allow all cores and priorities.
|
||||
|
core_mask = 0xF; |
||||
|
priority_mask = 0xFFFFFFFFFFFFFFFF; |
||||
|
kernel_version = PackedKernelVersion; |
||||
|
|
||||
|
return ParseCapabilities(capabilities, num_capabilities, vm_manager); |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities, |
||||
|
std::size_t num_capabilities, |
||||
|
VMManager& vm_manager) { |
||||
|
Clear(); |
||||
|
|
||||
|
return ParseCapabilities(capabilities, num_capabilities, vm_manager); |
||||
|
} |
||||
|
|
||||
|
void ProcessCapabilities::InitializeForMetadatalessProcess() { |
||||
|
// Allow all cores and priorities
|
||||
|
core_mask = 0xF; |
||||
|
priority_mask = 0xFFFFFFFFFFFFFFFF; |
||||
|
kernel_version = PackedKernelVersion; |
||||
|
|
||||
|
// Allow all system calls and interrupts.
|
||||
|
svc_capabilities.set(); |
||||
|
interrupt_capabilities.set(); |
||||
|
|
||||
|
// Allow using the maximum possible amount of handles
|
||||
|
handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT); |
||||
|
|
||||
|
// Allow all debugging capabilities.
|
||||
|
is_debuggable = true; |
||||
|
can_force_debug = true; |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities, |
||||
|
std::size_t num_capabilities, |
||||
|
VMManager& vm_manager) { |
||||
|
u32 set_flags = 0; |
||||
|
u32 set_svc_bits = 0; |
||||
|
|
||||
|
for (std::size_t i = 0; i < num_capabilities; ++i) { |
||||
|
const u32 descriptor = capabilities[i]; |
||||
|
const auto type = GetCapabilityType(descriptor); |
||||
|
|
||||
|
if (type == CapabilityType::MapPhysical) { |
||||
|
i++; |
||||
|
|
||||
|
// The MapPhysical type uses two descriptor flags for its parameters.
|
||||
|
// If there's only one, then there's a problem.
|
||||
|
if (i >= num_capabilities) { |
||||
|
return ERR_INVALID_COMBINATION; |
||||
|
} |
||||
|
|
||||
|
const auto size_flags = capabilities[i]; |
||||
|
if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) { |
||||
|
return ERR_INVALID_COMBINATION; |
||||
|
} |
||||
|
|
||||
|
const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager); |
||||
|
if (result.IsError()) { |
||||
|
return result; |
||||
|
} |
||||
|
} else { |
||||
|
const auto result = |
||||
|
ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager); |
||||
|
if (result.IsError()) { |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return RESULT_SUCCESS; |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, |
||||
|
u32 flag, VMManager& vm_manager) { |
||||
|
const auto type = GetCapabilityType(flag); |
||||
|
|
||||
|
if (type == CapabilityType::Unset) { |
||||
|
return ERR_INVALID_CAPABILITY_DESCRIPTOR; |
||||
|
} |
||||
|
|
||||
|
// Bail early on ignorable entries, as one would expect,
|
||||
|
// ignorable descriptors can be ignored.
|
||||
|
if (type == CapabilityType::Ignorable) { |
||||
|
return RESULT_SUCCESS; |
||||
|
} |
||||
|
|
||||
|
// Ensure that the give flag hasn't already been initialized before.
|
||||
|
// If it has been, then bail.
|
||||
|
const u32 flag_length = GetFlagBitOffset(type); |
||||
|
const u32 set_flag = 1U << flag_length; |
||||
|
if ((set_flag & set_flags & InitializeOnceMask) != 0) { |
||||
|
return ERR_INVALID_COMBINATION; |
||||
|
} |
||||
|
set_flags |= set_flag; |
||||
|
|
||||
|
switch (type) { |
||||
|
case CapabilityType::PriorityAndCoreNum: |
||||
|
return HandlePriorityCoreNumFlags(flag); |
||||
|
case CapabilityType::Syscall: |
||||
|
return HandleSyscallFlags(set_svc_bits, flag); |
||||
|
case CapabilityType::MapIO: |
||||
|
return HandleMapIOFlags(flag, vm_manager); |
||||
|
case CapabilityType::Interrupt: |
||||
|
return HandleInterruptFlags(flag); |
||||
|
case CapabilityType::ProgramType: |
||||
|
return HandleProgramTypeFlags(flag); |
||||
|
case CapabilityType::KernelVersion: |
||||
|
return HandleKernelVersionFlags(flag); |
||||
|
case CapabilityType::HandleTableSize: |
||||
|
return HandleHandleTableFlags(flag); |
||||
|
case CapabilityType::Debug: |
||||
|
return HandleDebugFlags(flag); |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return ERR_INVALID_CAPABILITY_DESCRIPTOR; |
||||
|
} |
||||
|
|
||||
|
void ProcessCapabilities::Clear() { |
||||
|
svc_capabilities.reset(); |
||||
|
interrupt_capabilities.reset(); |
||||
|
|
||||
|
core_mask = 0; |
||||
|
priority_mask = 0; |
||||
|
|
||||
|
handle_table_size = 0; |
||||
|
kernel_version = 0; |
||||
|
|
||||
|
program_type = ProgramType::SysModule; |
||||
|
|
||||
|
is_debuggable = false; |
||||
|
can_force_debug = false; |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { |
||||
|
if (priority_mask != 0 || core_mask != 0) { |
||||
|
return ERR_INVALID_CAPABILITY_DESCRIPTOR; |
||||
|
} |
||||
|
|
||||
|
const u32 core_num_min = (flags >> 16) & 0xFF; |
||||
|
const u32 core_num_max = (flags >> 24) & 0xFF; |
||||
|
if (core_num_min > core_num_max) { |
||||
|
return ERR_INVALID_COMBINATION; |
||||
|
} |
||||
|
|
||||
|
const u32 priority_min = (flags >> 10) & 0x3F; |
||||
|
const u32 priority_max = (flags >> 4) & 0x3F; |
||||
|
if (priority_min > priority_max) { |
||||
|
return ERR_INVALID_COMBINATION; |
||||
|
} |
||||
|
|
||||
|
// The switch only has 4 usable cores.
|
||||
|
if (core_num_max >= 4) { |
||||
|
return ERR_INVALID_PROCESSOR_ID; |
||||
|
} |
||||
|
|
||||
|
const auto make_mask = [](u64 min, u64 max) { |
||||
|
const u64 range = max - min + 1; |
||||
|
const u64 mask = (1ULL << range) - 1; |
||||
|
|
||||
|
return mask << min; |
||||
|
}; |
||||
|
|
||||
|
core_mask = make_mask(core_num_min, core_num_max); |
||||
|
priority_mask = make_mask(priority_min, priority_max); |
||||
|
return RESULT_SUCCESS; |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) { |
||||
|
const u32 index = flags >> 29; |
||||
|
const u32 svc_bit = 1U << index; |
||||
|
|
||||
|
// If we've already set this svc before, bail.
|
||||
|
if ((set_svc_bits & svc_bit) != 0) { |
||||
|
return ERR_INVALID_COMBINATION; |
||||
|
} |
||||
|
set_svc_bits |= svc_bit; |
||||
|
|
||||
|
const u32 svc_mask = (flags >> 5) & 0xFFFFFF; |
||||
|
for (u32 i = 0; i < 24; ++i) { |
||||
|
const u32 svc_number = index * 24 + i; |
||||
|
|
||||
|
if ((svc_mask & (1U << i)) == 0) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (svc_number >= svc_capabilities.size()) { |
||||
|
return ERR_OUT_OF_RANGE; |
||||
|
} |
||||
|
|
||||
|
svc_capabilities[svc_number] = true; |
||||
|
} |
||||
|
|
||||
|
return RESULT_SUCCESS; |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags, |
||||
|
VMManager& vm_manager) { |
||||
|
// TODO(Lioncache): Implement once the memory manager can handle this.
|
||||
|
return RESULT_SUCCESS; |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) { |
||||
|
// TODO(Lioncache): Implement once the memory manager can handle this.
|
||||
|
return RESULT_SUCCESS; |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) { |
||||
|
constexpr u32 interrupt_ignore_value = 0x3FF; |
||||
|
const u32 interrupt0 = (flags >> 12) & 0x3FF; |
||||
|
const u32 interrupt1 = (flags >> 22) & 0x3FF; |
||||
|
|
||||
|
for (u32 interrupt : {interrupt0, interrupt1}) { |
||||
|
if (interrupt == interrupt_ignore_value) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
// NOTE:
|
||||
|
// This should be checking a generic interrupt controller value
|
||||
|
// as part of the calculation, however, given we don't currently
|
||||
|
// emulate that, it's sufficient to mark every interrupt as defined.
|
||||
|
|
||||
|
if (interrupt >= interrupt_capabilities.size()) { |
||||
|
return ERR_OUT_OF_RANGE; |
||||
|
} |
||||
|
|
||||
|
interrupt_capabilities[interrupt] = true; |
||||
|
} |
||||
|
|
||||
|
return RESULT_SUCCESS; |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { |
||||
|
const u32 reserved = flags >> 17; |
||||
|
if (reserved != 0) { |
||||
|
return ERR_RESERVED_VALUE; |
||||
|
} |
||||
|
|
||||
|
program_type = static_cast<ProgramType>((flags >> 14) & 0b111); |
||||
|
return RESULT_SUCCESS; |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { |
||||
|
// Yes, the internal member variable is checked in the actual kernel here.
|
||||
|
// This might look odd for options that are only allowed to be initialized
|
||||
|
// just once, however the kernel has a separate initialization function for
|
||||
|
// kernel processes and userland processes. The kernel variant sets this
|
||||
|
// member variable ahead of time.
|
||||
|
|
||||
|
const u32 major_version = kernel_version >> 19; |
||||
|
|
||||
|
if (major_version != 0 || flags < 0x80000) { |
||||
|
return ERR_INVALID_CAPABILITY_DESCRIPTOR; |
||||
|
} |
||||
|
|
||||
|
kernel_version = flags; |
||||
|
return RESULT_SUCCESS; |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) { |
||||
|
const u32 reserved = flags >> 26; |
||||
|
if (reserved != 0) { |
||||
|
return ERR_RESERVED_VALUE; |
||||
|
} |
||||
|
|
||||
|
handle_table_size = (flags >> 16) & 0x3FF; |
||||
|
return RESULT_SUCCESS; |
||||
|
} |
||||
|
|
||||
|
ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) { |
||||
|
const u32 reserved = flags >> 19; |
||||
|
if (reserved != 0) { |
||||
|
return ERR_RESERVED_VALUE; |
||||
|
} |
||||
|
|
||||
|
is_debuggable = (flags & 0x20000) != 0; |
||||
|
can_force_debug = (flags & 0x40000) != 0; |
||||
|
return RESULT_SUCCESS; |
||||
|
} |
||||
|
|
||||
|
} // namespace Kernel
|
||||
@ -0,0 +1,264 @@ |
|||||
|
// Copyright 2018 yuzu emulator team |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <bitset> |
||||
|
|
||||
|
#include "common/common_types.h" |
||||
|
|
||||
|
union ResultCode; |
||||
|
|
||||
|
namespace Kernel { |
||||
|
|
||||
|
class VMManager; |
||||
|
|
||||
|
/// The possible types of programs that may be indicated |
||||
|
/// by the program type capability descriptor. |
||||
|
enum class ProgramType { |
||||
|
SysModule, |
||||
|
Application, |
||||
|
Applet, |
||||
|
}; |
||||
|
|
||||
|
/// Handles kernel capability descriptors that are provided by |
||||
|
/// application metadata. These descriptors provide information |
||||
|
/// that alters certain parameters for kernel process instance |
||||
|
/// that will run said application (or applet). |
||||
|
/// |
||||
|
/// Capabilities are a sequence of flag descriptors, that indicate various |
||||
|
/// configurations and constraints for a particular process. |
||||
|
/// |
||||
|
/// Flag types are indicated by a sequence of set low bits. E.g. the |
||||
|
/// types are indicated with the low bits as follows (where x indicates "don't care"): |
||||
|
/// |
||||
|
/// - Priority and core mask : 0bxxxxxxxxxxxx0111 |
||||
|
/// - Allowed service call mask: 0bxxxxxxxxxxx01111 |
||||
|
/// - Map physical memory : 0bxxxxxxxxx0111111 |
||||
|
/// - Map IO memory : 0bxxxxxxxx01111111 |
||||
|
/// - Interrupts : 0bxxxx011111111111 |
||||
|
/// - Application type : 0bxx01111111111111 |
||||
|
/// - Kernel version : 0bx011111111111111 |
||||
|
/// - Handle table size : 0b0111111111111111 |
||||
|
/// - Debugger flags : 0b1111111111111111 |
||||
|
/// |
||||
|
/// These are essentially a bit offset subtracted by 1 to create a mask. |
||||
|
/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000) |
||||
|
/// subtracted by one (7 -> 0b0111) |
||||
|
/// |
||||
|
/// An example of a bit layout (using the map physical layout): |
||||
|
/// <example> |
||||
|
/// The MapPhysical type indicates a sequence entry pair of: |
||||
|
/// |
||||
|
/// [initial, memory_flags], where: |
||||
|
/// |
||||
|
/// initial: |
||||
|
/// bits: |
||||
|
/// 7-24: Starting page to map memory at. |
||||
|
/// 25 : Indicates if the memory should be mapped as read only. |
||||
|
/// |
||||
|
/// memory_flags: |
||||
|
/// bits: |
||||
|
/// 7-20 : Number of pages to map |
||||
|
/// 21-25: Seems to be reserved (still checked against though) |
||||
|
/// 26 : Whether or not the memory being mapped is IO memory, or physical memory |
||||
|
/// </example> |
||||
|
/// |
||||
|
class ProcessCapabilities { |
||||
|
public: |
||||
|
using InterruptCapabilities = std::bitset<1024>; |
||||
|
using SyscallCapabilities = std::bitset<128>; |
||||
|
|
||||
|
ProcessCapabilities() = default; |
||||
|
ProcessCapabilities(const ProcessCapabilities&) = delete; |
||||
|
ProcessCapabilities(ProcessCapabilities&&) = default; |
||||
|
|
||||
|
ProcessCapabilities& operator=(const ProcessCapabilities&) = delete; |
||||
|
ProcessCapabilities& operator=(ProcessCapabilities&&) = default; |
||||
|
|
||||
|
/// Initializes this process capabilities instance for a kernel process. |
||||
|
/// |
||||
|
/// @param capabilities The capabilities to parse |
||||
|
/// @param num_capabilities The number of capabilities to parse. |
||||
|
/// @param vm_manager The memory manager to use for handling any mapping-related |
||||
|
/// operations (such as mapping IO memory, etc). |
||||
|
/// |
||||
|
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized, |
||||
|
/// otherwise, an error code upon failure. |
||||
|
/// |
||||
|
ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities, |
||||
|
VMManager& vm_manager); |
||||
|
|
||||
|
/// Initializes this process capabilities instance for a userland process. |
||||
|
/// |
||||
|
/// @param capabilities The capabilities to parse. |
||||
|
/// @param num_capabilities The total number of capabilities to parse. |
||||
|
/// @param vm_manager The memory manager to use for handling any mapping-related |
||||
|
/// operations (such as mapping IO memory, etc). |
||||
|
/// |
||||
|
/// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized, |
||||
|
/// otherwise, an error code upon failure. |
||||
|
/// |
||||
|
ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities, |
||||
|
VMManager& vm_manager); |
||||
|
|
||||
|
/// Initializes this process capabilities instance for a process that does not |
||||
|
/// have any metadata to parse. |
||||
|
/// |
||||
|
/// This is necessary, as we allow running raw executables, and the internal |
||||
|
/// kernel process capabilities also determine what CPU cores the process is |
||||
|
/// allowed to run on, and what priorities are allowed for threads. It also |
||||
|
/// determines the max handle table size, what the program type is, whether or |
||||
|
/// not the process can be debugged, or whether it's possible for a process to |
||||
|
/// forcibly debug another process. |
||||
|
/// |
||||
|
/// Given the above, this essentially enables all capabilities across the board |
||||
|
/// for the process. It allows the process to: |
||||
|
/// |
||||
|
/// - Run on any core |
||||
|
/// - Use any thread priority |
||||
|
/// - Use the maximum amount of handles a process is allowed to. |
||||
|
/// - Be debuggable |
||||
|
/// - Forcibly debug other processes. |
||||
|
/// |
||||
|
/// Note that this is not a behavior that the kernel allows a process to do via |
||||
|
/// a single function like this. This is yuzu-specific behavior to handle |
||||
|
/// executables with no capability descriptors whatsoever to derive behavior from. |
||||
|
/// It being yuzu-specific is why this is also not the default behavior and not |
||||
|
/// done by default in the constructor. |
||||
|
/// |
||||
|
void InitializeForMetadatalessProcess(); |
||||
|
|
||||
|
/// Gets the allowable core mask |
||||
|
u64 GetCoreMask() const { |
||||
|
return core_mask; |
||||
|
} |
||||
|
|
||||
|
/// Gets the allowable priority mask |
||||
|
u64 GetPriorityMask() const { |
||||
|
return priority_mask; |
||||
|
} |
||||
|
|
||||
|
/// Gets the SVC access permission bits |
||||
|
const SyscallCapabilities& GetServiceCapabilities() const { |
||||
|
return svc_capabilities; |
||||
|
} |
||||
|
|
||||
|
/// Gets the valid interrupt bits. |
||||
|
const InterruptCapabilities& GetInterruptCapabilities() const { |
||||
|
return interrupt_capabilities; |
||||
|
} |
||||
|
|
||||
|
/// Gets the program type for this process. |
||||
|
ProgramType GetProgramType() const { |
||||
|
return program_type; |
||||
|
} |
||||
|
|
||||
|
/// Gets the number of total allowable handles for the process' handle table. |
||||
|
u32 GetHandleTableSize() const { |
||||
|
return handle_table_size; |
||||
|
} |
||||
|
|
||||
|
/// Gets the kernel version value. |
||||
|
u32 GetKernelVersion() const { |
||||
|
return kernel_version; |
||||
|
} |
||||
|
|
||||
|
/// Whether or not this process can be debugged. |
||||
|
bool IsDebuggable() const { |
||||
|
return is_debuggable; |
||||
|
} |
||||
|
|
||||
|
/// Whether or not this process can forcibly debug another |
||||
|
/// process, even if that process is not considered debuggable. |
||||
|
bool CanForceDebug() const { |
||||
|
return can_force_debug; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
/// Attempts to parse a given sequence of capability descriptors. |
||||
|
/// |
||||
|
/// @param capabilities The sequence of capability descriptors to parse. |
||||
|
/// @param num_capabilities The number of descriptors within the given sequence. |
||||
|
/// @param vm_manager The memory manager that will perform any memory |
||||
|
/// mapping if necessary. |
||||
|
/// |
||||
|
/// @return RESULT_SUCCESS if no errors occur, otherwise an error code. |
||||
|
/// |
||||
|
ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, |
||||
|
VMManager& vm_manager); |
||||
|
|
||||
|
/// Attempts to parse a capability descriptor that is only represented by a |
||||
|
/// single flag set. |
||||
|
/// |
||||
|
/// @param set_flags Running set of flags that are used to catch |
||||
|
/// flags being initialized more than once when they shouldn't be. |
||||
|
/// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask. |
||||
|
/// @param flag The flag to attempt to parse. |
||||
|
/// @param vm_manager The memory manager that will perform any memory |
||||
|
/// mapping if necessary. |
||||
|
/// |
||||
|
/// @return RESULT_SUCCESS if no errors occurred, otherwise an error code. |
||||
|
/// |
||||
|
ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, |
||||
|
VMManager& vm_manager); |
||||
|
|
||||
|
/// Clears the internal state of this process capability instance. Necessary, |
||||
|
/// to have a sane starting point due to us allowing running executables without |
||||
|
/// configuration metadata. We assume a process is not going to have metadata, |
||||
|
/// and if it turns out that the process does, in fact, have metadata, then |
||||
|
/// we attempt to parse it. Thus, we need this to reset data members back to |
||||
|
/// a good state. |
||||
|
/// |
||||
|
/// DO NOT ever make this a public member function. This isn't an invariant |
||||
|
/// anything external should depend upon (and if anything comes to rely on it, |
||||
|
/// you should immediately be questioning the design of that thing, not this |
||||
|
/// class. If the kernel itself can run without depending on behavior like that, |
||||
|
/// then so can yuzu). |
||||
|
/// |
||||
|
void Clear(); |
||||
|
|
||||
|
/// Handles flags related to the priority and core number capability flags. |
||||
|
ResultCode HandlePriorityCoreNumFlags(u32 flags); |
||||
|
|
||||
|
/// Handles flags related to determining the allowable SVC mask. |
||||
|
ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags); |
||||
|
|
||||
|
/// Handles flags related to mapping physical memory pages. |
||||
|
ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager); |
||||
|
|
||||
|
/// Handles flags related to mapping IO pages. |
||||
|
ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager); |
||||
|
|
||||
|
/// Handles flags related to the interrupt capability flags. |
||||
|
ResultCode HandleInterruptFlags(u32 flags); |
||||
|
|
||||
|
/// Handles flags related to the program type. |
||||
|
ResultCode HandleProgramTypeFlags(u32 flags); |
||||
|
|
||||
|
/// Handles flags related to the handle table size. |
||||
|
ResultCode HandleHandleTableFlags(u32 flags); |
||||
|
|
||||
|
/// Handles flags related to the kernel version capability flags. |
||||
|
ResultCode HandleKernelVersionFlags(u32 flags); |
||||
|
|
||||
|
/// Handles flags related to debug-specific capabilities. |
||||
|
ResultCode HandleDebugFlags(u32 flags); |
||||
|
|
||||
|
SyscallCapabilities svc_capabilities; |
||||
|
InterruptCapabilities interrupt_capabilities; |
||||
|
|
||||
|
u64 core_mask = 0; |
||||
|
u64 priority_mask = 0; |
||||
|
|
||||
|
u32 handle_table_size = 0; |
||||
|
u32 kernel_version = 0; |
||||
|
|
||||
|
ProgramType program_type = ProgramType::SysModule; |
||||
|
|
||||
|
bool is_debuggable = false; |
||||
|
bool can_force_debug = false; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Kernel |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue