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:
◱ PixelyIon 2019-10-13 13:34:47 +05:30
parent f7effe86ae
commit ec71735ece
44 changed files with 683 additions and 354 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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 {
/**

View File

@ -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 {
/**

View File

@ -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;

View File

@ -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)));
}
}

View File

@ -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 {
/**

View 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));
}
}

View 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);
};
}

View File

@ -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) {

View File

@ -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

View File

@ -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)}}) {}

View File

@ -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 {
/**

View File

@ -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 {
/**

View File

@ -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);

View File

@ -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

View File

@ -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;
}
};
}

View File

@ -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) {}
};
}

View File

@ -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&) {}
}
};

View File

@ -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();
};
}

View File

@ -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 &region : 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 &region : memoryRegionMap)
sharedSize += region.second->size;
return sharedSize;
}
}

View File

@ -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));
}

View File

@ -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)

View File

@ -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;

View File

@ -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();
};
}

View File

@ -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;
}
}
}

View File

@ -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();
};
}

View File

@ -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);
}

View File

@ -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.

View 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);
}
};

View 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();
};
}

View File

@ -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;
};
}

View File

@ -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);
}
}

View File

@ -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);
};
}

View File

@ -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);

View File

@ -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 &region : memoryRegionMap)
sharedSize += region.second->size;
return sharedSize;
}
}

View File

@ -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();
};
}

View File

@ -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);
}
}

View File

@ -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);
};
}

View File

@ -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