diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 2f8c5ff7..6090aa24 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -11,7 +11,7 @@ add_subdirectory("libraries/fmt") set(source_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp) -include_directories(${source_DIR}) +include_directories(${source_DIR}/skyline) add_library(skyline SHARED ${source_DIR}/main.cpp @@ -25,13 +25,15 @@ add_library(skyline SHARED ${source_DIR}/skyline/kernel/types/KProcess.cpp ${source_DIR}/skyline/kernel/types/KThread.cpp ${source_DIR}/skyline/kernel/types/KSharedMemory.cpp + ${source_DIR}/skyline/kernel/types/KTransferMemory.cpp ${source_DIR}/skyline/kernel/types/KPrivateMemory.cpp ${source_DIR}/skyline/kernel/services/serviceman.cpp ${source_DIR}/skyline/kernel/services/sm/sm.cpp + ${source_DIR}/skyline/kernel/services/fatal/fatal.cpp ${source_DIR}/skyline/kernel/services/set/sys.cpp ${source_DIR}/skyline/kernel/services/apm/apm.cpp ${source_DIR}/skyline/kernel/services/am/appletOE.cpp - ${source_DIR}/skyline/kernel/services/fatal/fatal.cpp + ${source_DIR}/skyline/kernel/services/hid/hid.cpp ) target_link_libraries(skyline fmt tinyxml2) target_compile_options(skyline PRIVATE -Wno-c++17-extensions) diff --git a/app/src/main/cpp/skyline/common.cpp b/app/src/main/cpp/skyline/common.cpp index e406774f..5388004f 100644 --- a/app/src/main/cpp/skyline/common.cpp +++ b/app/src/main/cpp/skyline/common.cpp @@ -1,6 +1,5 @@ #include "common.h" #include -#include namespace skyline { Settings::Settings(const std::string &prefXml) { @@ -34,7 +33,7 @@ namespace skyline { return boolMap.at(key); } - void Settings::List(std::shared_ptr& logger) { + void Settings::List(std::shared_ptr logger) { for (auto& iter : stringMap) logger->Write(Logger::Info, "Key: {}, Value: {}, Type: String", iter.first, GetString(iter.first)); for (auto& iter : boolMap) @@ -56,7 +55,7 @@ namespace skyline { logFile.flush(); } - void Logger::Write(const LogLevel level, const std::string &str) { + void Logger::Write(const LogLevel level, const std::string& str) { #ifdef NDEBUG if (level == Debug) return; #endif diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index 904f879e..cbb55bd4 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -35,6 +35,7 @@ namespace skyline { constexpr u64 BaseAddr = 0x8000000; //!< The address space base constexpr u64 MapAddr = BaseAddr + 0x80000000; //!< The address of the map region constexpr u64 BaseSize = 0x7FF8000000; //!< The size of the address space + constexpr u64 BaseEnd = BaseAddr + BaseSize; //!< The end of the address space constexpr u64 MapSize = 0x1000000000; //!< The size of the map region constexpr u64 TotalPhyMem = 0xF8000000; // ~4 GB of RAM constexpr size_t DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB @@ -231,7 +232,7 @@ namespace skyline { /** * @brief Writes all settings keys and values to syslog. This function is for development purposes. */ - void List(std::shared_ptr &logger); + void List(std::shared_ptr logger); }; // Predeclare some classes here as we use them in DeviceState diff --git a/app/src/main/cpp/skyline/kernel/ipc.cpp b/app/src/main/cpp/skyline/kernel/ipc.cpp index ff7038e2..170e5021 100644 --- a/app/src/main/cpp/skyline/kernel/ipc.cpp +++ b/app/src/main/cpp/skyline/kernel/ipc.cpp @@ -1,5 +1,3 @@ -#include -#include #include "ipc.h" #include "types/KProcess.h" @@ -13,7 +11,7 @@ namespace skyline::kernel::ipc { if (header->handle_desc) { handleDesc = reinterpret_cast(currPtr); - currPtr += sizeof(HandleDescriptor) + (handleDesc->send_pid ? sizeof(u8) : 0); + currPtr += sizeof(HandleDescriptor) + (handleDesc->send_pid ? sizeof(u64) : 0); for (uint index = 0; handleDesc->copy_count > index; index++) { copyHandles.push_back(*reinterpret_cast(currPtr)); currPtr += sizeof(handle_t); @@ -143,6 +141,8 @@ namespace skyline::kernel::ipc { } } + state.logger->Write(Logger::Debug, "Output: Raw Size: {}, Command ID: 0x{:X}, Copy Handles: {}, Move Handles: {}", u32(header->raw_sz), u32(payload->value), copyHandles.size(), moveHandles.size()); + state.thisProcess->WriteMemory(tls.data(), state.thisThread->tls, constant::TlsIpcSize); } } diff --git a/app/src/main/cpp/skyline/kernel/ipc.h b/app/src/main/cpp/skyline/kernel/ipc.h index 21058404..f3ea71f4 100644 --- a/app/src/main/cpp/skyline/kernel/ipc.h +++ b/app/src/main/cpp/skyline/kernel/ipc.h @@ -1,9 +1,7 @@ #pragma once -#include -#include #include -#include "../common.h" +#include namespace skyline::kernel::ipc { /** @@ -108,7 +106,6 @@ namespace skyline::kernel::ipc { u32 address_0_31 : 32; BufferDescriptorX(u64 address, u16 counter, u16 size) : size(size) { - // TODO: Test this, the AND mask might be the other way around address_0_31 = static_cast(address & 0x7FFFFFFF80000000); address_32_35 = static_cast(address & 0x78000000); address_36_38 = static_cast(address & 0x7000000); diff --git a/app/src/main/cpp/skyline/kernel/services/am/appletOE.cpp b/app/src/main/cpp/skyline/kernel/services/am/appletOE.cpp index 92ae7f70..f97f9324 100644 --- a/app/src/main/cpp/skyline/kernel/services/am/appletOE.cpp +++ b/app/src/main/cpp/skyline/kernel/services/am/appletOE.cpp @@ -63,8 +63,9 @@ namespace skyline::kernel::service::am { } void ICommonStateGetter::GetEventHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - messageEvent = state.thisProcess->NewHandle(); - response.copyHandles.push_back(messageEvent->handle); + auto event = state.thisProcess->NewHandle(); + messageEvent = event.item; + response.copyHandles.push_back(event.handle); } void ICommonStateGetter::GetCurrentFocusState(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { 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 adbf6830..110b6527 100644 --- a/app/src/main/cpp/skyline/kernel/services/am/appletOE.h +++ b/app/src/main/cpp/skyline/kernel/services/am/appletOE.h @@ -1,9 +1,9 @@ #pragma once -#include "../base_service.h" -#include "../serviceman.h" -#include "../../types/KProcess.h" -#include "../../types/KEvent.h" +#include +#include +#include +#include namespace skyline::kernel::service::am { /** diff --git a/app/src/main/cpp/skyline/kernel/services/apm/apm.h b/app/src/main/cpp/skyline/kernel/services/apm/apm.h index e6aa609a..df6786bf 100644 --- a/app/src/main/cpp/skyline/kernel/services/apm/apm.h +++ b/app/src/main/cpp/skyline/kernel/services/apm/apm.h @@ -1,7 +1,7 @@ #pragma once -#include "../base_service.h" -#include "../serviceman.h" +#include +#include namespace skyline::kernel::service::apm { /** 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 0799296c..849603dc 100644 --- a/app/src/main/cpp/skyline/kernel/services/base_service.h +++ b/app/src/main/cpp/skyline/kernel/services/base_service.h @@ -1,7 +1,7 @@ #pragma once -#include "../../common.h" -#include "../ipc.h" +#include +#include #include #define SFunc(function) std::bind(&function, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) @@ -29,6 +29,8 @@ namespace skyline::kernel::service { am_IDisplayController, am_ILibraryAppletCreator, am_IDebugFunctions, + hid, + hid_IAppletResource, }; /** @@ -50,6 +52,8 @@ namespace skyline::kernel::service { {"am:ILibraryAppletCreator", Service::am_ILibraryAppletCreator}, {"am:IApplicationFunctions", Service::am_IApplicationFunctions}, {"am:IDebugFunctions", Service::am_IDebugFunctions}, + {"hid", Service::hid}, + {"hid:IAppletResource", Service::hid_IAppletResource}, }; class ServiceManager; diff --git a/app/src/main/cpp/skyline/kernel/services/fatal/fatal.cpp b/app/src/main/cpp/skyline/kernel/services/fatal/fatal.cpp index 5d1b3ca4..e5fdd6db 100644 --- a/app/src/main/cpp/skyline/kernel/services/fatal/fatal.cpp +++ b/app/src/main/cpp/skyline/kernel/services/fatal/fatal.cpp @@ -2,10 +2,12 @@ namespace skyline::kernel::service::fatal { fatalU::fatalU(const DeviceState &state, ServiceManager& manager) : BaseService(state, manager, false, Service::fatal_u, { - {0x0, SFunc(fatalU::ThrowFatal)} + {0x0, SFunc(fatalU::ThrowFatal)}, + {0x1, SFunc(fatalU::ThrowFatal)}, + {0x2, SFunc(fatalU::ThrowFatal)} }) {} void fatalU::ThrowFatal(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - throw exception("A fatal error has caused emulation to stop"); + throw exception(fmt::format("A fatal error with code: 0x{:X} has caused emulation to stop", *reinterpret_cast(request.cmdArg))); } } diff --git a/app/src/main/cpp/skyline/kernel/services/fatal/fatal.h b/app/src/main/cpp/skyline/kernel/services/fatal/fatal.h index 2af9ae0c..eec6afa4 100644 --- a/app/src/main/cpp/skyline/kernel/services/fatal/fatal.h +++ b/app/src/main/cpp/skyline/kernel/services/fatal/fatal.h @@ -1,7 +1,7 @@ #pragma once -#include "../base_service.h" -#include "../serviceman.h" +#include +#include namespace skyline::kernel::service::fatal { /** diff --git a/app/src/main/cpp/skyline/kernel/services/hid/hid.cpp b/app/src/main/cpp/skyline/kernel/services/hid/hid.cpp new file mode 100644 index 00000000..b895bae5 --- /dev/null +++ b/app/src/main/cpp/skyline/kernel/services/hid/hid.cpp @@ -0,0 +1,21 @@ +#include "hid.h" +#include + +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)} + }) {} + + void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + 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)); + } +} diff --git a/app/src/main/cpp/skyline/kernel/services/hid/hid.h b/app/src/main/cpp/skyline/kernel/services/hid/hid.h new file mode 100644 index 00000000..a14e3836 --- /dev/null +++ b/app/src/main/cpp/skyline/kernel/services/hid/hid.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +namespace skyline::constant { + constexpr size_t hidSharedMemSize = 0x40000; //!< The size of HID Shared Memory (https://switchbrew.org/wiki/HID_Shared_Memory) +} + +namespace skyline::kernel::service::hid { + /** + * @brief IAppletResource is used to get the handle to the HID shared memory (https://switchbrew.org/wiki/HID_services#IAppletResource) + */ + class IAppletResource : public BaseService { + public: + IAppletResource(const DeviceState &state, ServiceManager& manager); + + std::shared_ptr hidSharedMemory; + + /** + * @brief This opens a handle to HID shared memory (https://switchbrew.org/wiki/HID_services#GetSharedMemoryHandle) + */ + void GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; + + /** + * @brief hid or Human Interface Device service is used to access input devices (https://switchbrew.org/wiki/HID_services#hid) + */ + class hid : public BaseService { + private: + std::shared_ptr resource{}; + public: + hid(const DeviceState &state, ServiceManager& manager); + + /** + * @brief This returns an IAppletResource (https://switchbrew.org/wiki/HID_services#CreateAppletResource) + */ + void CreateAppletResource(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 e4cfe3df..8e831e02 100644 --- a/app/src/main/cpp/skyline/kernel/services/serviceman.cpp +++ b/app/src/main/cpp/skyline/kernel/services/serviceman.cpp @@ -1,10 +1,11 @@ #include "serviceman.h" -#include "../types/KProcess.h" +#include #include "sm/sm.h" #include "set/sys.h" #include "apm/apm.h" #include "am/appletOE.h" #include "fatal/fatal.h" +#include "hid/hid.h" namespace skyline::kernel::service { ServiceManager::ServiceManager(const DeviceState &state) : state(state) {} @@ -58,6 +59,12 @@ namespace skyline::kernel::service { case Service::am_IDebugFunctions: serviceMap[serviceType] = std::make_shared(state, *this); break; + case Service::hid: + serviceMap[serviceType] = std::make_shared(state, *this); + break; + case Service::hid_IAppletResource: + serviceMap[serviceType] = std::make_shared(state, *this); + break; } serviceObj = serviceMap[serviceType]; } else @@ -67,17 +74,18 @@ namespace skyline::kernel::service { } handle_t ServiceManager::NewSession(const Service serviceType) { - return state.thisProcess->NewHandle(GetService(serviceType), serviceType)->handle; + return state.thisProcess->NewHandle(GetService(serviceType), serviceType).handle; } - void ServiceManager::NewService(const Service serviceType, type::KSession &session, ipc::IpcResponse &response) { + std::shared_ptr ServiceManager::NewService(const Service serviceType, type::KSession &session, ipc::IpcResponse &response) { auto serviceObject = GetService(serviceType); if (response.isDomain) { session.domainTable[++session.handleIndex] = serviceObject; response.domainObjects.push_back(session.handleIndex); } else - response.moveHandles.push_back(state.thisProcess->NewHandle(serviceObject, serviceType)->handle); + response.moveHandles.push_back(state.thisProcess->NewHandle(serviceObject, serviceType).handle); state.logger->Write(Logger::Debug, "Service has been registered: \"{}\"", serviceObject->getName()); + return serviceObject; } void ServiceManager::CloseSession(const handle_t handle) { diff --git a/app/src/main/cpp/skyline/kernel/services/serviceman.h b/app/src/main/cpp/skyline/kernel/services/serviceman.h index 7e6efcf7..bbca0498 100644 --- a/app/src/main/cpp/skyline/kernel/services/serviceman.h +++ b/app/src/main/cpp/skyline/kernel/services/serviceman.h @@ -1,8 +1,8 @@ #pragma once -#include "../../nce.h" +#include +#include #include "base_service.h" -#include "../types/KSession.h" namespace skyline::kernel::service { /** @@ -34,7 +34,7 @@ namespace skyline::kernel::service { * @param session The session object of the command * @param response The response object to write the handle or virtual handle to */ - void NewService(const Service serviceType, type::KSession &session, ipc::IpcResponse &response); + std::shared_ptr NewService(const Service serviceType, type::KSession &session, ipc::IpcResponse &response); /** * @brief Closes an existing session to a service diff --git a/app/src/main/cpp/skyline/kernel/services/set/sys.cpp b/app/src/main/cpp/skyline/kernel/services/set/sys.cpp index 49160492..7293f3a8 100644 --- a/app/src/main/cpp/skyline/kernel/services/set/sys.cpp +++ b/app/src/main/cpp/skyline/kernel/services/set/sys.cpp @@ -1,5 +1,5 @@ #include "sys.h" -#include "../../types/KProcess.h" +#include namespace skyline::kernel::service::set { sys::sys(const DeviceState &state, ServiceManager& manager) : BaseService(state, manager, false, Service::set_sys, {{0x3, SFunc(sys::GetFirmwareVersion)}}) {} diff --git a/app/src/main/cpp/skyline/kernel/services/set/sys.h b/app/src/main/cpp/skyline/kernel/services/set/sys.h index 868f9410..82c99aaf 100644 --- a/app/src/main/cpp/skyline/kernel/services/set/sys.h +++ b/app/src/main/cpp/skyline/kernel/services/set/sys.h @@ -1,7 +1,7 @@ #pragma once -#include "../base_service.h" -#include "../serviceman.h" +#include +#include namespace skyline::kernel::service::set { /** diff --git a/app/src/main/cpp/skyline/kernel/services/sm/sm.h b/app/src/main/cpp/skyline/kernel/services/sm/sm.h index cb8ba6be..9a089373 100644 --- a/app/src/main/cpp/skyline/kernel/services/sm/sm.h +++ b/app/src/main/cpp/skyline/kernel/services/sm/sm.h @@ -1,7 +1,7 @@ #pragma once -#include "../base_service.h" -#include "../serviceman.h" +#include +#include namespace skyline::kernel::service::sm { /** diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index 6e381b06..35f9da79 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -1,50 +1,47 @@ -#include -#include -#include -#include #include "svc.h" -#include "../os.h" +#include namespace skyline::kernel::svc { void SetHeapSize(DeviceState &state) { auto heap = state.thisProcess->MapPrivateRegion(0, state.nce->GetRegister(Wreg::W1), {true, true, false}, memory::Type::Heap, memory::Region::Heap); state.nce->SetRegister(Wreg::W0, constant::status::Success); - state.nce->SetRegister(Xreg::X1, heap->address); + state.nce->SetRegister(Xreg::X1, heap.item->address); state.logger->Write(Logger::Debug, "Heap size was set to 0x{:X}", state.nce->GetRegister(Wreg::W1)); } void QueryMemory(DeviceState &state) { memory::MemoryInfo memInf; - bool found{}; u64 addr = state.nce->GetRegister(Xreg::X2); - for(auto& [region, sharedMem] : state.nce->memoryRegionMap) { - if (addr == sharedMem->address) { - memInf = sharedMem->GetInfo(state.thisProcess->mainThread); - found = true; + bool memFree = true; + for(const auto& [address, region] : state.thisProcess->memoryMap) { + if (addr >= address && addr < (address + region->size)) { + memInf = region->GetInfo(); + memFree = false; + break; } } - if(!found) { - if (state.thisProcess->memoryMap.count(addr)) - memInf = state.thisProcess->memoryMap.at(addr)->GetInfo(); - else { - state.nce->SetRegister(Wreg::W0, constant::status::InvAddress); - return; - } + if (memFree) { + memInf = { + .baseAddress = static_cast(static_cast(addr / PAGE_SIZE) * PAGE_SIZE), + .size = static_cast(-constant::BaseSize + 1), + .type = static_cast(memory::Type::Unmapped), + }; } state.thisProcess->WriteMemory(memInf, state.nce->GetRegister(Xreg::X0)); state.nce->SetRegister(Wreg::W0, constant::status::Success); } void CreateThread(DeviceState &state) { - // TODO: Check if the values supplied by the process are actually valid & Support Core Mask potentially ? + // TODO: Support Core Mask potentially auto thread = state.thisProcess->CreateThread(state.nce->GetRegister(Xreg::X1), state.nce->GetRegister(Xreg::X2), state.nce->GetRegister(Xreg::X3), static_cast(state.nce->GetRegister(Wreg::W4))); state.nce->SetRegister(Wreg::W0, constant::status::Success); state.nce->SetRegister(Wreg::W1, thread->handle); + state.logger->Write(Logger::Info, "Creating a thread: {}", thread->handle); } void StartThread(DeviceState &state) { auto &object = state.thisProcess->handleTable.at(static_cast(state.nce->GetRegister(Wreg::W0))); - if (object->handleType == type::KType::KThread) + if (object->objectType == type::KType::KThread) std::static_pointer_cast(object)->Start(); else throw exception("StartThread was called on a non-KThread object"); @@ -54,6 +51,19 @@ namespace skyline::kernel::svc { state.os->KillThread(state.thisThread->pid); } + void SleepThread(DeviceState &state) { + auto in = state.nce->GetRegister(Xreg::X0); + switch(in) { + case 0: + case 1: + 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->status = type::KThread::ThreadStatus::Sleeping; + } + } + void GetThreadPriority(DeviceState &state) { state.nce->SetRegister(Wreg::W1, state.thisProcess->GetHandle(static_cast(state.nce->GetRegister(Wreg::W0)))->priority); state.nce->SetRegister(Wreg::W0, constant::status::Success); @@ -64,11 +74,17 @@ namespace skyline::kernel::svc { state.nce->SetRegister(Wreg::W0, constant::status::Success); } + void MapSharedMemory(DeviceState &state) { + auto object = state.thisProcess->GetHandle(static_cast(state.nce->GetRegister(Wreg::W0))); + object->Map(state.nce->GetRegister(Xreg::X1), state.nce->GetRegister(Xreg::X2), state.thisProcess->mainThread); + state.nce->SetRegister(Wreg::W0, constant::status::Success); + } + void CloseHandle(DeviceState &state) { auto handle = static_cast(state.nce->GetRegister(Wreg::W0)); state.logger->Write(Logger::Debug, "Closing handle: 0x{:X}", handle); auto &object = state.thisProcess->handleTable.at(handle); - switch (object->handleType) { + switch (object->objectType) { case (type::KType::KThread): state.os->KillThread(std::static_pointer_cast(object)->pid); break; @@ -88,8 +104,28 @@ namespace skyline::kernel::svc { state.nce->SetRegister(Wreg::W0, constant::status::MaxHandles); return; } - state.thisThread->waitHandles.reserve(numHandles); - state.thisProcess->ReadMemory(state.thisThread->waitHandles.data(), state.nce->GetRegister(Xreg::X1), numHandles * sizeof(handle_t)); + std::vector waitHandles(numHandles); + state.thisProcess->ReadMemory(waitHandles.data(), state.nce->GetRegister(Xreg::X1), numHandles * sizeof(handle_t)); + for (const auto& handle : waitHandles) { + auto object = state.thisProcess->handleTable.at(handle); + switch(object->objectType) { + case type::KType::KProcess: + case type::KType::KThread: + case type::KType::KEvent: + case type::KType::KSession: + break; + default: + state.nce->SetRegister(Wreg::W0, constant::status::InvHandle); + return; + } + auto syncObject = std::static_pointer_cast(object); + if(syncObject->signalled) { + state.nce->SetRegister(Wreg::W0, constant::status::Success); + return; + } + state.thisThread->waitObjects.push_back(syncObject); + syncObject->waitThreads.push_back(state.thisThread->pid); + } state.thisThread->status = type::KThread::ThreadStatus::Waiting; } @@ -111,7 +147,10 @@ namespace skyline::kernel::svc { void OutputDebugString(DeviceState &state) { std::string debug(state.nce->GetRegister(Xreg::X1), '\0'); state.os->thisProcess->ReadMemory((void *) debug.data(), state.nce->GetRegister(Xreg::X0), state.nce->GetRegister(Xreg::X1)); - state.logger->Write(Logger::Info, "svcOutputDebugString: {}", debug.c_str()); + 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.nce->SetRegister(Wreg::W0, 0); } @@ -141,7 +180,7 @@ namespace skyline::kernel::svc { state.nce->SetRegister(Xreg::X1, constant::TotalPhyMem); break; case constant::infoState::TotalMemoryUsage: - state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address + state.thisProcess->mainThreadStackSz + state.nce->GetSharedSize()); + state.nce->SetRegister(Xreg::X1, state.os->thisProcess->memoryRegionMap.at(memory::Region::Heap)->address + state.thisProcess->mainThreadStackSz + state.thisProcess->GetProgramSize()); break; case constant::infoState::AddressSpaceBaseAddr: state.nce->SetRegister(Xreg::X1, constant::BaseAddr); diff --git a/app/src/main/cpp/skyline/kernel/svc.h b/app/src/main/cpp/skyline/kernel/svc.h index db6a72ea..66152ea7 100644 --- a/app/src/main/cpp/skyline/kernel/svc.h +++ b/app/src/main/cpp/skyline/kernel/svc.h @@ -1,7 +1,7 @@ #pragma once #include "ipc.h" -#include "../common.h" +#include namespace skyline { namespace constant::infoState { @@ -66,6 +66,11 @@ namespace skyline { */ void ExitThread(DeviceState &state); + /** + * @brief Sleep for a specified amount of time, or yield thread (https://switchbrew.org/wiki/SVC#svcExitThread) + */ + void SleepThread(DeviceState &state); + /** * @brief Get priority of provided thread handle (https://switchbrew.org/wiki/SVC#svcGetThreadPriority) */ @@ -76,6 +81,11 @@ namespace skyline { */ void SetThreadPriority(DeviceState &state); + /** + * @brief Maps the block supplied by the handle (https://switchbrew.org/wiki/SVC#svcMapSharedMemory) + */ + void MapSharedMemory(DeviceState &state); + /** * @brief Closes the specified handle */ @@ -121,7 +131,7 @@ namespace skyline { CreateThread, // 0x08 StartThread, // 0x09 ExitThread, // 0x0a - nullptr, // 0x0b + SleepThread, // 0x0b GetThreadPriority, // 0x0c SetThreadPriority, // 0x0d nullptr, // 0x0e @@ -129,7 +139,7 @@ namespace skyline { nullptr, // 0x10 nullptr, // 0x11 nullptr, // 0x12 - nullptr, // 0x13 + MapSharedMemory, // 0x13 nullptr, // 0x14 nullptr, // 0x15 CloseHandle, // 0x16 diff --git a/app/src/main/cpp/skyline/kernel/types/KEvent.h b/app/src/main/cpp/skyline/kernel/types/KEvent.h index 9f73af9a..9eb7cf30 100644 --- a/app/src/main/cpp/skyline/kernel/types/KEvent.h +++ b/app/src/main/cpp/skyline/kernel/types/KEvent.h @@ -9,10 +9,23 @@ namespace skyline::kernel::type { class KEvent : public KSyncObject { public: /** - * @param handle The handle of the object in the handle table - * @param pid The PID of the main thread * @param state The state of the device */ - KEvent(skyline::handle_t handle, pid_t pid, const DeviceState &state) : KSyncObject(handle, pid, state, KType::KEvent) {} + KEvent(const DeviceState &state) : KSyncObject(state, KType::KEvent) {} + + /** + * @brief Signals all threads waiting on this object + */ + virtual inline void Signal() { + KSyncObject::Signal(); + signalled = true; + } + + /** + * @brief Resets the KEvent to an unsignalled state + */ + inline void ResetSignal() { + signalled = false; + } }; } diff --git a/app/src/main/cpp/skyline/kernel/types/KObject.h b/app/src/main/cpp/skyline/kernel/types/KObject.h index 38c5eeb7..6c067a17 100644 --- a/app/src/main/cpp/skyline/kernel/types/KObject.h +++ b/app/src/main/cpp/skyline/kernel/types/KObject.h @@ -1,13 +1,13 @@ #pragma once -#include "../../common.h" +#include namespace skyline::kernel::type { /** * @brief These types are used to perform runtime evaluation of a kernel object's type when converting from base class */ enum class KType { - KThread, KProcess, KSharedMemory, KPrivateMemory, KSession, KEvent + KThread, KProcess, KSharedMemory, KTransferMemory, KPrivateMemory, KSession, KEvent }; /** @@ -15,17 +15,13 @@ namespace skyline::kernel::type { */ class KObject { public: - handle_t handle; //!< The handle of this KObject - pid_t ownerPid; //!< The pid of the process owning this object const DeviceState &state; //!< The state of the device - KType handleType; //!< The type of this object + KType objectType; //!< The type of this object /** - * @param handle The handle of the object in the handle table - * @param ownerPid The PID of the process which owns this * @param state The state of the device - * @param handleType The type of the object + * @param objectType The type of the object */ - KObject(handle_t handle, pid_t ownerPid, const DeviceState &state, KType handleType) : handle(handle), ownerPid(ownerPid), state(state), handleType(handleType) {} + KObject(const DeviceState &state, KType objectType) : state(state), objectType(objectType) {} }; } diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp index 8185f192..7930b8b0 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp @@ -1,9 +1,5 @@ #include "KPrivateMemory.h" -#include "../../nce.h" -#include "../../os.h" -#include -#include -#include +#include namespace skyline::kernel::type { u64 MapPrivateFunc(u64 dstAddress, u64 srcAddress, size_t size, u64 perms) { @@ -15,23 +11,19 @@ namespace skyline::kernel::type { return dstAddress; } - KPrivateMemory::KPrivateMemory(handle_t handle, pid_t pid, const DeviceState &state, u64 dstAddress, u64 srcAddress, size_t size, memory::Permission permission, const memory::Type type) : state(state), address(dstAddress), size(size), permission(permission), type(type), KObject(handle, pid, state, KType::KPrivateMemory) { + KPrivateMemory::KPrivateMemory(const DeviceState &state, pid_t pid, u64 dstAddress, u64 srcAddress, size_t size, memory::Permission permission, const memory::Type type) : state(state), owner(pid), address(dstAddress), size(size), permission(permission), type(type), KObject(state, KType::KPrivateMemory) { user_pt_regs fregs = {0}; fregs.regs[0] = dstAddress; fregs.regs[1] = srcAddress; fregs.regs[2] = size; fregs.regs[3] = static_cast(permission.Get()); - state.nce->ExecuteFunction(reinterpret_cast(MapPrivateFunc), fregs, ownerPid); + state.nce->ExecuteFunction(reinterpret_cast(MapPrivateFunc), fregs, pid); if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) throw exception("An error occurred while mapping private region in child process"); if (!this->address) this->address = fregs.regs[0]; } - u64 UnmapPrivateFunc(u64 address, size_t size) { - return static_cast(munmap(reinterpret_cast(address), size)); - } - u64 RemapPrivateFunc(u64 address, size_t oldSize, size_t size) { return reinterpret_cast(mremap(reinterpret_cast(address), oldSize, size, 0)); } @@ -41,7 +33,7 @@ namespace skyline::kernel::type { fregs.regs[0] = address; fregs.regs[1] = size; fregs.regs[2] = newSize; - state.nce->ExecuteFunction(reinterpret_cast(RemapPrivateFunc), fregs, ownerPid); + state.nce->ExecuteFunction(reinterpret_cast(RemapPrivateFunc), fregs, owner); if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) throw exception("An error occurred while remapping private region in child process"); size = newSize; @@ -56,7 +48,7 @@ namespace skyline::kernel::type { fregs.regs[0] = address; fregs.regs[1] = size; fregs.regs[2] = static_cast(newPerms.Get()); - state.nce->ExecuteFunction(reinterpret_cast(UpdatePermissionPrivateFunc), fregs, ownerPid); + state.nce->ExecuteFunction(reinterpret_cast(UpdatePermissionPrivateFunc), fregs, owner); if (static_cast(fregs.regs[0]) == -1) throw exception("An error occurred while updating private region's permissions in child process"); permission = newPerms; @@ -74,4 +66,17 @@ namespace skyline::kernel::type { info.deviceRefCount = deviceRefCount; return info; } + + u64 UnmapPrivateFunc(u64 address, size_t size) { + return static_cast(munmap(reinterpret_cast(address), size)); + } + + KPrivateMemory::~KPrivateMemory() { + try { + user_pt_regs fregs = {0}; + fregs.regs[0] = address; + fregs.regs[1] = size; + state.nce->ExecuteFunction(reinterpret_cast(UnmapPrivateFunc), fregs, owner); + } catch (const std::exception&) {} + } }; diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h index a52d46c2..6aec07f8 100644 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h @@ -1,6 +1,6 @@ #pragma once -#include "../../memory.h" +#include #include "KObject.h" namespace skyline::kernel::type { @@ -12,6 +12,7 @@ namespace skyline::kernel::type { const DeviceState &state; //!< The state of the device public: + pid_t owner; //!< The PID of the process owning this memory u64 address; //!< The address of the allocated memory size_t size; //!< The size of the allocated memory u16 ipcRefCount{}; //!< The amount of reference to this memory for IPC @@ -21,21 +22,19 @@ namespace skyline::kernel::type { /** * @brief Constructor of a private memory object - * @param handle A handle to this object - * @param pid The PID of the main thread * @param state The state of the device + * @param pid The PID of the main * @param dstAddress The address to map to (If NULL then an arbitrary address is picked) * @param srcAddress The address to map from (If NULL then no copy is performed) * @param size The size of the allocation * @param permission The permissions for the allocated memory + * @param type The type of the memory */ - KPrivateMemory(handle_t handle, pid_t pid, const DeviceState &state, u64 dstAddress, u64 srcAddress, size_t size, memory::Permission permission, const memory::Type type); + KPrivateMemory(const DeviceState &state, pid_t pid, u64 dstAddress, u64 srcAddress, size_t size, memory::Permission permission, const memory::Type type); /** * @brief Remap a chunk of memory as to change the size occupied by it - * @param address The address of the mapped memory - * @param old_size The current size of the memory - * @param size The new size of the memory + * @param newSize The new size of the memory */ void Resize(size_t newSize); @@ -51,5 +50,10 @@ namespace skyline::kernel::type { * @return A Memory::MemoryInfo struct based on attributes of the memory */ memory::MemoryInfo GetInfo(); + + /** + * @brief Destructor of private memory, it deallocates the memory + */ + ~KPrivateMemory(); }; } diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp index 0ca70855..2139ae18 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp @@ -1,8 +1,7 @@ #include "KProcess.h" -#include "../../nce.h" +#include #include #include -#include namespace skyline::kernel::type { KProcess::TlsPage::TlsPage(u64 address) : address(address) {} @@ -24,27 +23,24 @@ namespace skyline::kernel::type { return slot[constant::TlsSlots - 1]; } - u64 KProcess::GetTlsSlot(bool init) { - if (!init) + u64 KProcess::GetTlsSlot() { for (auto &tlsPage: tlsPages) { if (!tlsPage->Full()) return tlsPage->ReserveSlot(); } - auto tlsMem = NewHandle(0, 0, PAGE_SIZE, memory::Permission(true, true, false), memory::Type::ThreadLocal); + auto tlsMem = NewHandle(mainThread, 0, 0, PAGE_SIZE, memory::Permission(true, true, false), memory::Type::ThreadLocal).item; memoryMap[tlsMem->address] = tlsMem; tlsPages.push_back(std::make_shared(tlsMem->address)); auto &tlsPage = tlsPages.back(); - if (init) + if (tlsPages.empty()) tlsPage->ReserveSlot(); // User-mode exception handling return tlsPage->ReserveSlot(); } - KProcess::KProcess(handle_t handle, pid_t pid, const DeviceState &state, u64 entryPoint, u64 stackBase, u64 stackSize) : mainThread(pid), mainThreadStackSz(stackSize), KSyncObject(handle, pid, state, KType::KProcess) { + KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize) : mainThread(pid), mainThreadStackSz(stackSize), KSyncObject(state, KType::KProcess) { state.nce->WaitRdy(pid); - threadMap[mainThread] = NewHandle(pid, entryPoint, 0, stackBase + stackSize, GetTlsSlot(true), constant::DefaultPriority, this); + threadMap[pid] = NewHandle(pid, entryPoint, 0, stackBase + stackSize, GetTlsSlot(), constant::DefaultPriority, this).item; MapPrivateRegion(0, constant::DefHeapSize, {true, true, true}, memory::Type::Heap, memory::Region::Heap); - for (auto ®ion : state.nce->memoryRegionMap) - region.second->InitiateProcess(pid); memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC); // NOLINT(hicpp-signed-bitwise) if (memFd == -1) throw exception(fmt::format("Cannot open file descriptor to /proc/{}/mem", pid)); @@ -55,16 +51,15 @@ namespace skyline::kernel::type { } /** - * Function executed by all child threads after cloning + * @brief Function executed by all child threads after cloning */ int ExecuteChild(void *) { - ptrace(PTRACE_TRACEME); asm volatile("BRK #0xFF"); // BRK #constant::brkRdy (So we know when the thread/process is ready) return 0; } u64 CreateThreadFunc(u64 stackTop) { - pid_t pid = clone(&ExecuteChild, reinterpret_cast(stackTop), CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM, nullptr); // NOLINT(hicpp-signed-bitwise) + pid_t pid = clone(&ExecuteChild, reinterpret_cast(stackTop), CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO, nullptr); // NOLINT(hicpp-signed-bitwise) return static_cast(pid); } @@ -75,8 +70,9 @@ namespace skyline::kernel::type { state.nce->ExecuteFunction((void *) CreateThreadFunc, fregs, mainThread); auto pid = static_cast(fregs.regs[0]); if (pid == -1) - throw exception(fmt::format("Cannot create thread: Address: {}, Stack Top: {}", entryPoint, stackTop)); - threadMap[pid] = NewHandle(pid, entryPoint, entryArg, stackTop, GetTlsSlot(false), priority, this); + throw exception(fmt::format("Cannot create thread: Address: 0x{:X}, Stack Top: 0x{:X}", entryPoint, stackTop)); + threadMap[pid] = NewHandle(pid, entryPoint, entryArg, stackTop, GetTlsSlot(), priority, this).item; + state.logger->Write(Logger::Info, "EP: 0x{:X}, EA: 0x{:X}, STP: 0x{:X}, PR: 0x{:X}, TLS: {}", entryPoint, entryArg, stackTop, priority, threadMap[pid]->tls); return threadMap[pid]; } @@ -88,10 +84,21 @@ namespace skyline::kernel::type { pwrite64(memFd, source, size, offset); } - std::shared_ptr KProcess::MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region) { - auto item = NewHandle(address, 0, size, perms, type); - memoryMap[item->address] = item; - memoryRegionMap[region] = item; - return item; + int KProcess::GetMemoryFd() const { + return memFd; + } + + KProcess::HandleOut KProcess::MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region) { + auto mem = NewHandle(mainThread, address, 0, size, perms, type); + memoryMap[mem.item->address] = mem.item; + memoryRegionMap[region] = mem.item; + return mem; + } + + size_t KProcess::GetProgramSize() { + size_t sharedSize = 0; + for (auto ®ion : memoryRegionMap) + sharedSize += region.second->size; + return sharedSize; } } diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h index bc169e0d..e5dc7ff9 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.h +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h @@ -50,10 +50,9 @@ namespace skyline::kernel::type { /** * @brief Returns a TLS slot from an arbitrary TLS page - * @param init If this initializes the first page (As the first TLS slot is reserved) * @return The address of a free TLS slot */ - u64 GetTlsSlot(bool init); + u64 GetTlsSlot(); int memFd; //!< The file descriptor to the memory of the process @@ -68,16 +67,25 @@ namespace skyline::kernel::type { std::map> threadMap; //!< A mapping from a PID to it's corresponding KThread object std::vector> tlsPages; //!< A vector of all allocated TLS pages + /** + * This is used as the output for functions that return created kernel objects + * @tparam objectClass The class of the kernel object + */ + template + struct HandleOut { + std::shared_ptr item; + handle_t handle; + }; + /** * @brief Creates a KThread object for the main thread and opens the process's memory file - * @param handle A handle to the process, this isn't used if the kernel creates the process - * @param pid The PID of the main thread * @param state The state of the device + * @param pid The PID of the main thread * @param entryPoint The address to start execution at * @param stackBase The base of the stack * @param stackSize The size of the stack */ - KProcess(handle_t handle, pid_t pid, const DeviceState &state, u64 entryPoint, u64 stackBase, u64 stackSize); + KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, u64 stackBase, u64 stackSize); /** * Close the file descriptor to the process's memory @@ -134,6 +142,12 @@ namespace skyline::kernel::type { */ void WriteMemory(void *source, u64 offset, size_t size) const; + /** + * @brief Returns the FD of the memory for the process + * @return The FD of the memory for the process + */ + int GetMemoryFd() const; + /** * @brief Map a chunk of process local memory (private memory) * @param address The address to map to (Can be 0 if address doesn't matter) @@ -141,9 +155,14 @@ namespace skyline::kernel::type { * @param perms The permissions of the memory * @param type The type of the memory * @param region The specific region this memory is mapped for - * @return The address of the mapped chunk (Use when address is 0) + * @return The HandleOut of the created KPrivateMemory */ - std::shared_ptr MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region); + HandleOut MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region); + + /** + * @brief Returns the total memory occupied by regions mapped for the process + */ + size_t GetProgramSize(); /** * @brief Creates a new handle to a KObject and adds it to the process handle_table @@ -152,39 +171,54 @@ namespace skyline::kernel::type { * @return A shared pointer to the corresponding object */ template - std::shared_ptr NewHandle(objectArgs... args) { - std::shared_ptr item = std::make_shared(handleIndex, mainThread, state, args...); - handleTable[handleIndex++] = std::static_pointer_cast(item); - return item; + HandleOut NewHandle(objectArgs... args) { + std::shared_ptr item; + if constexpr (std::is_same()) + item = std::make_shared(state, handleIndex, args...); + else + item = std::make_shared(state, args...); + handleTable[handleIndex] = std::static_pointer_cast(item); + return {item, handleIndex++}; } - /** - * @brief Returns the underlying kernel object for a handle - * @tparam objectClass The class of the kernel object present in the handle - * @param handle The handle of the object - * @return A shared pointer to the object - */ + /** + * @brief This inserts an item into the process handle table + * @param item The item to insert + * @return The handle of the corresponding item in the handle table + */ + template + handle_t InsertItem(std::shared_ptr item) { + handleTable[handleIndex] = std::static_pointer_cast(item); + return handleIndex++; + } + + /** + * @brief Returns the underlying kernel object for a handle + * @tparam objectClass The class of the kernel object present in the handle + * @param handle The handle of the object + * @return A shared pointer to the object + */ template std::shared_ptr GetHandle(handle_t handle) { KType objectType; - if(std::is_same::value) + if constexpr(std::is_same()) objectType = KType::KThread; - else if(std::is_same::value) + else if constexpr(std::is_same()) objectType = KType::KProcess; - else if(std::is_same::value) + else if constexpr(std::is_same()) objectType = KType::KSharedMemory; - else if(std::is_same::value) + else if constexpr(std::is_same()) objectType = KType::KPrivateMemory; - else if(std::is_same::value) + else if constexpr(std::is_same()) objectType = KType::KSession; else throw exception("KProcess::GetHandle couldn't determine object type"); try { - auto &item = handleTable.at(handle); - if (item->handleType == objectType) + auto item = handleTable.at(handle); + if (item->objectType == objectType) return std::static_pointer_cast(item); else - throw exception(fmt::format("Tried to get kernel object (0x{:X}) with different type: {} when object is {}", handle, objectType, item->handleType)); + throw exception(fmt::format("Tried to get kernel object (0x{:X}) with different type: {} when object is {}", handle, objectType, item->objectType)); } catch (std::out_of_range) { throw exception(fmt::format("GetHandle was called with invalid handle: 0x{:X}", handle)); } diff --git a/app/src/main/cpp/skyline/kernel/types/KSession.h b/app/src/main/cpp/skyline/kernel/types/KSession.h index 9e0cfd7a..8319fa7c 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSession.h +++ b/app/src/main/cpp/skyline/kernel/types/KSession.h @@ -1,7 +1,7 @@ #pragma once -#include "../../common.h" -#include "../services/base_service.h" +#include +#include #include "KSyncObject.h" namespace skyline::kernel::type { @@ -18,13 +18,11 @@ namespace skyline::kernel::type { bool isDomain{}; //!< Holds if this is a domain session or not /** - * @param handle A handle to this object - * @param pid The PID of the main thread * @param state The state of the device * @param serviceObject A shared pointer to the service class * @param serviceType The type of the service */ - KSession(handle_t handle, pid_t pid, const DeviceState &state, std::shared_ptr &serviceObject, const service::Service &serviceType) : serviceObject(serviceObject), serviceType(serviceType), KSyncObject(handle, pid, state, KType::KSession) {} + KSession(const DeviceState &state, std::shared_ptr &serviceObject, const service::Service &serviceType) : serviceObject(serviceObject), serviceType(serviceType), KSyncObject(state, KType::KSession) {} /** * This converts this session into a domain session (https://switchbrew.org/wiki/IPC_Marshalling#Domains) diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp index ee560114..1b10a153 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp @@ -1,6 +1,5 @@ #include "KSharedMemory.h" -#include "../../nce.h" -#include "../../os.h" +#include #include #include @@ -8,91 +7,92 @@ constexpr const char* ASHMEM_NAME_DEF = "dev/ashmem"; constexpr int ASHMEM_SET_SIZE = 0x40087703; namespace skyline::kernel::type { - u64 MapFunc(u64 address, size_t size, u64 perms, u64 fd) { + u64 MapSharedFunc(u64 address, size_t size, u64 perms, u64 fd) { return reinterpret_cast(mmap(reinterpret_cast(address), size, static_cast(perms), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast(fd), 0)); // NOLINT(hicpp-signed-bitwise) } - KSharedMemory::KSharedMemory(handle_t handle, pid_t pid, const DeviceState &state, size_t size, const memory::Permission localPermission, const memory::Permission remotePermission, memory::Type type) : size(size), localPermission(localPermission), remotePermission(remotePermission), type(type), KObject(handle, pid, state, KType::KSharedMemory) { + KSharedMemory::KSharedMemory(const DeviceState &state, pid_t pid, u64 kaddress, size_t ksize, const memory::Permission localPermission, const memory::Permission remotePermission, memory::Type type) : kaddress(kaddress), ksize(ksize), localPermission(localPermission), remotePermission(remotePermission), type(type), owner(pid), KObject(state, KType::KSharedMemory) { fd = open(ASHMEM_NAME_DEF, O_RDWR | O_CLOEXEC); // NOLINT(hicpp-signed-bitwise) if (fd < 0) throw exception(fmt::format("An error occurred while opening {}: {}", ASHMEM_NAME_DEF, fd)); - if (ioctl(fd, ASHMEM_SET_SIZE, size) < 0) // NOLINT(hicpp-signed-bitwise) - throw exception(fmt::format("An error occurred while setting shared memory size: {}", size)); - } - - void KSharedMemory::Map(u64 address) { - this->address = address; - for (auto process : state.os->processVec) { - user_pt_regs fregs = {0}; - fregs.regs[0] = this->address; - fregs.regs[1] = size; - if (process == ownerPid) - fregs.regs[2] = static_cast(localPermission.Get()); - else - fregs.regs[2] = static_cast(remotePermission.Get()); - fregs.regs[3] = static_cast(fd); - state.nce->ExecuteFunction(reinterpret_cast(MapFunc), fregs, process); - if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) - throw exception("An error occurred while mapping shared region in child process"); - if (!this->address) this->address = fregs.regs[0]; - } - this->address = MapFunc(this->address, size, static_cast(ownerPid ? remotePermission.Get() : localPermission.Get()), static_cast(fd)); - if (this->address == reinterpret_cast(MAP_FAILED)) // NOLINT(hicpp-signed-bitwise) + if (ioctl(fd, ASHMEM_SET_SIZE, ksize) < 0) // NOLINT(hicpp-signed-bitwise) + throw exception(fmt::format("An error occurred while setting shared memory size: {}", ksize)); + kaddress = MapSharedFunc(kaddress, ksize, static_cast(pid ? remotePermission.Get() : localPermission.Get()), static_cast(fd)); + if (kaddress == reinterpret_cast(MAP_FAILED)) // NOLINT(hicpp-signed-bitwise) throw exception(fmt::format("An occurred while mapping shared region: {}", strerror(errno))); } - u64 UnmapFunc(u64 address, size_t size) { + u64 KSharedMemory::Map(u64 address, u64 size, pid_t process) { + user_pt_regs fregs = {0}; + fregs.regs[0] = address; + fregs.regs[1] = size; + if (process == owner) + fregs.regs[2] = static_cast(localPermission.Get()); + else + fregs.regs[2] = static_cast(remotePermission.Get()); + fregs.regs[3] = static_cast(fd); + state.nce->ExecuteFunction(reinterpret_cast(MapSharedFunc), fregs, process); + if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) + throw exception("An error occurred while mapping shared region in child process"); + procInfMap[process] = {fregs.regs[0], size}; + return fregs.regs[0]; + } + + u64 UnmapSharedFunc(u64 address, size_t size) { return static_cast(munmap(reinterpret_cast(address), size)); } KSharedMemory::~KSharedMemory() { - for (auto process : state.os->processVec) { - user_pt_regs fregs = {0}; - fregs.regs[0] = address; - fregs.regs[1] = size; - state.nce->ExecuteFunction(reinterpret_cast(UnmapFunc), fregs, process); + for (auto [process, procInf] : procInfMap) { + try { + user_pt_regs fregs = {0}; + fregs.regs[0] = procInf.address; + fregs.regs[1] = procInf.size; + state.nce->ExecuteFunction(reinterpret_cast(UnmapSharedFunc), fregs, process); + } catch (const std::exception&) {} } - UnmapFunc(address, size); + UnmapSharedFunc(kaddress, ksize); close(fd); } - u64 RemapFunc(u64 address, size_t oldSize, size_t size) { + u64 RemapSharedFunc(u64 address, size_t oldSize, size_t size) { return reinterpret_cast(mremap(reinterpret_cast(address), oldSize, size, 0)); } void KSharedMemory::Resize(size_t newSize) { - for (auto process : state.os->processVec) { + for (auto& [process, procInf] : procInfMap) { user_pt_regs fregs = {0}; - fregs.regs[0] = address; - fregs.regs[1] = size; + fregs.regs[0] = procInf.address; + fregs.regs[1] = procInf.size; fregs.regs[2] = newSize; - state.nce->ExecuteFunction(reinterpret_cast(RemapFunc), fregs, process); + state.nce->ExecuteFunction(reinterpret_cast(RemapSharedFunc), fregs, process); if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) throw exception("An error occurred while remapping shared region in child process"); + procInf.size = newSize; } - if (RemapFunc(address, size, newSize) == reinterpret_cast(MAP_FAILED)) + if (RemapSharedFunc(kaddress, ksize, newSize) == reinterpret_cast(MAP_FAILED)) throw exception(fmt::format("An occurred while remapping shared region: {}", strerror(errno))); - size = newSize; + ksize = newSize; } - u64 UpdatePermissionFunc(u64 address, size_t size, u64 perms) { + u64 UpdatePermissionSharedFunc(u64 address, size_t size, u64 perms) { return static_cast(mprotect(reinterpret_cast(address), size, static_cast(perms))); } void KSharedMemory::UpdatePermission(bool local, memory::Permission newPerms) { - for (auto process : state.os->processVec) { - if ((local && process == ownerPid) || (!local && process != ownerPid)) { + for (auto& [process, procInf] : procInfMap) { + if ((local && process == owner) || (!local && process != owner)) { user_pt_regs fregs = {0}; - fregs.regs[0] = address; - fregs.regs[1] = size; + fregs.regs[0] = procInf.address; + fregs.regs[1] = procInf.size; fregs.regs[2] = static_cast(newPerms.Get()); - state.nce->ExecuteFunction(reinterpret_cast(UpdatePermissionFunc), fregs, process); + state.nce->ExecuteFunction(reinterpret_cast(UpdatePermissionSharedFunc), fregs, process); if (static_cast(fregs.regs[0]) == -1) throw exception("An error occurred while updating shared region's permissions in child process"); } } - if ((local && ownerPid == 0) || (!local && ownerPid != 0)) - if (mprotect(reinterpret_cast(address), size, newPerms.Get()) == -1) + if ((local && owner == 0) || (!local && owner != 0)) + if (mprotect(reinterpret_cast(kaddress), ksize, newPerms.Get()) == -1) throw exception(fmt::format("An occurred while updating shared region's permissions: {}", strerror(errno))); if (local) localPermission = newPerms; @@ -100,24 +100,14 @@ namespace skyline::kernel::type { remotePermission = newPerms; } - void KSharedMemory::InitiateProcess(pid_t pid) { - user_pt_regs fregs = {0}; - fregs.regs[0] = address; - fregs.regs[1] = size; - fregs.regs[2] = static_cast(remotePermission.Get()); - state.nce->ExecuteFunction(reinterpret_cast(UpdatePermissionFunc), fregs, pid); - if (static_cast(fregs.regs[0]) == -1) - throw exception("An error occurred while setting shared region's permissions in child process"); - } - - memory::MemoryInfo KSharedMemory::GetInfo(pid_t pid) { + memory::MemoryInfo KSharedMemory::GetInfo(pid_t process) { memory::MemoryInfo info{}; - info.baseAddress = address; - info.size = size; + info.baseAddress = kaddress; + info.size = ksize; info.type = static_cast(type); info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0); info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0); - info.perms = (pid == ownerPid) ? localPermission : remotePermission; + info.perms = (process == owner) ? localPermission : remotePermission; info.ipcRefCount = ipcRefCount; info.deviceRefCount = deviceRefCount; return info; diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h index ffbd1429..c13966f3 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h @@ -1,6 +1,6 @@ #pragma once -#include "../../memory.h" +#include #include "KObject.h" namespace skyline::kernel::type { @@ -10,10 +10,16 @@ namespace skyline::kernel::type { class KSharedMemory : public KObject { private: int fd; //!< A file descriptor to the underlying shared memory + struct ProcInf { + u64 address; + size_t size; + }; + std::unordered_map procInfMap; //!< Maps from a PID to where the memory was mapped to public: - u64 address{}; //!< The address of the allocated memory - size_t size; //!< The size of the allocated memory + pid_t owner; //!< The PID of the process owning this shared memory + u64 kaddress; //!< The address of the allocated memory for the kernel + size_t ksize; //!< The size of the allocated memory u16 ipcRefCount{}; //!< The amount of reference to this memory for IPC u16 deviceRefCount{}; //!< The amount of reference to this memory for IPC memory::Permission localPermission; //!< The permission for the owner process @@ -21,25 +27,24 @@ namespace skyline::kernel::type { memory::Type type; //!< The type of this memory allocation /** - * @param handle A handle to this object - * @param pid The PID of the main thread * @param state The state of the device - * @param size The size of the allocation + * @param pid The PID of the owner thread + * @param kaddress The address of the allocation on the kernel (Arbitrary is 0) + * @param ksize The size of the allocation on the kernel * @param localPermission The permission of the owner process * @param remotePermission The permission of any process except the owner process + * @param type The type of the memory */ - KSharedMemory(handle_t handle, pid_t pid, const DeviceState &state, size_t size, const memory::Permission localPermission, const memory::Permission remotePermission, memory::Type type); + KSharedMemory(const DeviceState &state, pid_t pid, u64 kaddress, size_t ksize, const memory::Permission localPermission, const memory::Permission remotePermission, memory::Type type); /** * @brief Maps the shared memory at an address * @param address The address to map to (If NULL an arbitrary address is picked) + * @param size The amount of shared memory to map + * @param process The PID of the process + * @return The address of the allocation */ - void Map(u64 address); - - /** - * @brief Destructor of shared memory, it deallocates the memory from all processes - */ - ~KSharedMemory(); + u64 Map(u64 address, u64 size, pid_t process); /** * @brief Resize a chunk of memory as to change the size occupied by it @@ -55,15 +60,14 @@ namespace skyline::kernel::type { void UpdatePermission(bool local, memory::Permission newPerms); /** - * Initiates the instance of shared memory in a particular process - * @param pid The PID of the process - */ - void InitiateProcess(pid_t pid); - - /** - * @param pid The PID of the requesting process + * @param process The PID of the requesting process * @return A Memory::MemoryInfo struct based on attributes of the memory */ - memory::MemoryInfo GetInfo(pid_t pid); + memory::MemoryInfo GetInfo(pid_t process); + + /** + * @brief Destructor of shared memory, it deallocates the memory from all processes + */ + ~KSharedMemory(); }; } diff --git a/app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp b/app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp index f920f06a..6a5d8928 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp @@ -1,20 +1,12 @@ #include "KSyncObject.h" -#include "../../os.h" +#include namespace skyline::kernel::type { - KSyncObject::KSyncObject(skyline::handle_t handle, pid_t pid, const skyline::DeviceState &state, skyline::kernel::type::KType type) : KObject(handle, pid, state, type) {} + KSyncObject::KSyncObject(const skyline::DeviceState &state, skyline::kernel::type::KType type) : KObject(state, type) {} void KSyncObject::Signal() { - for (auto&[tid, thread] : state.os->threadMap.at(ownerPid)->threadMap) { - if (thread->status == type::KThread::ThreadStatus::Waiting) { - for (auto &waitHandle : thread->waitHandles) { - if (handle == waitHandle) { - thread->status = type::KThread::ThreadStatus::Runnable; - state.nce->SetRegister(Wreg::W0, constant::status::Success, thread->pid); - state.nce->SetRegister(Wreg::W1, handle, thread->pid); - } - } - } + for(const auto& pid : waitThreads) { + state.os->processMap.at(pid)->threadMap.at(pid)->status = KThread::ThreadStatus::Runnable; } } } diff --git a/app/src/main/cpp/skyline/kernel/types/KSyncObject.h b/app/src/main/cpp/skyline/kernel/types/KSyncObject.h index 8178a953..e60ec4b8 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSyncObject.h +++ b/app/src/main/cpp/skyline/kernel/types/KSyncObject.h @@ -1,6 +1,6 @@ #pragma once -#include "../../common.h" +#include #include "KObject.h" namespace skyline::kernel::type { @@ -9,18 +9,18 @@ namespace skyline::kernel::type { */ class KSyncObject : public KObject { public: + bool signalled = false; //!< If the current object is signalled (Used by KEvent as it stays signalled till svcClearEvent or svcClearSignal is called) + std::vector waitThreads; //!< A vector of threads waiting on this object + /** - * @param handle The handle of the object in the handle table - * @param pid The PID of the main thread * @param state The state of the device * @param type The type of the object */ - KSyncObject(skyline::handle_t handle, pid_t pid, const DeviceState &state, skyline::kernel::type::KType type); + KSyncObject(const DeviceState &state, skyline::kernel::type::KType type); - // TODO: Rewrite this so that we store list of waiting threads instead /** * @brief A function for calling when a particular KSyncObject is signalled */ - void Signal(); + virtual void Signal(); }; } diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.cpp b/app/src/main/cpp/skyline/kernel/types/KThread.cpp index e9ca07f1..b07408fd 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KThread.cpp @@ -1,10 +1,10 @@ #include #include "KThread.h" #include "KProcess.h" -#include "../../nce.h" +#include namespace skyline::kernel::type { - KThread::KThread(handle_t handle, pid_t parent_pid, const DeviceState &state, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent) : pid(self_pid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), KSyncObject(handle, parent_pid, state, KType::KThread) { + KThread::KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent) : handle(handle), pid(self_pid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), KSyncObject(state, KType::KThread) { UpdatePriority(priority); } diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.h b/app/src/main/cpp/skyline/kernel/types/KThread.h index 69b35dc3..35c8eb5e 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.h +++ b/app/src/main/cpp/skyline/kernel/types/KThread.h @@ -13,8 +13,9 @@ namespace skyline::kernel::type { u64 entryArg; //!< An argument to pass to the process on entry public: - enum class ThreadStatus { Created, Running, Waiting, Runnable } status = ThreadStatus::Created; //!< The state of the thread - std::vector waitHandles; //!< A vector holding handles this thread is waiting for + 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 + std::vector> waitObjects; //!< A vector holding handles this thread is waiting for u64 timeoutEnd{}; //!< The time when a svcWaitSynchronization 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) @@ -22,9 +23,8 @@ namespace skyline::kernel::type { u8 priority; //!< Hold the priority of a thread in Nintendo format /** - * @param handle The handle of the current thread - * @param parent_pid The PID of the main thread * @param state The state of the device + * @param handle The handle of the current thread * @param self_pid The PID of this thread * @param entryPoint The address to start execution at * @param entryArg An argument to pass to the process on entry @@ -33,7 +33,7 @@ namespace skyline::kernel::type { * @param priority The priority of the thread in Nintendo format * @param parent The parent process of this thread */ - KThread(handle_t handle, pid_t parent_pid, const DeviceState &state, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent); + KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent); /** * @brief Kills the thread and deallocates the memory allocated for stack. diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp new file mode 100644 index 00000000..744161c4 --- /dev/null +++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp @@ -0,0 +1,110 @@ +#include "KTransferMemory.h" +#include +#include + +constexpr const char *ASHMEM_NAME_DEF = "dev/ashmem"; +constexpr int ASHMEM_SET_SIZE = 0x40087703; + +namespace skyline::kernel::type { + u64 MapTransferFunc(u64 address, size_t size, u64 perms) { + return reinterpret_cast(mmap(reinterpret_cast(address), size, static_cast(perms), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0)); // NOLINT(hicpp-signed-bitwise) + } + + KTransferMemory::KTransferMemory(const DeviceState &state, pid_t pid, u64 address, size_t size, const memory::Permission permission) : owner(pid), cSize(size), permission(permission), KObject(state, KType::KTransferMemory) { + if (pid) { + user_pt_regs fregs = {0}; + fregs.regs[0] = address; + fregs.regs[1] = size; + fregs.regs[2] = static_cast(permission.Get()); + state.nce->ExecuteFunction(reinterpret_cast(MapTransferFunc), fregs, pid); + if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) + throw exception("An error occurred while mapping shared region in child process"); + cAddress = fregs.regs[0]; + } else { + address = MapTransferFunc(address, size, static_cast(permission.Get())); + if (reinterpret_cast(address) == MAP_FAILED) + throw exception("An error occurred while mapping transfer memory in kernel"); + cAddress = address; + } + } + + u64 UnmapTransferFunc(u64 address, size_t size) { + return static_cast(munmap(reinterpret_cast(address), size)); + } + + u64 KTransferMemory::Transfer(pid_t process, u64 address, u64 size) { + if (process) { + user_pt_regs fregs = {0}; + fregs.regs[0] = address; + fregs.regs[1] = size; + fregs.regs[2] = static_cast(permission.Get()); + state.nce->ExecuteFunction(reinterpret_cast(MapTransferFunc), fregs, process); + if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) + throw exception("An error occurred while mapping transfer memory in child process"); + address = fregs.regs[0]; + } else { + address = MapTransferFunc(address, size, static_cast(permission.Get())); + if (reinterpret_cast(address) == MAP_FAILED) + throw exception("An error occurred while mapping transfer memory in kernel"); + } + + size_t copySz = std::min(size, cSize); + + if (process && owner) { + // TODO: Update when copy_file_range (http://man7.org/linux/man-pages/man2/copy_file_range.2.html) is added to bionic + std::vector tempBuf(copySz); + state.os->processMap.at(process)->ReadMemory(tempBuf.data(), cAddress, copySz); + state.os->processMap.at(owner)->WriteMemory(tempBuf.data(), address, copySz); + } else if (process && !owner) { + state.os->processMap.at(process)->WriteMemory(reinterpret_cast(cAddress), address, copySz); + } else if (!process && owner) { + state.os->processMap.at(owner)->ReadMemory(reinterpret_cast(address), cAddress, copySz); + } else { + throw exception("Transferring from kernel to kernel is not supported"); + } + + if (owner) { + user_pt_regs fregs = {0}; + fregs.regs[0] = address; + fregs.regs[1] = size; + fregs.regs[2] = static_cast(permission.Get()); + state.nce->ExecuteFunction(reinterpret_cast(UnmapTransferFunc), fregs, owner); + if (reinterpret_cast(fregs.regs[0]) == MAP_FAILED) + throw exception("An error occurred while unmapping transfer memory in child process"); + } else { + if (reinterpret_cast(UnmapTransferFunc(address, size)) == MAP_FAILED) + throw exception("An error occurred while unmapping transfer memory in kernel"); + } + + owner = process; + cAddress = address; + cSize = size; + return address; + } + + memory::MemoryInfo KTransferMemory::GetInfo() { + memory::MemoryInfo info{}; + info.baseAddress = cAddress; + info.size = cSize; + info.type = static_cast(memory::Type::TransferMemory); + info.memoryAttribute.isIpcLocked = (info.ipcRefCount > 0); + info.memoryAttribute.isDeviceShared = (info.deviceRefCount > 0); + info.perms = permission; + info.ipcRefCount = ipcRefCount; + info.deviceRefCount = deviceRefCount; + return info; + } + + KTransferMemory::~KTransferMemory() { + if(owner) { + try { + user_pt_regs fregs = {0}; + fregs.regs[0] = cAddress; + fregs.regs[1] = cSize; + state.nce->ExecuteFunction(reinterpret_cast(UnmapTransferFunc), fregs, owner); + } catch (const std::exception &) { + } + } else + UnmapTransferFunc(cAddress, cSize); + } +}; diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h new file mode 100644 index 00000000..0a261fc3 --- /dev/null +++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include "KObject.h" + +namespace skyline::kernel::type { + /** + * @brief KTransferMemory is used to hold a particular amount of transferable memory + */ + class KTransferMemory : public KObject { + public: + pid_t owner; //!< The PID of the process owning this memory + u64 cAddress; //!< The current address of the allocated memory for the kernel + size_t cSize; //!< The current size of the allocated memory + u16 ipcRefCount{}; //!< The amount of reference to this memory for IPC + u16 deviceRefCount{}; //!< The amount of reference to this memory for IPC + memory::Permission permission; //!< The permissions of the memory + + /** + * @param state The state of the device + * @param pid The PID of the owner thread (Use 0 for kernel) + * @param address The address to map to (If NULL an arbitrary address is picked) + * @param size The size of the allocation + * @param permission The permissions of the memory + * @param type The type of the memory + */ + KTransferMemory(const DeviceState &state, pid_t pid, u64 address, size_t size, const memory::Permission permission); + + /** + * @brief Transfers this piece of memory to another process + * @param process The PID of the process (Use 0 for kernel) + * @param address The address to map to (If NULL an arbitrary address is picked) + * @param size The amount of shared memory to map + * @return The address of the allocation + */ + u64 Transfer(pid_t process, u64 address, u64 size); + + /** + * @brief Returns a MemoryInfo struct filled with attributes of this region of memory + * @return A memory::MemoryInfo struct based on attributes of the memory + */ + memory::MemoryInfo GetInfo(); + + /** + * @brief Destructor of private memory, it deallocates the memory + */ + ~KTransferMemory(); + }; +} diff --git a/app/src/main/cpp/skyline/loader/loader.h b/app/src/main/cpp/skyline/loader/loader.h index 36a0b568..cb69e72d 100644 --- a/app/src/main/cpp/skyline/loader/loader.h +++ b/app/src/main/cpp/skyline/loader/loader.h @@ -1,7 +1,8 @@ #pragma once #include -#include "../os.h" +#include +#include namespace skyline::loader { class Loader { @@ -28,5 +29,12 @@ namespace skyline::loader { * @param file_path_ The path to the ROM file */ Loader(std::string &filePath) : filePath(filePath), file(filePath, std::ios::binary | std::ios::beg) {} + + /** + * This loads in the data of the main process + * @param process The process to load in the data + * @param state The state of the device + */ + virtual void LoadProcessData(const std::shared_ptr process, const DeviceState &state) = 0; }; } diff --git a/app/src/main/cpp/skyline/loader/nro.cpp b/app/src/main/cpp/skyline/loader/nro.cpp index 7ecf5698..d16ecf53 100644 --- a/app/src/main/cpp/skyline/loader/nro.cpp +++ b/app/src/main/cpp/skyline/loader/nro.cpp @@ -2,43 +2,49 @@ #include "nro.h" namespace skyline::loader { - NroLoader::NroLoader(std::string filePath, const DeviceState &state) : Loader(filePath) { - NroHeader header{}; + NroLoader::NroLoader(std::string filePath) : Loader(filePath) { ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader)); if (header.magic != constant::NroMagic) throw exception(fmt::format("Invalid NRO magic! 0x{0:X}", header.magic)); + } - state.nce->MapSharedRegion(constant::BaseAddr, header.text.size, {true, true, true}, {true, true, true}, memory::Type::CodeStatic, memory::Region::Text); // R-X + void NroLoader::LoadProcessData(const std::shared_ptr process, const DeviceState &state) { + process->MapPrivateRegion(constant::BaseAddr, header.text.size, {true, true, true}, memory::Type::CodeStatic, memory::Region::Text); // R-X state.logger->Write(Logger::Debug, "Successfully mapped region .text @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr, header.text.size); - auto rodata = state.nce->MapSharedRegion(constant::BaseAddr + header.text.size, header.ro.size, {true, true, false}, {true, false, false}, memory::Type::CodeReadOnly, memory::Region::RoData); // R-- + process->MapPrivateRegion(constant::BaseAddr + header.text.size, header.ro.size, {true, false, false}, memory::Type::CodeReadOnly, memory::Region::RoData); // R-- state.logger->Write(Logger::Debug, "Successfully mapped region .ro @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + header.text.size, header.ro.size); - state.nce->MapSharedRegion(constant::BaseAddr + header.text.size + header.ro.size, header.data.size, {true, true, false}, {true, true, false}, memory::Type::CodeStatic, memory::Region::Data); // RW- + process->MapPrivateRegion(constant::BaseAddr + header.text.size + header.ro.size, header.data.size, {true, true, false}, memory::Type::CodeStatic, memory::Region::Data); // RW- state.logger->Write(Logger::Debug, "Successfully mapped region .data @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + header.text.size + header.ro.size, header.data.size); - state.nce->MapSharedRegion(constant::BaseAddr + header.text.size + header.ro.size + header.data.size, header.bssSize, {true, true, true}, {true, true, true}, memory::Type::CodeMutable, memory::Region::Bss); // RWX + process->MapPrivateRegion(constant::BaseAddr + header.text.size + header.ro.size + header.data.size, header.bssSize, {true, true, true}, memory::Type::CodeMutable, memory::Region::Bss); // RWX state.logger->Write(Logger::Debug, "Successfully mapped region .bss @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddr + header.text.size + header.ro.size + header.data.size, header.bssSize); - ReadOffset(reinterpret_cast(constant::BaseAddr), header.text.offset, header.text.size); - ReadOffset(reinterpret_cast(constant::BaseAddr + header.text.size), header.ro.offset, header.ro.size); - ReadOffset(reinterpret_cast(constant::BaseAddr + header.text.size + header.ro.size), header.data.offset, header.data.size); + std::vector text(header.text.size); + std::vector rodata(header.ro.size); + std::vector data(header.data.size); + ReadOffset(text.data(), header.text.offset, header.text.size); + ReadOffset(rodata.data(), header.ro.offset, header.ro.size); + ReadOffset(data.data(), header.data.offset, header.data.size); // Replace SVC & MRS with BRK - auto address = (u32 *) constant::BaseAddr + header.text.offset; - size_t textSize = header.text.size / sizeof(u32); - for (size_t iter = 0; iter < textSize; iter++) { - auto instrSvc = reinterpret_cast(address + iter); - auto instrMrs = reinterpret_cast(address + iter); + for (auto address = reinterpret_cast(text.data()); address <= reinterpret_cast(text.data() + header.text.size); address++) { + auto instrSvc = reinterpret_cast(address); + auto instrMrs = reinterpret_cast(address); if (instrSvc->Verify()) { instr::Brk brk(static_cast(instrSvc->value)); - address[iter] = *reinterpret_cast(&brk); + *address = *reinterpret_cast(&brk); } else if (instrMrs->Verify() && instrMrs->srcReg == constant::TpidrroEl0) { instr::Brk brk(static_cast(constant::SvcLast + 1 + instrMrs->dstReg)); - address[iter] = *reinterpret_cast(&brk); + *address = *reinterpret_cast(&brk); } } + + process->WriteMemory(text.data(), constant::BaseAddr, header.text.size); + process->WriteMemory(rodata.data(), constant::BaseAddr + header.text.size, header.ro.size); + process->WriteMemory(data.data(), constant::BaseAddr + header.text.size + header.ro.size, header.data.size); } } diff --git a/app/src/main/cpp/skyline/loader/nro.h b/app/src/main/cpp/skyline/loader/nro.h index b41beef1..667e1a85 100644 --- a/app/src/main/cpp/skyline/loader/nro.h +++ b/app/src/main/cpp/skyline/loader/nro.h @@ -39,13 +39,19 @@ namespace skyline::loader { NroSegmentHeader api_info; NroSegmentHeader dynstr; NroSegmentHeader dynsym; - }; + } header {}; public: /** * @param filePath The path to the ROM file + */ + NroLoader(std::string filePath); + + /** + * This loads in the data of the main process + * @param process The process to load in the data * @param state The state of the device */ - NroLoader(std::string filePath, const DeviceState &state); + void LoadProcessData(const std::shared_ptr process, const DeviceState &state); }; } diff --git a/app/src/main/cpp/skyline/memory.h b/app/src/main/cpp/skyline/memory.h index f7406f68..69f270f5 100644 --- a/app/src/main/cpp/skyline/memory.h +++ b/app/src/main/cpp/skyline/memory.h @@ -55,23 +55,23 @@ namespace skyline::memory { * @brief This holds certain attributes of a chunk of memory: https://switchbrew.org/wiki/SVC#MemoryAttribute */ struct MemoryAttribute { - bool isBorrowed : 1; - bool isIpcLocked : 1; + bool isBorrowed : 1; + bool isIpcLocked : 1; bool isDeviceShared : 1; - bool isUncached : 1; + bool isUncached : 1; }; /** * @brief This contains information about a chunk of memory: https://switchbrew.org/wiki/SVC#MemoryInfo */ struct MemoryInfo { - u64 baseAddress : 64; - u64 size : 64; - u64 type : 64; + u64 baseAddress; + u64 size; + u64 type; MemoryAttribute memoryAttribute; Permission perms; - u32 ipcRefCount : 32; - u32 deviceRefCount : 32; + u32 ipcRefCount; + u32 deviceRefCount; u32 : 32; }; static_assert(sizeof(MemoryInfo) == 0x28); diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp index aa394a42..018a4a27 100644 --- a/app/src/main/cpp/skyline/nce.cpp +++ b/app/src/main/cpp/skyline/nce.cpp @@ -1,8 +1,7 @@ #include #include #include -#include "os.h" -#include "nce.h" +#include extern bool Halt; @@ -34,8 +33,8 @@ namespace skyline { void NCE::Execute() { int status = 0; - while (!Halt && !state->os->threadMap.empty()) { - for (const auto &process : state->os->threadMap) { + while (!Halt && !state->os->processMap.empty()) { + for (const auto &process : state->os->processMap) { // NOLINT(performance-for-range-copy) state->os->thisProcess = process.second; state->os->thisThread = process.second->threadMap.at(process.first); currPid = process.first; @@ -48,8 +47,8 @@ namespace skyline { if (instr.Verify()) { // We store the instruction value as the immediate value in BRK. 0x0 to 0x7F are SVC, 0x80 to 0x9E is MRS for TPIDRRO_EL0. if (instr.value <= constant::SvcLast) { - state->os->SvcHandler(static_cast(instr.value), currPid); - if (state->thisThread->status == kernel::type::KThread::ThreadStatus::Waiting) + state->os->SvcHandler(static_cast(instr.value)); + if (state->thisThread->status != kernel::type::KThread::ThreadStatus::Running) continue; } else if (instr.value > constant::SvcLast && instr.value <= constant::SvcLast + constant::NumRegs) { // Catch MRS that reads the value of TPIDRRO_EL0 (TLS) @@ -63,19 +62,25 @@ namespace skyline { WriteRegisters(currRegs); ResumeProcess(); } else { - state->logger->Write(Logger::Warn, "Thread threw unknown signal, PID: {}, Stop Signal: {}", currPid, strsignal(WSTOPSIG(status))); // NOLINT(hicpp-signed-bitwise) + try { + ReadRegisters(currRegs); + u32 instr = static_cast(ptrace(PTRACE_PEEKDATA, currPid, currRegs.pc, NULL)); + state->logger->Write(Logger::Warn, "Thread threw unknown signal, PID: {}, Stop Signal: {}, Instruction: 0x{:X}, PC: 0x{:X}", currPid, strsignal(WSTOPSIG(status)), instr, currRegs.pc); // NOLINT(hicpp-signed-bitwise) + } catch (const exception &) { + state->logger->Write(Logger::Warn, "Thread threw unknown signal, PID: {}, Stop Signal: {}", currPid, strsignal(WSTOPSIG(status))); // NOLINT(hicpp-signed-bitwise) + } state->os->KillThread(currPid); } } - } else if (state->thisThread->status == kernel::type::KThread::ThreadStatus::Waiting) { + } 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()) { - state->thisThread->status = kernel::type::KThread::ThreadStatus::Running; - SetRegister(Wreg::W0, constant::status::Timeout); - currRegs.pc += sizeof(u32); - WriteRegisters(currRegs); - ResumeProcess(); + if (state->thisThread->status == kernel::type::KThread::ThreadStatus::Waiting) + SetRegister(Wreg::W0, constant::status::Timeout); + state->thisThread->status = kernel::type::KThread::ThreadStatus::Runnable; } - } else if (state->thisThread->status == kernel::type::KThread::ThreadStatus::Runnable) { + } + if (state->thisThread->status == kernel::type::KThread::ThreadStatus::Runnable) { + state->thisThread->waitObjects.clear(); state->thisThread->status = kernel::type::KThread::ThreadStatus::Running; currRegs.pc += sizeof(u32); WriteRegisters(currRegs); @@ -97,7 +102,7 @@ namespace skyline { ReadRegisters(backupRegs, pid); funcRegs.pc = reinterpret_cast(func); funcRegs.sp = backupRegs.sp; - funcRegs.regs[static_cast(Xreg::X30)] = reinterpret_cast(BrkLr); // Set LR to 'brk_lr' so the application will hit a breakpoint after the function returns [LR is where the program goes after it returns from a function] + funcRegs.regs[static_cast(Xreg::X30)] = reinterpret_cast(BrkLr); // Set LR to 'brk_lr' so the application will hit a breakpoint after the function returns WriteRegisters(funcRegs, pid); ResumeProcess(pid); funcRegs = WaitRdy(pid); @@ -192,18 +197,4 @@ namespace skyline { registerMap.at(pid).pstate = value; } } - - std::shared_ptr NCE::MapSharedRegion(const u64 address, const size_t size, const memory::Permission localPermission, const memory::Permission remotePermission, const memory::Type type, const memory::Region region) { - auto item = std::make_shared(0, 0, *state, size, localPermission, remotePermission, type); - item->Map(address); - memoryRegionMap[region] = item; - return item; - } - - size_t NCE::GetSharedSize() { - size_t sharedSize = 0; - for (auto ®ion : memoryRegionMap) - sharedSize += region.second->size; - return sharedSize; - } } diff --git a/app/src/main/cpp/skyline/nce.h b/app/src/main/cpp/skyline/nce.h index 3c5d83fb..f0d381b0 100644 --- a/app/src/main/cpp/skyline/nce.h +++ b/app/src/main/cpp/skyline/nce.h @@ -41,8 +41,6 @@ namespace skyline { instr::Brk ReadBrk(u64 address, pid_t pid = 0) const; public: - std::map> memoryRegionMap; //!< A mapping from every Memory::Region to a shared pointer to it's corresponding kernel::type::KSharedMemory - /** * @brief Initialize NCE by setting the device_state variable * @param state The state of the device @@ -139,23 +137,5 @@ namespace skyline { * @param pid The PID of the process (Defaults to currPid) */ void SetRegister(Sreg regId, u32 value, pid_t pid = 0); - - /** - * @brief Map a chunk of shared memory (Use only when kernel should be owner process else create KSharedMemory directly) - * @param address The address to map to (Can be 0 if address doesn't matter) - * @param size The size of the chunk of memory - * @param localPermission The permissions of the memory for the kernel - * @param remotePermission The permissions of the memory for the processes - * @param type The type of the memory - * @param region The specific region this memory is mapped for - * @return A shared pointer to the kernel::type::KSharedMemory object - */ - std::shared_ptr MapSharedRegion(const u64 address, const size_t size, const memory::Permission localPermission, const memory::Permission remotePermission, const memory::Type type, const memory::Region region); - - /** - * @brief Returns the total memory occupied by shared regions mapped using the kernel - * @return The total size of allocated shared memory by NCE::MapSharedRegion - */ - size_t GetSharedSize(); }; } diff --git a/app/src/main/cpp/skyline/os.cpp b/app/src/main/cpp/skyline/os.cpp index fb85cb5e..45395601 100644 --- a/app/src/main/cpp/skyline/os.cpp +++ b/app/src/main/cpp/skyline/os.cpp @@ -1,9 +1,6 @@ #include "os.h" #include "kernel/svc.h" #include "loader/nro.h" -#include "nce.h" - -extern bool Halt; namespace skyline::kernel { OS::OS(std::shared_ptr &logger, std::shared_ptr &settings) : state(this, thisProcess, thisThread, std::make_shared(), settings, logger), serviceManager(state) {} @@ -12,14 +9,13 @@ namespace skyline::kernel { state.nce->Initialize(state); std::string romExt = romFile.substr(romFile.find_last_of('.') + 1); std::transform(romExt.begin(), romExt.end(), romExt.begin(), [](unsigned char c) { return std::tolower(c); }); - - if (romExt == "nro") - loader::NroLoader loader(romFile, state); - else + auto process = CreateProcess(constant::BaseAddr, constant::DefStackSize); + if (romExt == "nro") { + loader::NroLoader loader(romFile); + loader.LoadProcessData(process, state); + } else throw exception("Unsupported ROM extension."); - - auto mainProcess = CreateProcess(state.nce->memoryRegionMap.at(memory::Region::Text)->address, constant::DefStackSize); - mainProcess->threadMap.at(mainProcess->mainThread)->Start(); // The kernel itself is responsible for starting the main thread + process->threadMap.at(process->mainThread)->Start(); // The kernel itself is responsible for starting the main thread state.nce->Execute(); } @@ -40,38 +36,42 @@ namespace skyline::kernel { munmap(stack, stackSize); throw exception("Failed to create guard pages"); } - pid_t pid = clone(&ExecuteChild, stack + stackSize, CLONE_FS | SIGCHLD, nullptr); // NOLINT(hicpp-signed-bitwise) + pid_t pid = clone(&ExecuteChild, stack + stackSize, CLONE_FILES | CLONE_FS | SIGCHLD, nullptr); // NOLINT(hicpp-signed-bitwise) if (pid == -1) throw exception(fmt::format("Call to clone() has failed: {}", strerror(errno))); - std::shared_ptr process = std::make_shared(0, pid, state, address, reinterpret_cast(stack), stackSize); - threadMap[pid] = process; + std::shared_ptr process = std::make_shared(state, pid, address, reinterpret_cast(stack), stackSize); + processMap[pid] = process; processVec.push_back(pid); state.logger->Write(Logger::Debug, "Successfully created process with PID: {}", pid); return process; } void OS::KillThread(pid_t pid) { - auto process = threadMap.at(pid); + auto process = processMap.at(pid); if (process->mainThread == pid) { state.logger->Write(Logger::Debug, "Exiting process with PID: {}", pid); // Erasing all shared_ptr instances to the process will call the destructor // However, in the case these are not all instances of it we wouldn't want to call the destructor for (auto&[key, value]: process->threadMap) - threadMap.erase(key); + processMap.erase(key); processVec.erase(std::remove(processVec.begin(), processVec.end(), pid), processVec.end()); } else { state.logger->Write(Logger::Debug, "Exiting thread with TID: {}", pid); process->handleTable.erase(process->threadMap[pid]->handle); process->threadMap.erase(pid); - threadMap.erase(pid); + processMap.erase(pid); } } - void OS::SvcHandler(u16 svc, pid_t pid) { + void OS::SvcHandler(u16 svc) { if (svc::SvcTable[svc]) { state.logger->Write(Logger::Debug, "SVC called 0x{:X}", svc); (*svc::SvcTable[svc])(state); } else throw exception(fmt::format("Unimplemented SVC 0x{:X}", svc)); } + + std::shared_ptr OS::MapSharedKernel(const u64 address, const size_t size, const memory::Permission kernelPermission, const memory::Permission remotePermission, const memory::Type type) { + return std::make_shared(state, 0, address, size, kernelPermission, remotePermission, type); + } } diff --git a/app/src/main/cpp/skyline/os.h b/app/src/main/cpp/skyline/os.h index 1b7b9eeb..b41fd170 100644 --- a/app/src/main/cpp/skyline/os.h +++ b/app/src/main/cpp/skyline/os.h @@ -18,7 +18,7 @@ namespace skyline::kernel { DeviceState state; //!< The state of the device public: - std::unordered_map> threadMap; //!< A mapping from a threat's PID to it's KProcess object + std::unordered_map> processMap; //!< A mapping from a threat's PID to it's KProcess object std::vector processVec; //!< A vector of all processes by their main thread's PID std::shared_ptr thisProcess; //!< The corresponding KProcess object of the process that's called an SVC std::shared_ptr thisThread; //!< The corresponding KThread object of the thread that's called an SVC @@ -53,8 +53,19 @@ namespace skyline::kernel { /** * @brief Handles a particular SuperVisor Call * @param svc The ID of the SVC to be called - * @param pid The PID of the process/thread calling the SVC */ - void SvcHandler(u16 svc, pid_t pid); + void SvcHandler(u16 svc); + + /** + * @brief Map a chunk of shared memory (Use only when kernel should be owner process else create KSharedMemory directly) + * @param address The address to map to (Can be 0 if address doesn't matter) + * @param size The size of the chunk of memory + * @param localPermission The permissions of the memory for the kernel + * @param remotePermission The permissions of the memory for the processes + * @param type The type of the memory + * @param region The specific region this memory is mapped for + * @return A shared pointer to the kernel::type::KSharedMemory object + */ + std::shared_ptr MapSharedKernel(const u64 address, const size_t size, const memory::Permission kernelPermission, const memory::Permission remotePermission, const memory::Type type); }; } diff --git a/build.gradle b/build.gradle index e88fc960..4c880263 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files