mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-28 08:05:29 +03:00
Fixes and Additions for HID support
The following things were fixed: * KSharedMemory * KSyncObject (and how waiting on them works) * Inclusion of Headers What was added: * Transfer Memory * svcSleepThread
This commit is contained in:
parent
f7effe86ae
commit
ec71735ece
@ -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)
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "common.h"
|
||||
#include <tinyxml2.h>
|
||||
#include <syslog.h>
|
||||
|
||||
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>& logger) {
|
||||
void Settings::List(std::shared_ptr<Logger> 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
|
||||
|
@ -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> &logger);
|
||||
void List(std::shared_ptr<Logger> logger);
|
||||
};
|
||||
|
||||
// Predeclare some classes here as we use them in DeviceState
|
||||
|
@ -1,5 +1,3 @@
|
||||
#include <syslog.h>
|
||||
#include <cstdlib>
|
||||
#include "ipc.h"
|
||||
#include "types/KProcess.h"
|
||||
|
||||
@ -13,7 +11,7 @@ namespace skyline::kernel::ipc {
|
||||
|
||||
if (header->handle_desc) {
|
||||
handleDesc = reinterpret_cast<HandleDescriptor *>(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<handle_t *>(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);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include "../common.h"
|
||||
#include <common.h>
|
||||
|
||||
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<u32>(address & 0x7FFFFFFF80000000);
|
||||
address_32_35 = static_cast<u16>(address & 0x78000000);
|
||||
address_36_38 = static_cast<u16>(address & 0x7000000);
|
||||
|
@ -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<type::KEvent>();
|
||||
response.copyHandles.push_back(messageEvent->handle);
|
||||
auto event = state.thisProcess->NewHandle<type::KEvent>();
|
||||
messageEvent = event.item;
|
||||
response.copyHandles.push_back(event.handle);
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetCurrentFocusState(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "../base_service.h"
|
||||
#include "../serviceman.h"
|
||||
#include "../../types/KProcess.h"
|
||||
#include "../../types/KEvent.h"
|
||||
#include <kernel/services/base_service.h>
|
||||
#include <kernel/services/serviceman.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <kernel/types/KEvent.h>
|
||||
|
||||
namespace skyline::kernel::service::am {
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../base_service.h"
|
||||
#include "../serviceman.h"
|
||||
#include <kernel/services/base_service.h>
|
||||
#include <kernel/services/serviceman.h>
|
||||
|
||||
namespace skyline::kernel::service::apm {
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common.h"
|
||||
#include "../ipc.h"
|
||||
#include <common.h>
|
||||
#include <kernel/ipc.h>
|
||||
#include <functional>
|
||||
|
||||
#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;
|
||||
|
@ -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<u32*>(request.cmdArg)));
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../base_service.h"
|
||||
#include "../serviceman.h"
|
||||
#include <kernel/services/base_service.h>
|
||||
#include <kernel/services/serviceman.h>
|
||||
|
||||
namespace skyline::kernel::service::fatal {
|
||||
/**
|
||||
|
21
app/src/main/cpp/skyline/kernel/services/hid/hid.cpp
Normal file
21
app/src/main/cpp/skyline/kernel/services/hid/hid.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "hid.h"
|
||||
#include <os.h>
|
||||
|
||||
namespace skyline::kernel::service::hid {
|
||||
hid::hid(const DeviceState &state, ServiceManager& manager) : BaseService(state, manager, false, Service::hid, {
|
||||
{0x0, SFunc(hid::CreateAppletResource)}
|
||||
}) {}
|
||||
|
||||
void hid::CreateAppletResource(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
resource = std::static_pointer_cast<IAppletResource>(manager.NewService(Service::hid_IAppletResource, session, response));
|
||||
}
|
||||
|
||||
IAppletResource::IAppletResource(const DeviceState &state, ServiceManager& manager) : BaseService(state, manager, false, Service::hid_IAppletResource, {
|
||||
{0x0, SFunc(IAppletResource::GetSharedMemoryHandle)}
|
||||
}) {}
|
||||
|
||||
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<type::KSharedMemory>(hidSharedMemory));
|
||||
}
|
||||
}
|
41
app/src/main/cpp/skyline/kernel/services/hid/hid.h
Normal file
41
app/src/main/cpp/skyline/kernel/services/hid/hid.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/services/base_service.h>
|
||||
#include <kernel/services/serviceman.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
|
||||
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<type::KSharedMemory> 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<IAppletResource> 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);
|
||||
};
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
#include "serviceman.h"
|
||||
#include "../types/KProcess.h"
|
||||
#include <kernel/types/KProcess.h>
|
||||
#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<am::IDebugFunctions>(state, *this);
|
||||
break;
|
||||
case Service::hid:
|
||||
serviceMap[serviceType] = std::make_shared<hid::hid>(state, *this);
|
||||
break;
|
||||
case Service::hid_IAppletResource:
|
||||
serviceMap[serviceType] = std::make_shared<hid::IAppletResource>(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<type::KSession>(GetService(serviceType), serviceType)->handle;
|
||||
return state.thisProcess->NewHandle<type::KSession>(GetService(serviceType), serviceType).handle;
|
||||
}
|
||||
|
||||
void ServiceManager::NewService(const Service serviceType, type::KSession &session, ipc::IpcResponse &response) {
|
||||
std::shared_ptr<BaseService> 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<type::KSession>(serviceObject, serviceType)->handle);
|
||||
response.moveHandles.push_back(state.thisProcess->NewHandle<type::KSession>(serviceObject, serviceType).handle);
|
||||
state.logger->Write(Logger::Debug, "Service has been registered: \"{}\"", serviceObject->getName());
|
||||
return serviceObject;
|
||||
}
|
||||
|
||||
void ServiceManager::CloseSession(const handle_t handle) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nce.h"
|
||||
#include <nce.h>
|
||||
#include <kernel/types/KSession.h>
|
||||
#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<BaseService> NewService(const Service serviceType, type::KSession &session, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Closes an existing session to a service
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "sys.h"
|
||||
#include "../../types/KProcess.h"
|
||||
#include <kernel/types/KProcess.h>
|
||||
|
||||
namespace skyline::kernel::service::set {
|
||||
sys::sys(const DeviceState &state, ServiceManager& manager) : BaseService(state, manager, false, Service::set_sys, {{0x3, SFunc(sys::GetFirmwareVersion)}}) {}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../base_service.h"
|
||||
#include "../serviceman.h"
|
||||
#include <kernel/services/base_service.h>
|
||||
#include <kernel/services/serviceman.h>
|
||||
|
||||
namespace skyline::kernel::service::set {
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../base_service.h"
|
||||
#include "../serviceman.h"
|
||||
#include <kernel/services/base_service.h>
|
||||
#include <kernel/services/serviceman.h>
|
||||
|
||||
namespace skyline::kernel::service::sm {
|
||||
/**
|
||||
|
@ -1,50 +1,47 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <syslog.h>
|
||||
#include <utility>
|
||||
#include "svc.h"
|
||||
#include "../os.h"
|
||||
#include <os.h>
|
||||
|
||||
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<u64>(static_cast<u64>(addr / PAGE_SIZE) * PAGE_SIZE),
|
||||
.size = static_cast<u64>(-constant::BaseSize + 1),
|
||||
.type = static_cast<u64>(memory::Type::Unmapped),
|
||||
};
|
||||
}
|
||||
state.thisProcess->WriteMemory<memory::MemoryInfo>(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<u8>(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<const unsigned int &>(state.nce->GetRegister(Wreg::W0)));
|
||||
if (object->handleType == type::KType::KThread)
|
||||
if (object->objectType == type::KType::KThread)
|
||||
std::static_pointer_cast<type::KThread>(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::nanoseconds>(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<type::KThread>(static_cast<handle_t>(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<type::KSharedMemory>(static_cast<handle_t>(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<handle_t>(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<type::KThread>(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<handle_t> 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<type::KSyncObject>(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);
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ipc.h"
|
||||
#include "../common.h"
|
||||
#include <common.h>
|
||||
|
||||
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
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common.h"
|
||||
#include <common.h>
|
||||
|
||||
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) {}
|
||||
};
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
#include "KPrivateMemory.h"
|
||||
#include "../../nce.h"
|
||||
#include "../../os.h"
|
||||
#include <android/sharedmem.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <nce.h>
|
||||
|
||||
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<u64>(permission.Get());
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapPrivateFunc), fregs, ownerPid);
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapPrivateFunc), fregs, pid);
|
||||
if (reinterpret_cast<void *>(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<u64>(munmap(reinterpret_cast<void *>(address), size));
|
||||
}
|
||||
|
||||
u64 RemapPrivateFunc(u64 address, size_t oldSize, size_t size) {
|
||||
return reinterpret_cast<u64>(mremap(reinterpret_cast<void *>(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<void *>(RemapPrivateFunc), fregs, ownerPid);
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapPrivateFunc), fregs, owner);
|
||||
if (reinterpret_cast<void *>(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<u64>(newPerms.Get());
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionPrivateFunc), fregs, ownerPid);
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionPrivateFunc), fregs, owner);
|
||||
if (static_cast<int>(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<u64>(munmap(reinterpret_cast<void *>(address), size));
|
||||
}
|
||||
|
||||
KPrivateMemory::~KPrivateMemory() {
|
||||
try {
|
||||
user_pt_regs fregs = {0};
|
||||
fregs.regs[0] = address;
|
||||
fregs.regs[1] = size;
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(UnmapPrivateFunc), fregs, owner);
|
||||
} catch (const std::exception&) {}
|
||||
}
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../memory.h"
|
||||
#include <memory.h>
|
||||
#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();
|
||||
};
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
#include "KProcess.h"
|
||||
#include "../../nce.h"
|
||||
#include <nce.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
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<KPrivateMemory>(0, 0, PAGE_SIZE, memory::Permission(true, true, false), memory::Type::ThreadLocal);
|
||||
auto tlsMem = NewHandle<KPrivateMemory>(mainThread, 0, 0, PAGE_SIZE, memory::Permission(true, true, false), memory::Type::ThreadLocal).item;
|
||||
memoryMap[tlsMem->address] = tlsMem;
|
||||
tlsPages.push_back(std::make_shared<TlsPage>(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<KThread>(pid, entryPoint, 0, stackBase + stackSize, GetTlsSlot(true), constant::DefaultPriority, this);
|
||||
threadMap[pid] = NewHandle<KThread>(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<void *>(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<void *>(stackTop), CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO, nullptr); // NOLINT(hicpp-signed-bitwise)
|
||||
return static_cast<u64>(pid);
|
||||
}
|
||||
|
||||
@ -75,8 +70,9 @@ namespace skyline::kernel::type {
|
||||
state.nce->ExecuteFunction((void *) CreateThreadFunc, fregs, mainThread);
|
||||
auto pid = static_cast<pid_t>(fregs.regs[0]);
|
||||
if (pid == -1)
|
||||
throw exception(fmt::format("Cannot create thread: Address: {}, Stack Top: {}", entryPoint, stackTop));
|
||||
threadMap[pid] = NewHandle<KThread>(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<KThread>(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<KPrivateMemory> KProcess::MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region) {
|
||||
auto item = NewHandle<KPrivateMemory>(address, 0, size, perms, type);
|
||||
memoryMap[item->address] = item;
|
||||
memoryRegionMap[region] = item;
|
||||
return item;
|
||||
int KProcess::GetMemoryFd() const {
|
||||
return memFd;
|
||||
}
|
||||
|
||||
KProcess::HandleOut<KPrivateMemory> KProcess::MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region) {
|
||||
auto mem = NewHandle<KPrivateMemory>(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;
|
||||
}
|
||||
}
|
||||
|
@ -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<pid_t, std::shared_ptr<KThread>> threadMap; //!< A mapping from a PID to it's corresponding KThread object
|
||||
std::vector<std::shared_ptr<TlsPage>> 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<typename objectClass>
|
||||
struct HandleOut {
|
||||
std::shared_ptr<objectClass> 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<KPrivateMemory> MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region);
|
||||
HandleOut<KPrivateMemory> 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<typename objectClass, typename ...objectArgs>
|
||||
std::shared_ptr<objectClass> NewHandle(objectArgs... args) {
|
||||
std::shared_ptr<objectClass> item = std::make_shared<objectClass>(handleIndex, mainThread, state, args...);
|
||||
handleTable[handleIndex++] = std::static_pointer_cast<KObject>(item);
|
||||
return item;
|
||||
HandleOut<objectClass> NewHandle(objectArgs... args) {
|
||||
std::shared_ptr<objectClass> item;
|
||||
if constexpr (std::is_same<objectClass, KThread>())
|
||||
item = std::make_shared<objectClass>(state, handleIndex, args...);
|
||||
else
|
||||
item = std::make_shared<objectClass>(state, args...);
|
||||
handleTable[handleIndex] = std::static_pointer_cast<KObject>(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<typename objectClass>
|
||||
handle_t InsertItem(std::shared_ptr<objectClass> item) {
|
||||
handleTable[handleIndex] = std::static_pointer_cast<KObject>(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<typename objectClass>
|
||||
std::shared_ptr<objectClass> GetHandle(handle_t handle) {
|
||||
KType objectType;
|
||||
if(std::is_same<objectClass, KThread>::value)
|
||||
if constexpr(std::is_same<objectClass, KThread>())
|
||||
objectType = KType::KThread;
|
||||
else if(std::is_same<objectClass, KProcess>::value)
|
||||
else if constexpr(std::is_same<objectClass, KProcess>())
|
||||
objectType = KType::KProcess;
|
||||
else if(std::is_same<objectClass, KSharedMemory>::value)
|
||||
else if constexpr(std::is_same<objectClass, KSharedMemory>())
|
||||
objectType = KType::KSharedMemory;
|
||||
else if(std::is_same<objectClass, KPrivateMemory>::value)
|
||||
else if constexpr(std::is_same<objectClass, KPrivateMemory>())
|
||||
objectType = KType::KPrivateMemory;
|
||||
else if(std::is_same<objectClass, KSession>::value)
|
||||
else if constexpr(std::is_same<objectClass, KSession>())
|
||||
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<objectClass>(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));
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common.h"
|
||||
#include "../services/base_service.h"
|
||||
#include <common.h>
|
||||
#include <kernel/services/base_service.h>
|
||||
#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<service::BaseService> &serviceObject, const service::Service &serviceType) : serviceObject(serviceObject), serviceType(serviceType), KSyncObject(handle, pid, state, KType::KSession) {}
|
||||
KSession(const DeviceState &state, std::shared_ptr<service::BaseService> &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)
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "KSharedMemory.h"
|
||||
#include "../../nce.h"
|
||||
#include "../../os.h"
|
||||
#include <nce.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -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<u64>(mmap(reinterpret_cast<void *>(address), size, static_cast<int>(perms), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast<int>(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<u64 >(localPermission.Get());
|
||||
else
|
||||
fregs.regs[2] = static_cast<u64>(remotePermission.Get());
|
||||
fregs.regs[3] = static_cast<u64>(fd);
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapFunc), fregs, process);
|
||||
if (reinterpret_cast<void *>(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<u64>(ownerPid ? remotePermission.Get() : localPermission.Get()), static_cast<u64>(fd));
|
||||
if (this->address == reinterpret_cast<u64>(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<u64>(pid ? remotePermission.Get() : localPermission.Get()), static_cast<u64>(fd));
|
||||
if (kaddress == reinterpret_cast<u64>(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<u64 >(localPermission.Get());
|
||||
else
|
||||
fregs.regs[2] = static_cast<u64>(remotePermission.Get());
|
||||
fregs.regs[3] = static_cast<u64>(fd);
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapSharedFunc), fregs, process);
|
||||
if (reinterpret_cast<void *>(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<u64>(munmap(reinterpret_cast<void *>(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<void *>(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<void *>(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<u64>(mremap(reinterpret_cast<void *>(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<void *>(RemapFunc), fregs, process);
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapSharedFunc), fregs, process);
|
||||
if (reinterpret_cast<void *>(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<u64>(MAP_FAILED))
|
||||
if (RemapSharedFunc(kaddress, ksize, newSize) == reinterpret_cast<u64>(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<u64>(mprotect(reinterpret_cast<void *>(address), size, static_cast<int>(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<u64>(newPerms.Get());
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionFunc), fregs, process);
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionSharedFunc), fregs, process);
|
||||
if (static_cast<int>(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<void *>(address), size, newPerms.Get()) == -1)
|
||||
if ((local && owner == 0) || (!local && owner != 0))
|
||||
if (mprotect(reinterpret_cast<void *>(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<u64>(remotePermission.Get());
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionFunc), fregs, pid);
|
||||
if (static_cast<int>(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<u64>(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;
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../memory.h"
|
||||
#include <memory.h>
|
||||
#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<pid_t, ProcInf> 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();
|
||||
};
|
||||
}
|
||||
|
@ -1,20 +1,12 @@
|
||||
#include "KSyncObject.h"
|
||||
#include "../../os.h"
|
||||
#include <os.h>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../common.h"
|
||||
#include <common.h>
|
||||
#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<pid_t> 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();
|
||||
};
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
#include <sys/resource.h>
|
||||
#include "KThread.h"
|
||||
#include "KProcess.h"
|
||||
#include "../../nce.h"
|
||||
#include <nce.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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<handle_t> 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<std::shared_ptr<KSyncObject>> 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.
|
||||
|
110
app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp
Normal file
110
app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
#include "KTransferMemory.h"
|
||||
#include <nce.h>
|
||||
#include <os.h>
|
||||
|
||||
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<u64>(mmap(reinterpret_cast<void *>(address), size, static_cast<int>(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<u64 >(permission.Get());
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapTransferFunc), fregs, pid);
|
||||
if (reinterpret_cast<void *>(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<u64>(permission.Get()));
|
||||
if (reinterpret_cast<void *>(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<u64>(munmap(reinterpret_cast<void *>(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<u64 >(permission.Get());
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapTransferFunc), fregs, process);
|
||||
if (reinterpret_cast<void *>(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<u64>(permission.Get()));
|
||||
if (reinterpret_cast<void *>(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<u8> 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<void *>(cAddress), address, copySz);
|
||||
} else if (!process && owner) {
|
||||
state.os->processMap.at(owner)->ReadMemory(reinterpret_cast<void *>(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<u64 >(permission.Get());
|
||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(UnmapTransferFunc), fregs, owner);
|
||||
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
|
||||
throw exception("An error occurred while unmapping transfer memory in child process");
|
||||
} else {
|
||||
if (reinterpret_cast<void *>(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<u64>(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<void *>(UnmapTransferFunc), fregs, owner);
|
||||
} catch (const std::exception &) {
|
||||
}
|
||||
} else
|
||||
UnmapTransferFunc(cAddress, cSize);
|
||||
}
|
||||
};
|
49
app/src/main/cpp/skyline/kernel/types/KTransferMemory.h
Normal file
49
app/src/main/cpp/skyline/kernel/types/KTransferMemory.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory.h>
|
||||
#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();
|
||||
};
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "../os.h"
|
||||
#include <os.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
|
||||
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<kernel::type::KProcess> process, const DeviceState &state) = 0;
|
||||
};
|
||||
}
|
||||
|
@ -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<kernel::type::KProcess> 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<u8 *>(constant::BaseAddr), header.text.offset, header.text.size);
|
||||
ReadOffset(reinterpret_cast<u8 *>(constant::BaseAddr + header.text.size), header.ro.offset, header.ro.size);
|
||||
ReadOffset(reinterpret_cast<u8 *>(constant::BaseAddr + header.text.size + header.ro.size), header.data.offset, header.data.size);
|
||||
std::vector<u8> text(header.text.size);
|
||||
std::vector<u8> rodata(header.ro.size);
|
||||
std::vector<u8> 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<instr::Svc *>(address + iter);
|
||||
auto instrMrs = reinterpret_cast<instr::Mrs *>(address + iter);
|
||||
for (auto address = reinterpret_cast<u32*>(text.data()); address <= reinterpret_cast<u32*>(text.data() + header.text.size); address++) {
|
||||
auto instrSvc = reinterpret_cast<instr::Svc *>(address);
|
||||
auto instrMrs = reinterpret_cast<instr::Mrs *>(address);
|
||||
|
||||
if (instrSvc->Verify()) {
|
||||
instr::Brk brk(static_cast<u16>(instrSvc->value));
|
||||
address[iter] = *reinterpret_cast<u32 *>(&brk);
|
||||
*address = *reinterpret_cast<u32 *>(&brk);
|
||||
} else if (instrMrs->Verify() && instrMrs->srcReg == constant::TpidrroEl0) {
|
||||
instr::Brk brk(static_cast<u16>(constant::SvcLast + 1 + instrMrs->dstReg));
|
||||
address[iter] = *reinterpret_cast<u32 *>(&brk);
|
||||
*address = *reinterpret_cast<u32 *>(&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);
|
||||
}
|
||||
}
|
||||
|
@ -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<kernel::type::KProcess> process, const DeviceState &state);
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -1,8 +1,7 @@
|
||||
#include <sched.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/elf.h>
|
||||
#include "os.h"
|
||||
#include "nce.h"
|
||||
#include <os.h>
|
||||
|
||||
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<u16>(instr.value), currPid);
|
||||
if (state->thisThread->status == kernel::type::KThread::ThreadStatus::Waiting)
|
||||
state->os->SvcHandler(static_cast<u16>(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<u32>(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::nanoseconds>(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<u64>(func);
|
||||
funcRegs.sp = backupRegs.sp;
|
||||
funcRegs.regs[static_cast<uint>(Xreg::X30)] = reinterpret_cast<u64>(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<uint>(Xreg::X30)] = reinterpret_cast<u64>(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<kernel::type::KSharedMemory> 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<kernel::type::KSharedMemory>(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;
|
||||
}
|
||||
}
|
||||
|
@ -41,8 +41,6 @@ namespace skyline {
|
||||
instr::Brk ReadBrk(u64 address, pid_t pid = 0) const;
|
||||
|
||||
public:
|
||||
std::map<memory::Region, std::shared_ptr<kernel::type::KSharedMemory>> 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<kernel::type::KSharedMemory> 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();
|
||||
};
|
||||
}
|
||||
|
@ -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> &logger, std::shared_ptr<Settings> &settings) : state(this, thisProcess, thisThread, std::make_shared<NCE>(), 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<type::KProcess> process = std::make_shared<kernel::type::KProcess>(0, pid, state, address, reinterpret_cast<u64>(stack), stackSize);
|
||||
threadMap[pid] = process;
|
||||
std::shared_ptr<type::KProcess> process = std::make_shared<kernel::type::KProcess>(state, pid, address, reinterpret_cast<u64>(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<kernel::type::KSharedMemory> 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<kernel::type::KSharedMemory>(state, 0, address, size, kernelPermission, remotePermission, type);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ namespace skyline::kernel {
|
||||
DeviceState state; //!< The state of the device
|
||||
|
||||
public:
|
||||
std::unordered_map<pid_t, std::shared_ptr<type::KProcess>> threadMap; //!< A mapping from a threat's PID to it's KProcess object
|
||||
std::unordered_map<pid_t, std::shared_ptr<type::KProcess>> processMap; //!< A mapping from a threat's PID to it's KProcess object
|
||||
std::vector<pid_t> processVec; //!< A vector of all processes by their main thread's PID
|
||||
std::shared_ptr<type::KProcess> thisProcess; //!< The corresponding KProcess object of the process that's called an SVC
|
||||
std::shared_ptr<type::KThread> 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<kernel::type::KSharedMemory> MapSharedKernel(const u64 address, const size_t size, const memory::Permission kernelPermission, const memory::Permission remotePermission, const memory::Type type);
|
||||
};
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user