diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index cbb55bd4..4480a10b 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -55,6 +55,7 @@ namespace skyline { constexpr u8 DefaultPriority = 31; //!< The default priority of a process constexpr std::pair PriorityAn = {19, -8}; //!< The range of priority for Android, taken from https://medium.com/mindorks/exploring-android-thread-priority-5d0542eebbd1 constexpr std::pair 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); }; + /** + * @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::high_resolution_clock::now().time_since_epoch()).count(); + } + // Predeclare some classes here as we use them in DeviceState class NCE; namespace kernel { diff --git a/app/src/main/cpp/skyline/kernel/services/am/appletOE.h b/app/src/main/cpp/skyline/kernel/services/am/appletOE.h index 110b6527..3d229cd5 100644 --- a/app/src/main/cpp/skyline/kernel/services/am/appletOE.h +++ b/app/src/main/cpp/skyline/kernel/services/am/appletOE.h @@ -75,11 +75,13 @@ namespace skyline::kernel::service::am { std::shared_ptr 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); diff --git a/app/src/main/cpp/skyline/kernel/services/base_service.h b/app/src/main/cpp/skyline/kernel/services/base_service.h index 849603dc..7d072acf 100644 --- a/app/src/main/cpp/skyline/kernel/services/base_service.h +++ b/app/src/main/cpp/skyline/kernel/services/base_service.h @@ -69,7 +69,6 @@ namespace skyline::kernel::service { public: Service serviceType; //!< Which service this is - uint numSessions{}; // 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(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(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(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(request.cmdArg); + styleSet = *reinterpret_cast(&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(styleSet->pro_controller), static_cast(styleSet->joycon_handheld), static_cast(styleSet->joycon_dual), static_cast(styleSet->joycon_left), static_cast + (styleSet->joycon_right), static_cast(styleSet->gamecube), static_cast(styleSet->pokeball), static_cast(styleSet->nes), static_cast(styleSet->nes_handheld), static_cast(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(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(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(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(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(request.cmdArg); + deviceMap[input->controllerType].assignment = JoyConAssignment::Dual; + } } diff --git a/app/src/main/cpp/skyline/kernel/services/hid/hid.h b/app/src/main/cpp/skyline/kernel/services/hid/hid.h index a14e3836..69aa6101 100644 --- a/app/src/main/cpp/skyline/kernel/services/hid/hid.h +++ b/app/src/main/cpp/skyline/kernel/services/hid/hid.h @@ -29,7 +29,87 @@ namespace skyline::kernel::service::hid { */ class hid : public BaseService { private: - std::shared_ptr 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 resource{}; //!< A shared pointer to the applet resource + std::optional styleSet; //!< The controller styles supported by the application + std::unordered_map 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); }; } diff --git a/app/src/main/cpp/skyline/kernel/services/serviceman.cpp b/app/src/main/cpp/skyline/kernel/services/serviceman.cpp index 8e831e02..2c8b3783 100644 --- a/app/src/main/cpp/skyline/kernel/services/serviceman.cpp +++ b/app/src/main/cpp/skyline/kernel/services/serviceman.cpp @@ -12,64 +12,60 @@ namespace skyline::kernel::service { std::shared_ptr ServiceManager::GetService(const Service serviceType) { std::shared_ptr serviceObj; - if (serviceMap.find(serviceType) == serviceMap.end()) { - switch (serviceType) { + switch (serviceType) { case Service::sm: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::fatal_u: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::set_sys: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::apm: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::apm_ISession: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::am_appletOE: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::am_IApplicationProxy: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::am_ICommonStateGetter: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::am_IWindowController: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::am_IAudioController: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::am_IDisplayController: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::am_ISelfController: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::am_ILibraryAppletCreator: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::am_IApplicationFunctions: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::am_IDebugFunctions: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::hid: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(state, *this); break; case Service::hid_IAppletResource: - serviceMap[serviceType] = std::make_shared(state, *this); + serviceObj = std::make_shared(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(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(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; } diff --git a/app/src/main/cpp/skyline/kernel/services/serviceman.h b/app/src/main/cpp/skyline/kernel/services/serviceman.h index bbca0498..5c33e17f 100644 --- a/app/src/main/cpp/skyline/kernel/services/serviceman.h +++ b/app/src/main/cpp/skyline/kernel/services/serviceman.h @@ -11,8 +11,12 @@ namespace skyline::kernel::service { class ServiceManager { private: const DeviceState &state; //!< The state of the device - std::unordered_map> serviceMap; //!< A map from it's type to a BaseService object + std::vector> 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 GetService(const Service serviceType); public: diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index 35f9da79..80e2d831 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -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::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::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(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); } diff --git a/app/src/main/cpp/skyline/kernel/svc.h b/app/src/main/cpp/skyline/kernel/svc.h index 66152ea7..a80c0812 100644 --- a/app/src/main/cpp/skyline/kernel/svc.h +++ b/app/src/main/cpp/skyline/kernel/svc.h @@ -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 diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp index 2139ae18..2d28b975 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp @@ -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(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(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); + } } diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h index e5dc7ff9..da367190 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.h +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h @@ -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> memoryMap; //!< A mapping from every address to a shared pointer of it's corresponding KPrivateMemory, used to keep track of KPrivateMemory instances - std::map> memoryRegionMap; //!< A mapping from every MemoryRegion to a shared pointer of it's corresponding KPrivateMemory - std::map> handleTable; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object - std::map> threadMap; //!< A mapping from a PID to it's corresponding KThread object + std::unordered_map> 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> memoryRegionMap; //!< A mapping from every MemoryRegion to a shared pointer of it's corresponding KPrivateMemory + std::unordered_map> handleTable; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object + std::unordered_map> threadMap; //!< A mapping from a PID to it's corresponding KThread object + std::unordered_map>> mutexMap; //!< A map from a mutex's address to a vector of threads waiting on it (Sorted by priority) + std::unordered_map>> condVarMap; //!< A map from a conditional variable's address to a vector of threads waiting on it (Sorted by priority) std::vector> 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); }; } diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.h b/app/src/main/cpp/skyline/kernel/types/KThread.h index 35c8eb5e..ff191f97 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.h +++ b/app/src/main/cpp/skyline/kernel/types/KThread.h @@ -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> 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 diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp index 018a4a27..e6aac67f 100644 --- a/app/src/main/cpp/skyline/nce.cpp +++ b/app/src/main/cpp/skyline/nce.cpp @@ -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::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(regId)] = value; } - u64 NCE::GetRegister(Wreg regId, pid_t pid) { + u32 NCE::GetRegister(Wreg regId, pid_t pid) { return (reinterpret_cast(®isterMap.at(pid ? pid : currPid).regs))[static_cast(regId) * 2]; } diff --git a/app/src/main/cpp/skyline/nce.h b/app/src/main/cpp/skyline/nce.h index f0d381b0..0aaccf57 100644 --- a/app/src/main/cpp/skyline/nce.h +++ b/app/src/main/cpp/skyline/nce.h @@ -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