mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-06-02 00:58:44 +02:00
9b9bf8d300
* Fix `AddClearColorSubpass` bug where it would not generate a `VkCmdNextSubpass` when an attachment clear was utilized * Fix `AddSubpass` bug where the Depth Stencil texture would not be synced * Respect `VkCommandPool` external synchronization requirements by making it thread-local with a custom RAII wrapper * Fix linear RT width calculation as it's provided in terms of bytes rather than format units * Fix `AllocateStagingBuffer` bug where it would not supply `eTransferDst` as a usage flag * Fix `AllocateMappedImage` where `VkMemoryPropertyFlags` were not respected resulting in non-`eHostVisible` memory being utilized * Change feature requirement in `AndroidManifest.xml` to Vulkan 1.1 from OGL 3.1 as this was incorrect
244 lines
12 KiB
C++
244 lines
12 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
|
|
|
#pragma once
|
|
|
|
#include <vfs/npdm.h>
|
|
#include "KThread.h"
|
|
#include "KTransferMemory.h"
|
|
#include "KSession.h"
|
|
#include "KEvent.h"
|
|
|
|
namespace skyline {
|
|
namespace constant {
|
|
constexpr u16 TlsSlotSize{0x200}; //!< The size of a single TLS slot
|
|
constexpr u8 TlsSlots{PAGE_SIZE / TlsSlotSize}; //!< The amount of TLS slots in a single page
|
|
constexpr KHandle BaseHandleIndex{0xD000}; //!< The index of the base handle
|
|
}
|
|
|
|
namespace kernel::type {
|
|
/**
|
|
* @brief KProcess manages process-global state such as memory, kernel handles allocated to the process and synchronization primitives
|
|
*/
|
|
class KProcess : public KSyncObject {
|
|
public: // We have intermittent public/private members to ensure proper construction/destruction order
|
|
MemoryManager memory;
|
|
|
|
private:
|
|
std::mutex threadMutex; //!< Synchronizes thread creation to prevent a race between thread creation and thread killing
|
|
bool disableThreadCreation{}; //!< Whether to disable thread creation, we use this to prevent thread creation after all threads have been killed
|
|
std::vector<std::shared_ptr<KThread>> threads;
|
|
|
|
using SyncWaiters = std::multimap<void *, std::shared_ptr<KThread>>;
|
|
std::mutex syncWaiterMutex; //!< Synchronizes all mutations to the map to prevent races
|
|
SyncWaiters syncWaiters; //!< All threads waiting on process-wide synchronization primitives (Atomic keys + Address Arbiter)
|
|
|
|
/**
|
|
* @brief The status of a single TLS page (A page is 4096 bytes on ARMv8)
|
|
* Each TLS page has 8 slots, each 0x200 (512) bytes in size
|
|
* The first slot of the first page is reserved for user-mode exception handling
|
|
* @url https://switchbrew.org/wiki/Thread_Local_Storage
|
|
*/
|
|
struct TlsPage {
|
|
u8 index{}; //!< The slots are assigned sequentially, this holds the index of the last TLS slot reserved
|
|
std::shared_ptr<KPrivateMemory> memory; //!< A single page sized memory allocation for this TLS page
|
|
|
|
TlsPage(std::shared_ptr<KPrivateMemory> memory);
|
|
|
|
/**
|
|
* @return A non-null pointer to a TLS page slot on success, a nullptr will be returned if this page is full
|
|
* @note This function is not thread-safe and should be called exclusively by one thread at a time
|
|
*/
|
|
u8 *ReserveSlot();
|
|
};
|
|
|
|
public:
|
|
u8 *tlsExceptionContext{}; //!< A pointer to the TLS exception handling context slot
|
|
std::mutex tlsMutex; //!< A mutex to synchronize allocation of TLS pages to prevent extra pages from being created
|
|
std::vector<std::shared_ptr<TlsPage>> tlsPages; //!< All TLS pages allocated by this process
|
|
std::shared_ptr<KPrivateMemory> mainThreadStack; //!< The stack memory of the main thread stack is owned by the KProcess itself
|
|
std::shared_ptr<KPrivateMemory> heap;
|
|
vfs::NPDM npdm;
|
|
|
|
private:
|
|
std::shared_mutex handleMutex;
|
|
std::vector<std::shared_ptr<KObject>> handles;
|
|
|
|
public:
|
|
KProcess(const DeviceState &state);
|
|
|
|
~KProcess();
|
|
|
|
/**
|
|
* @brief Kill the main thread/all running threads in the process in a graceful manner
|
|
* @param join Return after the main thread has joined rather than instantly
|
|
* @param all Whether to kill all running threads or just the main thread
|
|
* @param disableCreation Whether to disable further thread creation
|
|
* @note If there are no threads then this will silently return
|
|
* @note The main thread should eventually kill the rest of the threads itself
|
|
*/
|
|
void Kill(bool join, bool all = false, bool disableCreation = false);
|
|
|
|
/**
|
|
* @brief This initializes the process heap and TLS Error Context slot pointer, it should be called prior to creating the first thread
|
|
* @note This requires VMM regions to be initialized, it will map heap at an arbitrary location otherwise
|
|
*/
|
|
void InitializeHeapTls();
|
|
|
|
/**
|
|
* @return A 0x200 TLS slot allocated inside the TLS/IO region
|
|
*/
|
|
u8 *AllocateTlsSlot();
|
|
|
|
/**
|
|
* @return A shared pointer to a KThread initialized with the specified values or nullptr, if thread creation has been disabled
|
|
* @note The default values are for the main thread and will use values from the NPDM
|
|
*/
|
|
std::shared_ptr<KThread> CreateThread(void *entry, u64 argument = 0, void *stackTop = nullptr, std::optional<u8> priority = std::nullopt, std::optional<u8> idealCore = std::nullopt);
|
|
|
|
/**
|
|
* @brief The output for functions that return created kernel objects
|
|
* @tparam objectClass The class of the kernel object
|
|
*/
|
|
template<typename objectClass>
|
|
struct HandleOut {
|
|
std::shared_ptr<objectClass> item; //!< A shared pointer to the object
|
|
KHandle handle; //!< The handle of the object in the process
|
|
};
|
|
|
|
/**
|
|
* @brief Creates a new handle to a KObject and adds it to the process handle_table
|
|
* @tparam objectClass The class of the kernel object to create
|
|
* @param args The arguments for the kernel object except handle, pid and state
|
|
*/
|
|
template<typename objectClass, typename ...objectArgs>
|
|
HandleOut<objectClass> NewHandle(objectArgs... args) {
|
|
std::unique_lock lock(handleMutex);
|
|
|
|
std::shared_ptr<objectClass> item;
|
|
if constexpr (std::is_same<objectClass, KThread>())
|
|
item = std::make_shared<objectClass>(state, constant::BaseHandleIndex + handles.size(), args...);
|
|
else
|
|
item = std::make_shared<objectClass>(state, args...);
|
|
handles.push_back(std::static_pointer_cast<KObject>(item));
|
|
return {item, static_cast<KHandle>((constant::BaseHandleIndex + handles.size()) - 1)};
|
|
}
|
|
|
|
/**
|
|
* @brief Inserts an item into the process handle table
|
|
* @return The handle of the corresponding item in the handle table
|
|
*/
|
|
template<typename objectClass>
|
|
KHandle InsertItem(std::shared_ptr<objectClass> &item) {
|
|
std::unique_lock lock(handleMutex);
|
|
|
|
handles.push_back(std::static_pointer_cast<KObject>(item));
|
|
return static_cast<KHandle>((constant::BaseHandleIndex + handles.size()) - 1);
|
|
}
|
|
|
|
template<typename objectClass = KObject>
|
|
std::shared_ptr<objectClass> GetHandle(KHandle handle) {
|
|
std::shared_lock lock(handleMutex);
|
|
|
|
KType objectType;
|
|
if constexpr(std::is_same<objectClass, KThread>()) {
|
|
constexpr KHandle threadSelf{0xFFFF8000}; // The handle used by threads to refer to themselves
|
|
if (handle == threadSelf)
|
|
return state.thread;
|
|
objectType = KType::KThread;
|
|
} else if constexpr(std::is_same<objectClass, KProcess>()) {
|
|
constexpr KHandle processSelf{0xFFFF8001}; // The handle used by threads in a process to refer to the process
|
|
if (handle == processSelf)
|
|
return state.process;
|
|
objectType = KType::KProcess;
|
|
} else if constexpr(std::is_same<objectClass, KSharedMemory>()) {
|
|
objectType = KType::KSharedMemory;
|
|
} else if constexpr(std::is_same<objectClass, KTransferMemory>()) {
|
|
objectType = KType::KTransferMemory;
|
|
} else if constexpr(std::is_same<objectClass, KPrivateMemory>()) {
|
|
objectType = KType::KPrivateMemory;
|
|
} else if constexpr(std::is_same<objectClass, KSession>()) {
|
|
objectType = KType::KSession;
|
|
} else if constexpr(std::is_same<objectClass, KEvent>()) {
|
|
objectType = KType::KEvent;
|
|
} else {
|
|
throw exception("KProcess::GetHandle couldn't determine object type");
|
|
}
|
|
try {
|
|
auto &item{handles.at(handle - constant::BaseHandleIndex)};
|
|
if (item != nullptr && item->objectType == objectType)
|
|
return std::static_pointer_cast<objectClass>(item);
|
|
else if (item == nullptr)
|
|
throw exception("GetHandle was called with a deleted handle: 0x{:X}", handle);
|
|
else
|
|
throw exception("Tried to get kernel object (0x{:X}) with different type: {} when object is {}", handle, objectType, item->objectType);
|
|
} catch (const std::out_of_range &) {
|
|
throw std::out_of_range(fmt::format("GetHandle was called with an invalid handle: 0x{:X}", handle));
|
|
}
|
|
}
|
|
|
|
template<>
|
|
std::shared_ptr<KObject> GetHandle<KObject>(KHandle handle) {
|
|
std::shared_lock lock(handleMutex);
|
|
auto &item{handles.at(handle - constant::BaseHandleIndex)};
|
|
if (item != nullptr)
|
|
return item;
|
|
else
|
|
throw std::out_of_range(fmt::format("GetHandle was called with a deleted handle: 0x{:X}", handle));
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieves a kernel memory object that owns the specified address
|
|
* @param address The address to look for
|
|
* @return A shared pointer to the corresponding KMemory object
|
|
*/
|
|
std::optional<HandleOut<KMemory>> GetMemoryObject(u8 *ptr);
|
|
|
|
/**
|
|
* @brief Closes a handle in the handle table
|
|
*/
|
|
void CloseHandle(KHandle handle) {
|
|
handles.at(handle - constant::BaseHandleIndex) = nullptr;
|
|
}
|
|
|
|
/**
|
|
* @brief Clear the process handle table
|
|
* @note A handle created prior to clearing must not be retrieved after this is run
|
|
*/
|
|
void ClearHandleTable();
|
|
|
|
/**
|
|
* @brief Locks the mutex at the specified address
|
|
* @param ownerHandle The psuedo-handle of the current mutex owner
|
|
* @param tag The handle of the thread which is requesting this lock
|
|
*/
|
|
Result MutexLock(u32 *mutex, KHandle ownerHandle, KHandle tag);
|
|
|
|
/**
|
|
* @brief Unlocks the mutex at the specified address
|
|
*/
|
|
void MutexUnlock(u32 *mutex);
|
|
|
|
/**
|
|
* @brief Waits on the conditional variable at the specified address
|
|
*/
|
|
Result ConditionalVariableWait(u32 *key, u32 *mutex, KHandle tag, i64 timeout);
|
|
|
|
/**
|
|
* @brief Signals the conditional variable at the specified address
|
|
*/
|
|
void ConditionalVariableSignal(u32 *key, i32 amount);
|
|
|
|
/**
|
|
* @brief Waits on the supplied address with the specified arbitration function
|
|
*/
|
|
Result WaitForAddress(u32 *address, u32 value, i64 timeout, bool(*arbitrationFunction)(u32 *address, u32 value));
|
|
|
|
/**
|
|
* @brief Signals a variable amount of waiters at the supplied address
|
|
*/
|
|
Result SignalToAddress(u32 *address, u32 value, i32 amount, bool(*mutateFunction)(u32 *address, u32 value, u32 waiterCount) = nullptr);
|
|
};
|
|
}
|
|
}
|