mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 12:01:52 +01:00
libNX HID initialization
What was added: * HID Service * Support for Mutexes and Conditional Variables What was improved: * Service API now creates one instance per Session rather than a single instance for all Sessions * Changed std::map objects into std::unordered_map in KProcess * Comments on enumeration values
This commit is contained in:
parent
ec71735ece
commit
2476c5d48a
@ -55,6 +55,7 @@ namespace skyline {
|
||||
constexpr u8 DefaultPriority = 31; //!< The default priority of a process
|
||||
constexpr std::pair<int8_t, int8_t> PriorityAn = {19, -8}; //!< The range of priority for Android, taken from https://medium.com/mindorks/exploring-android-thread-priority-5d0542eebbd1
|
||||
constexpr std::pair<u8, u8> PriorityNin = {0, 63}; //!< The range of priority for the Nintendo Switch
|
||||
constexpr u32 mtxOwnerMask = 0xBFFFFFFF; //!< The mask of values which contain the owner of a mutex
|
||||
// IPC
|
||||
constexpr size_t TlsIpcSize = 0x100; //!< The size of the IPC command buffer in a TLS slot
|
||||
constexpr u8 PortSize = 0x8; //!< The size of a port name string
|
||||
@ -235,6 +236,14 @@ namespace skyline {
|
||||
void List(std::shared_ptr<Logger> logger);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Returns the current time in nanoseconds
|
||||
* @return The current time in nanoseconds
|
||||
*/
|
||||
inline long long int GetCurrTimeNs() {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
// Predeclare some classes here as we use them in DeviceState
|
||||
class NCE;
|
||||
namespace kernel {
|
||||
|
@ -75,11 +75,13 @@ namespace skyline::kernel::service::am {
|
||||
std::shared_ptr<type::KEvent> messageEvent{};
|
||||
|
||||
enum class ApplicationStatus : u8 {
|
||||
InFocus = 1, OutOfFocus = 2
|
||||
InFocus = 1, //!< The application is in foreground
|
||||
OutOfFocus = 2 //!< The application is in the background
|
||||
};
|
||||
|
||||
enum class OperationMode : u8 {
|
||||
Handheld = 0, Docked = 1
|
||||
Handheld = 0, //!< The device is in handheld mode
|
||||
Docked = 1 //!< The device is in docked mode
|
||||
} operationMode;
|
||||
public:
|
||||
ICommonStateGetter(const DeviceState &state, ServiceManager &manager);
|
||||
|
@ -69,7 +69,6 @@ namespace skyline::kernel::service {
|
||||
|
||||
public:
|
||||
Service serviceType; //!< Which service this is
|
||||
uint numSessions{}; //<! The amount of active sessions
|
||||
const bool hasLoop; //<! If the service has a loop or not
|
||||
|
||||
/**
|
||||
|
@ -2,14 +2,6 @@
|
||||
#include <os.h>
|
||||
|
||||
namespace skyline::kernel::service::hid {
|
||||
hid::hid(const DeviceState &state, ServiceManager& manager) : BaseService(state, manager, false, Service::hid, {
|
||||
{0x0, SFunc(hid::CreateAppletResource)}
|
||||
}) {}
|
||||
|
||||
void hid::CreateAppletResource(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
resource = std::static_pointer_cast<IAppletResource>(manager.NewService(Service::hid_IAppletResource, session, response));
|
||||
}
|
||||
|
||||
IAppletResource::IAppletResource(const DeviceState &state, ServiceManager& manager) : BaseService(state, manager, false, Service::hid_IAppletResource, {
|
||||
{0x0, SFunc(IAppletResource::GetSharedMemoryHandle)}
|
||||
}) {}
|
||||
@ -18,4 +10,76 @@ namespace skyline::kernel::service::hid {
|
||||
hidSharedMemory = state.os->MapSharedKernel(0, constant::hidSharedMemSize, memory::Permission(true, false, false), memory::Permission(true, true, false), memory::Type::SharedMemory);
|
||||
response.copyHandles.push_back(state.thisProcess->InsertItem<type::KSharedMemory>(hidSharedMemory));
|
||||
}
|
||||
|
||||
hid::hid(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, false, Service::hid, {
|
||||
{0x0, SFunc(hid::CreateAppletResource)},
|
||||
{0x64, SFunc(hid::SetSupportedNpadStyleSet)},
|
||||
{0x66, SFunc(hid::SetSupportedNpadIdType)},
|
||||
{0x67, SFunc(hid::ActivateNpad)},
|
||||
{0x78, SFunc(hid::SetNpadJoyHoldType)},
|
||||
{0x7A, SFunc(hid::SetNpadJoyAssignmentModeSingleByDefault)},
|
||||
{0x7B, SFunc(hid::SetNpadJoyAssignmentModeSingle)},
|
||||
{0x7C, SFunc(hid::SetNpadJoyAssignmentModeDual)}
|
||||
}) {}
|
||||
|
||||
void hid::CreateAppletResource(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
resource = std::static_pointer_cast<IAppletResource>(manager.NewService(Service::hid_IAppletResource, session, response));
|
||||
}
|
||||
|
||||
void hid::SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
struct InputStruct {
|
||||
u32 styleSet;
|
||||
u64 appletUserId;
|
||||
} *input = reinterpret_cast<InputStruct *>(request.cmdArg);
|
||||
styleSet = *reinterpret_cast<StyleSet *>(&input->styleSet);
|
||||
state.logger->Write(Logger::Info, "Controller Support: Pro-Controller: {} Joy-Con: Handheld: {}, Dual: {}, L: {}, R: {} GameCube: {} PokeBall: {} NES: {} NES Handheld: {} SNES: {}", static_cast<bool>(styleSet->pro_controller), static_cast<bool>(styleSet->joycon_handheld), static_cast<bool>(styleSet->joycon_dual), static_cast<bool>(styleSet->joycon_left), static_cast<bool>
|
||||
(styleSet->joycon_right), static_cast<bool>(styleSet->gamecube), static_cast<bool>(styleSet->pokeball), static_cast<bool>(styleSet->nes), static_cast<bool>(styleSet->nes_handheld), static_cast<bool>(styleSet->snes));
|
||||
}
|
||||
|
||||
void hid::SetSupportedNpadIdType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
const auto &buffer = request.vecBufX[0];
|
||||
uint numId = buffer->size / sizeof(NpadId);
|
||||
u64 address = buffer->Address();
|
||||
for (uint i = 0; i < numId; i++) {
|
||||
auto id = state.thisProcess->ReadMemory<NpadId>(address);
|
||||
deviceMap[id] = JoyConDevice(id);
|
||||
address += sizeof(NpadId);
|
||||
}
|
||||
}
|
||||
|
||||
void hid::ActivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {}
|
||||
|
||||
void hid::SetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
struct InputStruct {
|
||||
NpadId controllerId;
|
||||
u64 appletUserId;
|
||||
} *input = reinterpret_cast<InputStruct *>(request.cmdArg);
|
||||
deviceMap[input->controllerId].assignment = JoyConAssignment::Single;
|
||||
}
|
||||
|
||||
void hid::SetNpadJoyAssignmentModeSingleByDefault(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
struct InputStruct {
|
||||
u64 appletUserId;
|
||||
JoyConOrientation orientation;
|
||||
} *input = reinterpret_cast<InputStruct *>(request.cmdArg);
|
||||
orientation = input->orientation;
|
||||
}
|
||||
|
||||
void hid::SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
struct InputStruct {
|
||||
NpadId controllerId;
|
||||
u64 appletUserId;
|
||||
JoyConSide joyDeviceType;
|
||||
} *input = reinterpret_cast<InputStruct *>(request.cmdArg);
|
||||
deviceMap[input->controllerId].assignment = JoyConAssignment::Single;
|
||||
deviceMap[input->controllerId].side = input->joyDeviceType;
|
||||
}
|
||||
|
||||
void hid::SetNpadJoyAssignmentModeDual(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
struct InputStruct {
|
||||
NpadId controllerType;
|
||||
u64 appletUserId;
|
||||
} *input = reinterpret_cast<InputStruct *>(request.cmdArg);
|
||||
deviceMap[input->controllerType].assignment = JoyConAssignment::Dual;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,87 @@ namespace skyline::kernel::service::hid {
|
||||
*/
|
||||
class hid : public BaseService {
|
||||
private:
|
||||
std::shared_ptr<IAppletResource> resource{};
|
||||
/**
|
||||
* @brief This holds the controller styles supported by an application
|
||||
*/
|
||||
struct StyleSet {
|
||||
bool pro_controller : 1; //!< The Pro Controller
|
||||
bool joycon_handheld : 1; //!< Joy-Cons in handheld mode
|
||||
bool joycon_dual : 1; //!< Joy-Cons in a pair
|
||||
bool joycon_left : 1; //!< Left Joy-Con only
|
||||
bool joycon_right : 1; //!< Right Joy-Con only
|
||||
bool gamecube : 1; //!< GameCube controller
|
||||
bool pokeball : 1; //!< Poké Ball Plus controller
|
||||
bool nes : 1; //!< NES controller
|
||||
bool nes_handheld : 1; //!< NES controller in handheld mode
|
||||
bool snes : 1; //!< SNES controller
|
||||
u32 : 22;
|
||||
};
|
||||
static_assert(sizeof(StyleSet) == 4);
|
||||
|
||||
/**
|
||||
* @brief This holds a Controller's ID (https://switchbrew.org/wiki/HID_services#NpadIdType)
|
||||
*/
|
||||
enum class NpadId : u32 {
|
||||
Player1 = 0x0, //!< 1st Player
|
||||
Player2 = 0x1, //!< 2nd Player
|
||||
Player3 = 0x2, //!< 3rd Player
|
||||
Player4 = 0x3, //!< 4th Player
|
||||
Player5 = 0x4, //!< 5th Player
|
||||
Player6 = 0x5, //!< 6th Player
|
||||
Player7 = 0x6, //!< 7th Player
|
||||
Player8 = 0x7, //!< 8th Player
|
||||
Unknown = 0x10, //!< Unknown
|
||||
Handheld = 0x20 //!< Handheld mode
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This holds a Controller's Assignment mode
|
||||
*/
|
||||
enum class JoyConAssignment {
|
||||
Dual, //!< Dual Joy-Cons
|
||||
Single, //!< Single Joy-Con
|
||||
Unset //!< Not set
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This holds which Joy-Con to use Single mode (Not if SetNpadJoyAssignmentModeSingleByDefault is used)
|
||||
*/
|
||||
enum class JoyConSide : i64 {
|
||||
Left, //!< Left Joy-Con
|
||||
Right, //!< Right Joy-Con
|
||||
Unset //!< Not set
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This denotes the orientation of the Joy-Con(s)
|
||||
*/
|
||||
enum class JoyConOrientation : u64 {
|
||||
Vertical, //!< The Joy-Con is held vertically
|
||||
Horizontal, //!< The Joy-Con is held horizontally
|
||||
Unset //!< Not set
|
||||
};
|
||||
|
||||
// TODO: Replace JoyConDevice with base NpadDevice class
|
||||
|
||||
/**
|
||||
* @brief This holds the state of a single Npad device
|
||||
*/
|
||||
struct JoyConDevice {
|
||||
NpadId id; //!< The ID of this device
|
||||
JoyConAssignment assignment{JoyConAssignment::Unset}; //!< The assignment mode of this device
|
||||
JoyConSide side{JoyConSide::Unset}; //!< The type of the device
|
||||
|
||||
JoyConDevice() : id(NpadId::Unknown) {}
|
||||
|
||||
JoyConDevice(const NpadId &id) : id(id) {}
|
||||
};
|
||||
|
||||
std::shared_ptr<IAppletResource> resource{}; //!< A shared pointer to the applet resource
|
||||
std::optional<StyleSet> styleSet; //!< The controller styles supported by the application
|
||||
std::unordered_map<NpadId, JoyConDevice> deviceMap; //!< Mapping from a controller's ID to it's corresponding JoyConDevice
|
||||
JoyConOrientation orientation{JoyConOrientation::Unset}; //!< The Orientation of the Joy-Con(s)
|
||||
|
||||
public:
|
||||
hid(const DeviceState &state, ServiceManager& manager);
|
||||
|
||||
@ -37,5 +117,40 @@ namespace skyline::kernel::service::hid {
|
||||
* @brief This returns an IAppletResource (https://switchbrew.org/wiki/HID_services#CreateAppletResource)
|
||||
*/
|
||||
void CreateAppletResource(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief This sets the style of controllers supported (https://switchbrew.org/wiki/HID_services#SetSupportedNpadStyleSet)
|
||||
*/
|
||||
void SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief This sets the NpadIds which are supported (https://switchbrew.org/wiki/HID_services#SetSupportedNpadIdType)
|
||||
*/
|
||||
void SetSupportedNpadIdType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief This requests the activation of a controller. This is stubbed as we don't have to activate anything.
|
||||
*/
|
||||
void ActivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Sets the Joy-Con hold mode (https://switchbrew.org/wiki/HID_services#SetNpadJoyHoldType)
|
||||
*/
|
||||
void SetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Sets the Joy-Con assignment mode to Single by default (https://switchbrew.org/wiki/HID_services#SetNpadJoyAssignmentModeSingleByDefault)
|
||||
*/
|
||||
void SetNpadJoyAssignmentModeSingleByDefault(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Sets the Joy-Con assignment mode to Single (https://switchbrew.org/wiki/HID_services#SetNpadJoyAssignmentModeSingle)
|
||||
*/
|
||||
void SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Sets the Joy-Con assignment mode to Dual (https://switchbrew.org/wiki/HID_services#SetNpadJoyAssignmentModeDual)
|
||||
*/
|
||||
void SetNpadJoyAssignmentModeDual(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
};
|
||||
}
|
||||
|
@ -12,64 +12,60 @@ namespace skyline::kernel::service {
|
||||
|
||||
std::shared_ptr<BaseService> ServiceManager::GetService(const Service serviceType) {
|
||||
std::shared_ptr<BaseService> serviceObj;
|
||||
if (serviceMap.find(serviceType) == serviceMap.end()) {
|
||||
switch (serviceType) {
|
||||
switch (serviceType) {
|
||||
case Service::sm:
|
||||
serviceMap[serviceType] = std::make_shared<sm::sm>(state, *this);
|
||||
serviceObj = std::make_shared<sm::sm>(state, *this);
|
||||
break;
|
||||
case Service::fatal_u:
|
||||
serviceMap[serviceType] = std::make_shared<fatal::fatalU>(state, *this);
|
||||
serviceObj = std::make_shared<fatal::fatalU>(state, *this);
|
||||
break;
|
||||
case Service::set_sys:
|
||||
serviceMap[serviceType] = std::make_shared<set::sys>(state, *this);
|
||||
serviceObj = std::make_shared<set::sys>(state, *this);
|
||||
break;
|
||||
case Service::apm:
|
||||
serviceMap[serviceType] = std::make_shared<apm::apm>(state, *this);
|
||||
serviceObj = std::make_shared<apm::apm>(state, *this);
|
||||
break;
|
||||
case Service::apm_ISession:
|
||||
serviceMap[serviceType] = std::make_shared<apm::ISession>(state, *this);
|
||||
serviceObj = std::make_shared<apm::ISession>(state, *this);
|
||||
break;
|
||||
case Service::am_appletOE:
|
||||
serviceMap[serviceType] = std::make_shared<am::appletOE>(state, *this);
|
||||
serviceObj = std::make_shared<am::appletOE>(state, *this);
|
||||
break;
|
||||
case Service::am_IApplicationProxy:
|
||||
serviceMap[serviceType] = std::make_shared<am::IApplicationProxy>(state, *this);
|
||||
serviceObj = std::make_shared<am::IApplicationProxy>(state, *this);
|
||||
break;
|
||||
case Service::am_ICommonStateGetter:
|
||||
serviceMap[serviceType] = std::make_shared<am::ICommonStateGetter>(state, *this);
|
||||
serviceObj = std::make_shared<am::ICommonStateGetter>(state, *this);
|
||||
break;
|
||||
case Service::am_IWindowController:
|
||||
serviceMap[serviceType] = std::make_shared<am::IWindowController>(state, *this);
|
||||
serviceObj = std::make_shared<am::IWindowController>(state, *this);
|
||||
break;
|
||||
case Service::am_IAudioController:
|
||||
serviceMap[serviceType] = std::make_shared<am::IAudioController>(state, *this);
|
||||
serviceObj = std::make_shared<am::IAudioController>(state, *this);
|
||||
break;
|
||||
case Service::am_IDisplayController:
|
||||
serviceMap[serviceType] = std::make_shared<am::IDisplayController>(state, *this);
|
||||
serviceObj = std::make_shared<am::IDisplayController>(state, *this);
|
||||
break;
|
||||
case Service::am_ISelfController:
|
||||
serviceMap[serviceType] = std::make_shared<am::ISelfController>(state, *this);
|
||||
serviceObj = std::make_shared<am::ISelfController>(state, *this);
|
||||
break;
|
||||
case Service::am_ILibraryAppletCreator:
|
||||
serviceMap[serviceType] = std::make_shared<am::ILibraryAppletCreator>(state, *this);
|
||||
serviceObj = std::make_shared<am::ILibraryAppletCreator>(state, *this);
|
||||
break;
|
||||
case Service::am_IApplicationFunctions:
|
||||
serviceMap[serviceType] = std::make_shared<am::IApplicationFunctions>(state, *this);
|
||||
serviceObj = std::make_shared<am::IApplicationFunctions>(state, *this);
|
||||
break;
|
||||
case Service::am_IDebugFunctions:
|
||||
serviceMap[serviceType] = std::make_shared<am::IDebugFunctions>(state, *this);
|
||||
serviceObj = std::make_shared<am::IDebugFunctions>(state, *this);
|
||||
break;
|
||||
case Service::hid:
|
||||
serviceMap[serviceType] = std::make_shared<hid::hid>(state, *this);
|
||||
serviceObj = std::make_shared<hid::hid>(state, *this);
|
||||
break;
|
||||
case Service::hid_IAppletResource:
|
||||
serviceMap[serviceType] = std::make_shared<hid::IAppletResource>(state, *this);
|
||||
serviceObj = std::make_shared<hid::IAppletResource>(state, *this);
|
||||
break;
|
||||
}
|
||||
serviceObj = serviceMap[serviceType];
|
||||
} else
|
||||
serviceObj = serviceMap.at(serviceType);
|
||||
serviceObj->numSessions++;
|
||||
}
|
||||
serviceVec.push_back(serviceObj);
|
||||
return serviceObj;
|
||||
}
|
||||
|
||||
@ -89,20 +85,19 @@ namespace skyline::kernel::service {
|
||||
}
|
||||
|
||||
void ServiceManager::CloseSession(const handle_t handle) {
|
||||
auto object = state.thisProcess->GetHandle<type::KSession>(handle);
|
||||
if (object->serviceStatus == type::KSession::ServiceStatus::Open) {
|
||||
if (object->isDomain) {
|
||||
for (const auto &service : object->domainTable)
|
||||
if (!(service.second->numSessions--))
|
||||
serviceMap.erase(service.second->serviceType);
|
||||
} else if (!(serviceMap.at(object->serviceType)->numSessions--))
|
||||
serviceMap.erase(object->serviceType);
|
||||
object->serviceStatus = type::KSession::ServiceStatus::Closed;
|
||||
auto session = state.thisProcess->GetHandle<type::KSession>(handle);
|
||||
if (session->serviceStatus == type::KSession::ServiceStatus::Open) {
|
||||
if (session->isDomain) {
|
||||
for (const auto &[objectId, service] : session->domainTable)
|
||||
serviceVec.erase(std::remove(serviceVec.begin(), serviceVec.end(), service), serviceVec.end());
|
||||
} else
|
||||
serviceVec.erase(std::remove(serviceVec.begin(), serviceVec.end(), session->serviceObject), serviceVec.end());
|
||||
session->serviceStatus = type::KSession::ServiceStatus::Closed;
|
||||
}
|
||||
};
|
||||
|
||||
void ServiceManager::Loop() {
|
||||
for (auto&[index, service] : serviceMap)
|
||||
for (auto &service : serviceVec)
|
||||
if (service->hasLoop)
|
||||
service->Loop();
|
||||
}
|
||||
@ -127,8 +122,7 @@ namespace skyline::kernel::service {
|
||||
service->HandleRequest(*session, request, response);
|
||||
break;
|
||||
case ipc::DomainCommand::CloseVHandle:
|
||||
if (!(service->numSessions--))
|
||||
serviceMap.erase(service->serviceType);
|
||||
serviceVec.erase(std::remove(serviceVec.begin(), serviceVec.end(), service), serviceVec.end());
|
||||
session->domainTable.erase(request.domain->object_id);
|
||||
break;
|
||||
}
|
||||
|
@ -11,8 +11,12 @@ namespace skyline::kernel::service {
|
||||
class ServiceManager {
|
||||
private:
|
||||
const DeviceState &state; //!< The state of the device
|
||||
std::unordered_map<Service, std::shared_ptr<BaseService>> serviceMap; //!< A map from it's type to a BaseService object
|
||||
std::vector<std::shared_ptr<BaseService>> serviceVec; //!< A vector with all of the services
|
||||
|
||||
/**
|
||||
* @param serviceType The type of service requested
|
||||
* @return A shared pointer to an instance of the service
|
||||
*/
|
||||
std::shared_ptr<BaseService> GetService(const Service serviceType);
|
||||
|
||||
public:
|
||||
|
@ -59,7 +59,7 @@ namespace skyline::kernel::svc {
|
||||
case 2:
|
||||
state.thisThread->status = type::KThread::ThreadStatus::Runnable; // Will cause the application to awaken on the next iteration of the main loop
|
||||
default:
|
||||
state.thisThread->timeoutEnd = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count() + in;
|
||||
state.thisThread->timeout = GetCurrTimeNs() + in;
|
||||
state.thisThread->status = type::KThread::ThreadStatus::Sleeping;
|
||||
}
|
||||
}
|
||||
@ -98,7 +98,7 @@ namespace skyline::kernel::svc {
|
||||
}
|
||||
|
||||
void WaitSynchronization(DeviceState &state) {
|
||||
state.thisThread->timeoutEnd = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count() + state.nce->GetRegister(Xreg::X3);
|
||||
state.thisThread->timeout = GetCurrTimeNs() + state.nce->GetRegister(Xreg::X3);
|
||||
auto numHandles = state.nce->GetRegister(Wreg::W2);
|
||||
if (numHandles > constant::MaxSyncHandles) {
|
||||
state.nce->SetRegister(Wreg::W0, constant::status::MaxHandles);
|
||||
@ -126,7 +126,54 @@ namespace skyline::kernel::svc {
|
||||
state.thisThread->waitObjects.push_back(syncObject);
|
||||
syncObject->waitThreads.push_back(state.thisThread->pid);
|
||||
}
|
||||
state.thisThread->status = type::KThread::ThreadStatus::Waiting;
|
||||
state.thisThread->status = type::KThread::ThreadStatus::WaitSync;
|
||||
}
|
||||
|
||||
void ArbitrateLock(DeviceState &state) {
|
||||
if (state.nce->GetRegister(Wreg::W2) != state.thisThread->handle)
|
||||
throw exception("A process requested locking a thread on behalf of another process");
|
||||
state.thisProcess->MutexLock(state.nce->GetRegister(Xreg::X1));
|
||||
state.nce->SetRegister(Wreg::W0, constant::status::Success);
|
||||
}
|
||||
|
||||
void ArbitrateUnlock(DeviceState &state) {
|
||||
state.thisProcess->MutexUnlock(state.nce->GetRegister(Xreg::X0));
|
||||
state.nce->SetRegister(Wreg::W0, constant::status::Success);
|
||||
}
|
||||
|
||||
void WaitProcessWideKeyAtomic(DeviceState &state) {
|
||||
auto mtxAddr = state.nce->GetRegister(Xreg::X0);
|
||||
if (state.nce->GetRegister(Wreg::W2) != state.thisThread->handle)
|
||||
throw exception("svcWaitProcessWideKeyAtomic was called on behalf of another thread");
|
||||
state.thisProcess->MutexUnlock(mtxAddr);
|
||||
auto &cvarVec = state.thisProcess->condVarMap[state.nce->GetRegister(Xreg::X1)];
|
||||
for (auto thread = cvarVec.begin();; thread++) {
|
||||
if ((*thread)->priority < state.thisThread->priority) {
|
||||
cvarVec.insert(thread, state.thisThread);
|
||||
break;
|
||||
} else if (thread + 1 == cvarVec.end()) {
|
||||
cvarVec.push_back(state.thisThread);
|
||||
break;
|
||||
}
|
||||
}
|
||||
state.thisThread->status = type::KThread::ThreadStatus::WaitCondVar;
|
||||
state.thisThread->timeout = GetCurrTimeNs() + state.nce->GetRegister(Xreg::X3);
|
||||
state.nce->SetRegister(Wreg::W0, constant::status::Success);
|
||||
}
|
||||
|
||||
void SignalProcessWideKey(DeviceState &state) {
|
||||
auto address = state.nce->GetRegister(Xreg::X0);
|
||||
auto count = state.nce->GetRegister(Wreg::W1);
|
||||
state.nce->SetRegister(Wreg::W0, constant::status::Success);
|
||||
if (!state.thisProcess->condVarMap.count(address))
|
||||
return; // No threads to awaken
|
||||
auto &cvarVec = state.thisProcess->condVarMap[address];
|
||||
count = std::min(count, static_cast<u32>(cvarVec.size()));
|
||||
for (uint index = 0; index < count; index++)
|
||||
cvarVec[index]->status = type::KThread::ThreadStatus::Runnable;
|
||||
cvarVec.erase(cvarVec.begin(), cvarVec.begin() + count);
|
||||
if (cvarVec.empty())
|
||||
state.thisProcess->condVarMap.erase(address);
|
||||
}
|
||||
|
||||
void ConnectToNamedPort(DeviceState &state) {
|
||||
@ -150,7 +197,7 @@ namespace skyline::kernel::svc {
|
||||
std::string::size_type pos = 0;
|
||||
while ((pos = debug.find("\r\n", pos)) != std::string::npos)
|
||||
debug.erase(pos, 2);
|
||||
state.logger->Write(Logger::Info, "svcOutputDebugString: {}", debug);
|
||||
state.logger->Write(Logger::Info, "Debug Output: {}", debug);
|
||||
state.nce->SetRegister(Wreg::W0, 0);
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,26 @@ namespace skyline {
|
||||
*/
|
||||
void WaitSynchronization(DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief Locks a specified mutex
|
||||
*/
|
||||
void ArbitrateLock(DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief Unlocks a specified mutex
|
||||
*/
|
||||
void ArbitrateUnlock(DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief Waits on a process-wide key (Conditional-Variable)
|
||||
*/
|
||||
void WaitProcessWideKeyAtomic(DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief Signals a process-wide key (Conditional-Variable)
|
||||
*/
|
||||
void SignalProcessWideKey(DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief Connects to a named IPC port
|
||||
*/
|
||||
@ -146,10 +166,10 @@ namespace skyline {
|
||||
nullptr, // 0x17
|
||||
WaitSynchronization, // 0x18
|
||||
nullptr, // 0x19
|
||||
nullptr, // 0x1a
|
||||
nullptr, // 0x1b
|
||||
nullptr, // 0x1c
|
||||
nullptr, // 0x1d
|
||||
ArbitrateLock, // 0x1a
|
||||
ArbitrateUnlock, // 0x1b
|
||||
WaitProcessWideKeyAtomic, // 0x1c
|
||||
SignalProcessWideKey, // 0x1d
|
||||
nullptr, // 0x1e
|
||||
ConnectToNamedPort, // 0x1f
|
||||
nullptr, // 0x20
|
||||
|
@ -101,4 +101,42 @@ namespace skyline::kernel::type {
|
||||
sharedSize += region.second->size;
|
||||
return sharedSize;
|
||||
}
|
||||
|
||||
void KProcess::MutexLock(u64 address) {
|
||||
auto mtxVec = state.thisProcess->mutexMap[address];
|
||||
u32 mtxVal = state.thisProcess->ReadMemory<u32>(address);
|
||||
if (mtxVec.empty()) {
|
||||
mtxVal = (mtxVal & ~constant::mtxOwnerMask) | state.thisThread->handle;
|
||||
state.thisProcess->WriteMemory(mtxVal, address);
|
||||
} else {
|
||||
for (auto thread = mtxVec.begin();; thread++) {
|
||||
if ((*thread)->priority < state.thisThread->priority) {
|
||||
mtxVec.insert(thread, state.thisThread);
|
||||
break;
|
||||
} else if (thread + 1 == mtxVec.end()) {
|
||||
mtxVec.push_back(state.thisThread);
|
||||
break;
|
||||
}
|
||||
}
|
||||
state.thisThread->status = KThread::ThreadStatus::WaitMutex;
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::MutexUnlock(u64 address) {
|
||||
auto mtxVec = state.thisProcess->mutexMap[address];
|
||||
u32 mtxVal = state.thisProcess->ReadMemory<u32>(address);
|
||||
if ((mtxVal & constant::mtxOwnerMask) != state.thisThread->pid)
|
||||
throw exception("A non-owner thread tried to release a mutex");
|
||||
if (mtxVec.empty()) {
|
||||
mtxVal = 0;
|
||||
} else {
|
||||
auto &thread = mtxVec.front();
|
||||
mtxVal = (mtxVal & ~constant::mtxOwnerMask) | thread->handle;
|
||||
thread->status = KThread::ThreadStatus::Runnable;
|
||||
mtxVec.erase(mtxVec.begin());
|
||||
if (!mtxVec.empty())
|
||||
mtxVal |= (~constant::mtxOwnerMask);
|
||||
}
|
||||
state.thisProcess->WriteMemory(mtxVal, address);
|
||||
}
|
||||
}
|
||||
|
@ -57,14 +57,20 @@ namespace skyline::kernel::type {
|
||||
int memFd; //!< The file descriptor to the memory of the process
|
||||
|
||||
public:
|
||||
enum class ProcessStatus { Created, CreatedAttached, Started, Crashed, StartedAttached, Exiting, Exited, DebugSuspended } status = ProcessStatus::Created; //!< The state of the process
|
||||
enum class ProcessStatus {
|
||||
Created, //!< The process was created but the main thread has not started yet
|
||||
Started, //!< The process has been started
|
||||
Exiting //!< The process is exiting
|
||||
} status = ProcessStatus::Created; //!< The state of the process
|
||||
handle_t handleIndex = constant::BaseHandleIndex; //!< This is used to keep track of what to map as an handle
|
||||
pid_t mainThread; //!< The PID of the main thread
|
||||
size_t mainThreadStackSz; //!< The size of the main thread's stack (All other threads map stack themselves so we don't know the size per-se)
|
||||
std::map<u64, std::shared_ptr<KPrivateMemory>> memoryMap; //!< A mapping from every address to a shared pointer of it's corresponding KPrivateMemory, used to keep track of KPrivateMemory instances
|
||||
std::map<memory::Region, std::shared_ptr<KPrivateMemory>> memoryRegionMap; //!< A mapping from every MemoryRegion to a shared pointer of it's corresponding KPrivateMemory
|
||||
std::map<handle_t, std::shared_ptr<KObject>> handleTable; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object
|
||||
std::map<pid_t, std::shared_ptr<KThread>> threadMap; //!< A mapping from a PID to it's corresponding KThread object
|
||||
std::unordered_map<u64, std::shared_ptr<KPrivateMemory>> memoryMap; //!< A mapping from every address to a shared pointer of it's corresponding KPrivateMemory, used to keep track of KPrivateMemory instances
|
||||
std::unordered_map<memory::Region, std::shared_ptr<KPrivateMemory>> memoryRegionMap; //!< A mapping from every MemoryRegion to a shared pointer of it's corresponding KPrivateMemory
|
||||
std::unordered_map<handle_t, std::shared_ptr<KObject>> handleTable; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object
|
||||
std::unordered_map<pid_t, std::shared_ptr<KThread>> threadMap; //!< A mapping from a PID to it's corresponding KThread object
|
||||
std::unordered_map<u64, std::vector<std::shared_ptr<KThread>>> mutexMap; //!< A map from a mutex's address to a vector of threads waiting on it (Sorted by priority)
|
||||
std::unordered_map<u64, std::vector<std::shared_ptr<KThread>>> condVarMap; //!< A map from a conditional variable's address to a vector of threads waiting on it (Sorted by priority)
|
||||
std::vector<std::shared_ptr<TlsPage>> tlsPages; //!< A vector of all allocated TLS pages
|
||||
|
||||
/**
|
||||
@ -223,5 +229,17 @@ namespace skyline::kernel::type {
|
||||
throw exception(fmt::format("GetHandle was called with invalid handle: 0x{:X}", handle));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This locks the Mutex at the specified address
|
||||
* @param address The address of the mutex
|
||||
*/
|
||||
void MutexLock(u64 address);
|
||||
|
||||
/**
|
||||
* @brief This unlocks the Mutex at the specified address
|
||||
* @param address The address of the mutex
|
||||
*/
|
||||
void MutexUnlock(u64 address);
|
||||
};
|
||||
}
|
||||
|
@ -13,10 +13,18 @@ namespace skyline::kernel::type {
|
||||
u64 entryArg; //!< An argument to pass to the process on entry
|
||||
|
||||
public:
|
||||
enum class ThreadStatus { Created, Running, Sleeping, Waiting, Runnable } status = ThreadStatus::Created; //!< The state of the thread
|
||||
handle_t handle; // The handle of the object in the handle table
|
||||
enum class ThreadStatus {
|
||||
Created, //!< The thread has been created but has not been started yet
|
||||
Running, //!< The thread is running currently
|
||||
Sleeping, //!< The thread is sleeping due to svcSleepThread
|
||||
WaitSync, //!< The thread is waiting for a KSyncObject signal
|
||||
WaitMutex, //!< The thread is waiting on a Mutex
|
||||
WaitCondVar, //!< The thread is waiting on a Conditional Variable
|
||||
Runnable //!< The thread is ready to run after waiting
|
||||
} status = ThreadStatus::Created; //!< The state of the thread
|
||||
std::vector<std::shared_ptr<KSyncObject>> waitObjects; //!< A vector holding handles this thread is waiting for
|
||||
u64 timeoutEnd{}; //!< The time when a svcWaitSynchronization
|
||||
u64 timeout{}; //!< The end of a timeout for svcWaitSynchronization or the end of the sleep period for svcSleepThread
|
||||
handle_t handle; // The handle of the object in the handle table
|
||||
pid_t pid; //!< The PID of the current thread (As in kernel PID and not PGID [In short, Linux implements threads as processes that share a lot of stuff at the kernel level])
|
||||
u64 stackTop; //!< The top of the stack (Where it starts growing downwards from)
|
||||
u64 tls; //!< The address of TLS (Thread Local Storage) slot assigned to the current thread
|
||||
|
@ -72,9 +72,9 @@ namespace skyline {
|
||||
state->os->KillThread(currPid);
|
||||
}
|
||||
}
|
||||
} else if (state->thisThread->status == kernel::type::KThread::ThreadStatus::Waiting || state->thisThread->status == kernel::type::KThread::ThreadStatus::Sleeping) {
|
||||
if (state->thisThread->timeoutEnd >= std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count()) {
|
||||
if (state->thisThread->status == kernel::type::KThread::ThreadStatus::Waiting)
|
||||
} else if (state->thisThread->status == kernel::type::KThread::ThreadStatus::WaitSync || state->thisThread->status == kernel::type::KThread::ThreadStatus::Sleeping || state->thisThread->status == kernel::type::KThread::ThreadStatus::WaitCondVar) {
|
||||
if (state->thisThread->timeout >= GetCurrTimeNs()) {
|
||||
if (state->thisThread->status == kernel::type::KThread::ThreadStatus::WaitSync || state->thisThread->status == kernel::type::KThread::ThreadStatus::WaitCondVar)
|
||||
SetRegister(Wreg::W0, constant::status::Timeout);
|
||||
state->thisThread->status = kernel::type::KThread::ThreadStatus::Runnable;
|
||||
}
|
||||
@ -166,7 +166,7 @@ namespace skyline {
|
||||
registerMap.at(pid ? pid : currPid).regs[static_cast<uint>(regId)] = value;
|
||||
}
|
||||
|
||||
u64 NCE::GetRegister(Wreg regId, pid_t pid) {
|
||||
u32 NCE::GetRegister(Wreg regId, pid_t pid) {
|
||||
return (reinterpret_cast<u32 *>(®isterMap.at(pid ? pid : currPid).regs))[static_cast<uint>(regId) * 2];
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ namespace skyline {
|
||||
* @param pid The PID of the process (Defaults to currPid)
|
||||
* @return The value in the register
|
||||
*/
|
||||
u64 GetRegister(Wreg regId, pid_t pid = 0);
|
||||
u32 GetRegister(Wreg regId, pid_t pid = 0);
|
||||
|
||||
/**
|
||||
* @brief Set the value of a Wn register
|
||||
|
Loading…
Reference in New Issue
Block a user