diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 882c9ab59..a7d5e4431 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -113,6 +113,8 @@ add_library(core STATIC
     hle/kernel/object.h
     hle/kernel/process.cpp
     hle/kernel/process.h
+    hle/kernel/process_capability.cpp
+    hle/kernel/process_capability.h
     hle/kernel/readable_event.cpp
     hle/kernel/readable_event.h
     hle/kernel/resource_limit.cpp
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 8b58d701d..a3d725866 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -11,6 +11,7 @@ namespace Kernel {
 // Confirmed Switch kernel error codes
 
 constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
+constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
 constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
 constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
 constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
index 6b7927fd8..89a3bc740 100644
--- a/src/core/hle/kernel/handle_table.h
+++ b/src/core/hle/kernel/handle_table.h
@@ -43,6 +43,9 @@ enum KernelHandle : Handle {
  */
 class HandleTable final : NonCopyable {
 public:
+    /// This is the maximum limit of handles allowed per process in Horizon
+    static constexpr std::size_t MAX_COUNT = 1024;
+
     HandleTable();
     ~HandleTable();
 
@@ -91,9 +94,6 @@ public:
     void Clear();
 
 private:
-    /// This is the maximum limit of handles allowed per process in Horizon
-    static constexpr std::size_t MAX_COUNT = 1024;
-
     /// Stores the Object referenced by the handle or null if the slot is empty.
     std::array<SharedPtr<Object>, MAX_COUNT> objects;
 
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
new file mode 100644
index 000000000..8d787547b
--- /dev/null
+++ b/src/core/hle/kernel/process_capability.cpp
@@ -0,0 +1,253 @@
+// 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;
+
+    is_debuggable = false;
+    can_force_debug = false;
+}
+
+ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
+    // TODO: Implement
+    return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
+    // TODO: Implement
+    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) {
+    // TODO: Implement
+    return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
+    // TODO: Implement
+    return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
+    // TODO: Implement
+    return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
+    // TODO: Implement
+    return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
+    // TODO: Implement
+    return RESULT_SUCCESS;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h
new file mode 100644
index 000000000..5cff10476
--- /dev/null
+++ b/src/core/hle/kernel/process_capability.h
@@ -0,0 +1,209 @@
+// 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;
+
+/// 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();
+
+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;
+    u32 program_type = 0;
+
+    bool is_debuggable = false;
+    bool can_force_debug = false;
+};
+
+} // namespace Kernel