Refactor Common

This refactored common by:
* Moving out as many constants to class/function local scopes from being declared in `common`
* Spacing out common and any function to which a constant was moved out to
* Fixing comments here and there
In addition, some naming inconsistencies were fixed as well.
This commit is contained in:
◱ PixelyIon 2020-03-25 23:29:37 +05:30 committed by ◱ PixelyIon
parent 618353c1fa
commit 773ee25e5a
44 changed files with 906 additions and 813 deletions

View File

@ -1621,7 +1621,7 @@
<inspection_tool class="NotInHierarchyMessage" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="NotInHierarchyMessage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NotInitializedVariable" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="NotInitializedVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NotReleasedValue" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="NotReleasedValue" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NotSuperclass" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="NotSuperclass" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NotVisibleClass" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="NotVisibleClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NotifyCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="NotifyCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NotifyWithoutCorrespondingWait" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="NotifyWithoutCorrespondingWait" enabled="true" level="WARNING" enabled_by_default="true" />
@ -1644,7 +1644,7 @@
<inspection_tool class="NumericToString" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="NumericToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCDFA" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="OCDFA" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCGlobalUnused" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="OCGlobalUnused" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCInconsistentNaming" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> <inspection_tool class="OCInconsistentNaming" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="OCLegacyObjCLiteral" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="OCLegacyObjCLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCLoopDoesntUseConditionVariable" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="OCLoopDoesntUseConditionVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCNotLocalizedString" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="OCNotLocalizedString" enabled="true" level="WARNING" enabled_by_default="true" />

View File

@ -29,7 +29,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env,
std::signal(SIGABRT, signalHandler); std::signal(SIGABRT, signalHandler);
std::signal(SIGFPE, signalHandler); std::signal(SIGFPE, signalHandler);
setpriority(PRIO_PROCESS, static_cast<id_t>(getpid()), skyline::constant::PriorityAn.second); setpriority(PRIO_PROCESS, static_cast<id_t>(getpid()), skyline::constant::AndroidPriority.second);
auto jvmManager = std::make_shared<skyline::JvmManager>(env, instance); auto jvmManager = std::make_shared<skyline::JvmManager>(env, instance);
auto settings = std::make_shared<skyline::Settings>(preferenceFd); auto settings = std::make_shared<skyline::Settings>(preferenceFd);

View File

@ -3,41 +3,43 @@
#include <oboe/Oboe.h> #include <oboe/Oboe.h>
#include <common.h> #include <common.h>
namespace skyline::audio { namespace skyline {
/**
* @brief The available PCM stream formats
*/
enum class PcmFormat : u8 {
Invalid = 0, //!< An invalid PCM format
Int8 = 1, //!< 8 bit integer PCM
Int16 = 2, //!< 16 bit integer PCM
Int24 = 3, //!< 24 bit integer PCM
Int32 = 4, //!< 32 bit integer PCM
PcmFloat = 5, //!< Floating point PCM
AdPcm = 6 //!< Adaptive differential PCM
};
/**
* @brief The state of an audio track
*/
enum class AudioOutState : u8 {
Started = 0, //!< Stream is started and is playing
Stopped = 1, //!< Stream is stopped, there are no samples left to play
Paused = 2 //!< Stream is paused, some samples may not have been played yet
};
/**
* @brief Stores various information about pushed buffers
*/
struct BufferIdentifier {
u64 tag; //!< The tag of the buffer
u64 finalSample; //!< The final sample this buffer will be played in
bool released; //!< Whether the buffer has been released
};
namespace constant { namespace constant {
constexpr int SampleRate = 48000; //!< The sampling rate to use for the oboe audio output constexpr auto SampleRate = 48000; //!< The sampling rate to use for the oboe audio output
constexpr int ChannelCount = 2; //!< The amount of channels to use for the oboe audio output constexpr auto ChannelCount = 2; //!< The amount of channels to use for the oboe audio output
constexpr oboe::AudioFormat PcmFormat = oboe::AudioFormat::I16; //!< The pcm data format to use for the oboe audio output constexpr auto PcmFormat = oboe::AudioFormat::I16; //!< The pcm data format to use for the oboe audio output
}; };
namespace audio {
/**
* @brief The available PCM stream formats
*/
enum class PcmFormat : u8 {
Invalid = 0, //!< An invalid PCM format
Int8 = 1, //!< 8 bit integer PCM
Int16 = 2, //!< 16 bit integer PCM
Int24 = 3, //!< 24 bit integer PCM
Int32 = 4, //!< 32 bit integer PCM
PcmFloat = 5, //!< Floating point PCM
AdPcm = 6 //!< Adaptive differential PCM
};
/**
* @brief The state of an audio track
*/
enum class AudioOutState : u8 {
Started = 0, //!< Stream is started and is playing
Stopped = 1, //!< Stream is stopped, there are no samples left to play
Paused = 2 //!< Stream is paused, some samples may not have been played yet
};
/**
* @brief Stores various information about pushed buffers
*/
struct BufferIdentifier {
u64 tag; //!< The tag of the buffer
u64 finalSample; //!< The final sample this buffer will be played in
bool released; //!< Whether the buffer has been released
};
}
} }

View File

@ -10,7 +10,7 @@ namespace skyline::audio {
i32 d; i32 d;
}; };
constexpr std::array<LutEntry, 128> curveLut0 = {{ constexpr std::array<LutEntry, 128> CurveLut0 = {{
{6600, 19426, 6722, 3}, {6479, 19424, 6845, 9}, {6359, 19419, 6968, 15}, {6239, 19412, 7093, 22}, {6600, 19426, 6722, 3}, {6479, 19424, 6845, 9}, {6359, 19419, 6968, 15}, {6239, 19412, 7093, 22},
{6121, 19403, 7219, 28}, {6004, 19391, 7345, 34}, {5888, 19377, 7472, 41}, {5773, 19361, 7600, 48}, {6121, 19403, 7219, 28}, {6004, 19391, 7345, 34}, {5888, 19377, 7472, 41}, {5773, 19361, 7600, 48},
{5659, 19342, 7728, 55}, {5546, 19321, 7857, 62}, {5434, 19298, 7987, 69}, {5323, 19273, 8118, 77}, {5659, 19342, 7728, 55}, {5546, 19321, 7857, 62}, {5434, 19298, 7987, 69}, {5323, 19273, 8118, 77},
@ -44,7 +44,7 @@ namespace skyline::audio {
{48, 7600, 19361, 5773}, {41, 7472, 19377, 5888}, {34, 7345, 19391, 6004}, {28, 7219, 19403, 6121}, {48, 7600, 19361, 5773}, {41, 7472, 19377, 5888}, {34, 7345, 19391, 6004}, {28, 7219, 19403, 6121},
{22, 7093, 19412, 6239}, {15, 6968, 19419, 6359}, {9, 6845, 19424, 6479}, {3, 6722, 19426, 6600}}}; {22, 7093, 19412, 6239}, {15, 6968, 19419, 6359}, {9, 6845, 19424, 6479}, {3, 6722, 19426, 6600}}};
constexpr std::array<LutEntry, 128> curveLut1 = {{ constexpr std::array<LutEntry, 128> CurveLut1 = {{
{-68, 32639, 69, -5}, {-200, 32630, 212, -15}, {-328, 32613, 359, -26}, {-450, 32586, 512, -36}, {-68, 32639, 69, -5}, {-200, 32630, 212, -15}, {-328, 32613, 359, -26}, {-450, 32586, 512, -36},
{-568, 32551, 669, -47}, {-680, 32507, 832, -58}, {-788, 32454, 1000, -69}, {-891, 32393, 1174, -80}, {-568, 32551, 669, -47}, {-680, 32507, 832, -58}, {-788, 32454, 1000, -69}, {-891, 32393, 1174, -80},
{-990, 32323, 1352, -92}, {-1084, 32244, 1536, -103}, {-1173, 32157, 1724, -115}, {-1258, 32061, 1919, -128}, {-990, 32323, 1352, -92}, {-1084, 32244, 1536, -103}, {-1173, 32157, 1724, -115}, {-1258, 32061, 1919, -128},
@ -78,7 +78,7 @@ namespace skyline::audio {
{-80, 1174, 32393, -891}, {-69, 1000, 32454, -788}, {-58, 832, 32507, -680}, {-47, 669, 32551, -568}, {-80, 1174, 32393, -891}, {-69, 1000, 32454, -788}, {-58, 832, 32507, -680}, {-47, 669, 32551, -568},
{-36, 512, 32586, -450}, {-26, 359, 32613, -328}, {-15, 212, 32630, -200}, {-5, 69, 32639, -68}}}; {-36, 512, 32586, -450}, {-26, 359, 32613, -328}, {-15, 212, 32630, -200}, {-5, 69, 32639, -68}}};
constexpr std::array<LutEntry, 128> curveLut2 = {{ constexpr std::array<LutEntry, 128> CurveLut2 = {{
{3195, 26287, 3329, -32}, {3064, 26281, 3467, -34}, {2936, 26270, 3608, -38}, {2811, 26253, 3751, -42}, {3195, 26287, 3329, -32}, {3064, 26281, 3467, -34}, {2936, 26270, 3608, -38}, {2811, 26253, 3751, -42},
{2688, 26230, 3897, -46}, {2568, 26202, 4046, -50}, {2451, 26169, 4199, -54}, {2338, 26130, 4354, -58}, {2688, 26230, 3897, -46}, {2568, 26202, 4046, -50}, {2451, 26169, 4199, -54}, {2338, 26130, 4354, -58},
{2227, 26085, 4512, -63}, {2120, 26035, 4673, -67}, {2015, 25980, 4837, -72}, {1912, 25919, 5004, -76}, {2227, 26085, 4512, -63}, {2120, 26035, 4673, -67}, {2015, 25980, 4837, -72}, {1912, 25919, 5004, -76},
@ -118,14 +118,13 @@ namespace skyline::audio {
std::vector<i16> outputBuffer(static_cast<size_t>(outputSize)); std::vector<i16> outputBuffer(static_cast<size_t>(outputSize));
const std::array<skyline::audio::LutEntry, 128> &lut = [step] { const std::array<skyline::audio::LutEntry, 128> &lut = [step] {
if (step > 0xaaaa) { if (step > 0xaaaa)
return curveLut0; return CurveLut0;
} else if (step <= 0x8000) { else if (step <= 0x8000)
return curveLut1; return CurveLut1;
} else { else
return curveLut2; return CurveLut2;
} }();
} ();
for (uint outIndex = 0, inIndex = 0; outIndex < outputBuffer.size(); outIndex += channelCount) { for (uint outIndex = 0, inIndex = 0; outIndex < outputBuffer.size(); outIndex += channelCount) {
uint lutIndex = (fraction >> 8) << 2; uint lutIndex = (fraction >> 8) << 2;

View File

@ -11,6 +11,7 @@ namespace skyline {
for (int i = 0; i < 1000; ++i) { for (int i = 0; i < 1000; ++i) {
if (!flag.test_and_set(std::memory_order_acquire)) if (!flag.test_and_set(std::memory_order_acquire))
return; return;
asm volatile("yield"); asm volatile("yield");
} }
sched_yield(); sched_yield();
@ -21,14 +22,17 @@ namespace skyline {
auto none = Group::None; auto none = Group::None;
constexpr u64 timeout = 100; // The timeout in ns constexpr u64 timeout = 100; // The timeout in ns
auto end = utils::GetTimeNs() + timeout; auto end = utils::GetTimeNs() + timeout;
while (true) { while (true) {
if (next == group) { if (next == group) {
if (flag == group) { if (flag == group) {
std::lock_guard lock(mtx); std::lock_guard lock(mtx);
if (flag == group) { if (flag == group) {
auto groupT = group; auto groupT = group;
next.compare_exchange_strong(groupT, Group::None); next.compare_exchange_strong(groupT, Group::None);
num++; num++;
return; return;
} }
} else { } else {
@ -36,6 +40,7 @@ namespace skyline {
} }
} else if (flag == group && (next == Group::None || utils::GetTimeNs() >= end)) { } else if (flag == group && (next == Group::None || utils::GetTimeNs() >= end)) {
std::lock_guard lock(mtx); std::lock_guard lock(mtx);
if (flag == group) { if (flag == group) {
num++; num++;
return; return;
@ -43,6 +48,7 @@ namespace skyline {
} else { } else {
next.compare_exchange_weak(none, group); next.compare_exchange_weak(none, group);
} }
none = Group::None; none = Group::None;
asm volatile("yield"); asm volatile("yield");
} }
@ -50,38 +56,44 @@ namespace skyline {
void GroupMutex::unlock() { void GroupMutex::unlock() {
std::lock_guard lock(mtx); std::lock_guard lock(mtx);
if (!--num) if (!--num)
flag.exchange(next); flag.exchange(next);
} }
Settings::Settings(const int preferenceFd) { Settings::Settings(const int preferenceFd) {
tinyxml2::XMLDocument pref; tinyxml2::XMLDocument pref;
if (pref.LoadFile(fdopen(preferenceFd, "r"))) if (pref.LoadFile(fdopen(preferenceFd, "r")))
throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr())); throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
tinyxml2::XMLElement *elem = pref.LastChild()->FirstChild()->ToElement(); tinyxml2::XMLElement *elem = pref.LastChild()->FirstChild()->ToElement();
while (elem) { while (elem) {
switch (elem->Value()[0]) { switch (elem->Value()[0]) {
case 's': case 's':
stringMap[elem->FindAttribute("name")->Value()] = elem->GetText(); stringMap[elem->FindAttribute("name")->Value()] = elem->GetText();
break; break;
case 'b': case 'b':
boolMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute( boolMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->BoolValue();
"value")->BoolValue();
break; break;
case 'i': case 'i':
intMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute( intMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->IntValue();
"value")->IntValue();
break; break;
default: default:
syslog(LOG_ALERT, "Settings type is missing: %s for %s", elem->Value(), syslog(LOG_ALERT, "Settings type is missing: %s for %s", elem->Value(), elem->FindAttribute("name")->Value());
elem->FindAttribute("name")->Value());
break; break;
}; };
if (elem->NextSibling()) if (elem->NextSibling())
elem = elem->NextSibling()->ToElement(); elem = elem->NextSibling()->ToElement();
else else
break; break;
} }
pref.Clear(); pref.Clear();
} }
@ -100,6 +112,7 @@ namespace skyline {
void Settings::List(const std::shared_ptr<Logger> &logger) { void Settings::List(const std::shared_ptr<Logger> &logger) {
for (auto &iter : stringMap) for (auto &iter : stringMap)
logger->Info("Key: {}, Value: {}, Type: String", iter.first, GetString(iter.first)); logger->Info("Key: {}, Value: {}, Type: String", iter.first, GetString(iter.first));
for (auto &iter : boolMap) for (auto &iter : boolMap)
logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first)); logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first));
} }
@ -115,15 +128,18 @@ namespace skyline {
void Logger::WriteHeader(const std::string &str) { void Logger::WriteHeader(const std::string &str) {
syslog(LOG_ALERT, "%s", str.c_str()); syslog(LOG_ALERT, "%s", str.c_str());
logFile << "0|" << str << "\n"; logFile << "0|" << str << "\n";
logFile.flush(); logFile.flush();
} }
void Logger::Write(const LogLevel level, std::string str) { void Logger::Write(const LogLevel level, std::string str) {
syslog(levelSyslog[static_cast<u8>(level)], "%s", str.c_str()); syslog(levelSyslog[static_cast<u8>(level)], "%s", str.c_str());
for (auto &character : str) for (auto &character : str)
if (character == '\n') if (character == '\n')
character = '\\'; character = '\\';
logFile << "1|" << levelStr[static_cast<u8>(level)] << "|" << str << "\n"; logFile << "1|" << levelStr[static_cast<u8>(level)] << "|" << str << "\n";
logFile.flush(); logFile.flush();
} }

View File

@ -19,47 +19,20 @@
#include "nce/guest_common.h" #include "nce/guest_common.h"
namespace skyline { namespace skyline {
using handle_t = u32; //!< The type of a kernel handle using KHandle = u32; //!< The type of a kernel handle
namespace constant { namespace constant {
// Memory // Memory
constexpr u64 BaseAddress = 0x8000000; //!< The address space base constexpr auto BaseAddress = 0x8000000; //!< The address space base
constexpr u64 TotalPhyMem = 0xF8000000; // ~4 GB of RAM constexpr auto DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB
constexpr size_t DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB
constexpr size_t HeapSizeDiv = 0x200000; //!< The amount heap size has to be divisible by
constexpr size_t DefHeapSize = HeapSizeDiv; //!< The default amount of heap
constexpr size_t TlsSlotSize = 0x200; //!< The size of a single TLS slot
constexpr u8 TlsSlots = PAGE_SIZE / TlsSlotSize; //!< The amount of TLS slots in a single page
// Loader
constexpr u32 NroMagic = 0x304F524E; //!< "NRO0" in reverse, this is written at the start of every NRO file
// NCE
constexpr u8 NumRegs = 30; //!< The amount of registers that ARMv8 has
constexpr u32 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_EL0 in MRS
constexpr u32 CntfrqEl0 = 0x5F00; //!< ID of CNTFRQ_EL0 in MRS
constexpr u32 TegraX1Freq = 19200000; //!< The clock frequency of the Tegra X1 (19.2 MHz)
constexpr u32 CntpctEl0 = 0x5F01; //!< ID of CNTPCT_EL0 in MRS
constexpr u32 CntvctEl0 = 0x5F02; //!< ID of CNTVCT_EL0 in MRS
// Kernel // Kernel
constexpr u64 MaxSyncHandles = 0x40; //!< The total amount of handles that can be passed to WaitSynchronization constexpr std::pair<int8_t, int8_t> AndroidPriority = {19, -8}; //!< The range of priority for Android
constexpr handle_t BaseHandleIndex = 0xD000; // The index of the base handle constexpr std::pair<u8, u8> SwitchPriority = {0, 63}; //!< The range of priority for the Nintendo Switch
constexpr handle_t ThreadSelf = 0xFFFF8000; //!< This is the handle used by threads to refer to themselves // Display
constexpr u8 DefaultPriority = 44; //!< The default priority of a process constexpr auto HandheldResolutionW = 1280; //!< The width component of the handheld resolution
constexpr std::pair<int8_t, int8_t> PriorityAn = {19, -8}; //!< The range of priority for Android, taken from https://medium.com/mindorks/exploring-android-thread-priority-5d0542eebbd1 constexpr auto HandheldResolutionH = 720; //!< The height component of the handheld resolution
constexpr std::pair<u8, u8> PriorityNin = {0, 63}; //!< The range of priority for the Nintendo Switch constexpr auto DockedResolutionW = 1920; //!< The width component of the docked resolution
constexpr u32 MtxOwnerMask = 0xBFFFFFFF; //!< The mask of values which contain the owner of a mutex constexpr auto DockedResolutionH = 1080; //!< The height component of the docked resolution
// IPC
constexpr size_t TlsIpcSize = 0x100; //!< The size of the IPC command buffer in a TLS slot
constexpr u8 PortSize = 0x8; //!< The size of a port name string
constexpr u32 SfcoMagic = 0x4F434653; //!< SFCO in reverse, written to IPC messages
constexpr u32 SfciMagic = 0x49434653; //!< SFCI in reverse, present in received IPC messages
constexpr u64 IpcPaddingSum = 0x10; //!< The sum of the padding surrounding DataPayload
constexpr handle_t BaseVirtualHandleIndex = 0x1; // The index of the base virtual handle
// GPU
constexpr u32 HandheldResolutionW = 1280; //!< The width component of the handheld resolution
constexpr u32 HandheldResolutionH = 720; //!< The height component of the handheld resolution
constexpr u32 DockedResolutionW = 1920; //!< The width component of the docked resolution
constexpr u32 DockedResolutionH = 1080; //!< The height component of the docked resolution
constexpr u32 TokenLength = 0x50; //!< The length of the token on BufferQueue parcels
// Status codes // Status codes
namespace status { namespace status {
constexpr u32 Success = 0x0; //!< "Success" constexpr u32 Success = 0x0; //!< "Success"
@ -98,13 +71,13 @@ namespace skyline {
* @return The current time in nanoseconds * @return The current time in nanoseconds
*/ */
inline u64 GetTimeNs() { inline u64 GetTimeNs() {
constexpr uint64_t NsInSecond = 1000000000; constexpr uint64_t nsInSecond = 1000000000;
static u64 frequency{}; static u64 frequency{};
if (!frequency) if (!frequency)
asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency)); asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency));
u64 ticks; u64 ticks;
asm("MRS %0, CNTVCT_EL0" : "=r"(ticks)); asm("MRS %0, CNTVCT_EL0" : "=r"(ticks));
return ((ticks / frequency) * NsInSecond) + (((ticks % frequency) * NsInSecond + (frequency / 2)) / frequency); return ((ticks / frequency) * nsInSecond) + (((ticks % frequency) * nsInSecond + (frequency / 2)) / frequency);
} }
/** /**

View File

@ -23,12 +23,12 @@ namespace skyline::kernel::ipc {
handleDesc = reinterpret_cast<HandleDescriptor *>(pointer); handleDesc = reinterpret_cast<HandleDescriptor *>(pointer);
pointer += sizeof(HandleDescriptor) + (handleDesc->sendPid ? sizeof(u64) : 0); pointer += sizeof(HandleDescriptor) + (handleDesc->sendPid ? sizeof(u64) : 0);
for (uint index = 0; handleDesc->copyCount > index; index++) { for (uint index = 0; handleDesc->copyCount > index; index++) {
copyHandles.push_back(*reinterpret_cast<handle_t *>(pointer)); copyHandles.push_back(*reinterpret_cast<KHandle *>(pointer));
pointer += sizeof(handle_t); pointer += sizeof(KHandle);
} }
for (uint index = 0; handleDesc->moveCount > index; index++) { for (uint index = 0; handleDesc->moveCount > index; index++) {
moveHandles.push_back(*reinterpret_cast<handle_t *>(pointer)); moveHandles.push_back(*reinterpret_cast<KHandle *>(pointer));
pointer += sizeof(handle_t); pointer += sizeof(KHandle);
} }
} }
@ -69,7 +69,9 @@ namespace skyline::kernel::ipc {
pointer += sizeof(BufferDescriptorABW); pointer += sizeof(BufferDescriptorABW);
} }
u64 padding = ((((reinterpret_cast<u64>(pointer) - reinterpret_cast<u64>(tls)) - 1U) & ~(constant::IpcPaddingSum - 1U)) + constant::IpcPaddingSum + (reinterpret_cast<u64>(tls) - reinterpret_cast<u64>(pointer))); // Calculate the amount of padding at the front constexpr auto ipcPaddingSum = 0x10; // The sum of the padding surrounding the data payload
u64 padding = ((((reinterpret_cast<u64>(pointer) - reinterpret_cast<u64>(tls)) - 1U) & ~(ipcPaddingSum - 1U)) + ipcPaddingSum + (reinterpret_cast<u64>(tls) - reinterpret_cast<u64>(pointer))); // Calculate the amount of padding at the front
pointer += padding; pointer += padding;
if (isDomain && (header->type == CommandType::Request)) { if (isDomain && (header->type == CommandType::Request)) {
@ -84,24 +86,26 @@ namespace skyline::kernel::ipc {
pointer += domain->payloadSz; pointer += domain->payloadSz;
for (uint index = 0; domain->inputCount > index; index++) { for (uint index = 0; domain->inputCount > index; index++) {
domainObjects.push_back(*reinterpret_cast<handle_t *>(pointer)); domainObjects.push_back(*reinterpret_cast<KHandle *>(pointer));
pointer += sizeof(handle_t); pointer += sizeof(KHandle);
} }
} else { } else {
payload = reinterpret_cast<PayloadHeader *>(pointer); payload = reinterpret_cast<PayloadHeader *>(pointer);
pointer += sizeof(PayloadHeader); pointer += sizeof(PayloadHeader);
cmdArg = pointer; cmdArg = pointer;
cmdArgSz = (header->rawSize * sizeof(u32)) - (constant::IpcPaddingSum + sizeof(PayloadHeader)); cmdArgSz = (header->rawSize * sizeof(u32)) - (ipcPaddingSum + sizeof(PayloadHeader));
pointer += cmdArgSz; pointer += cmdArgSz;
} }
payloadOffset = cmdArg; payloadOffset = cmdArg;
if (payload->magic != constant::SfciMagic && header->type != CommandType::Control) constexpr auto sfciMagic = 0x49434653; //!< SFCI in reverse, present in received IPC messages
if (payload->magic != sfciMagic && header->type != CommandType::Control)
state.logger->Debug("Unexpected Magic in PayloadHeader: 0x{:X}", u32(payload->magic)); state.logger->Debug("Unexpected Magic in PayloadHeader: 0x{:X}", u32(payload->magic));
pointer += constant::IpcPaddingSum - padding; pointer += ipcPaddingSum - padding;
if (header->cFlag == BufferCFlag::SingleDescriptor) { if (header->cFlag == BufferCFlag::SingleDescriptor) {
auto bufC = reinterpret_cast<BufferDescriptorC *>(pointer); auto bufC = reinterpret_cast<BufferDescriptorC *>(pointer);
@ -135,10 +139,14 @@ namespace skyline::kernel::ipc {
void IpcResponse::WriteResponse() { void IpcResponse::WriteResponse() {
auto tls = state.process->GetPointer<u8>(state.thread->tls); auto tls = state.process->GetPointer<u8>(state.thread->tls);
u8 *pointer = tls; u8 *pointer = tls;
memset(tls, 0, constant::TlsIpcSize);
constexpr auto tlsIpcSize = 0x100; // The size of the IPC command buffer in a TLS slot
memset(tls, 0, tlsIpcSize);
constexpr auto ipcPaddingSum = 0x10; // The sum of the padding surrounding the data payload
auto header = reinterpret_cast<CommandHeader *>(pointer); auto header = reinterpret_cast<CommandHeader *>(pointer);
header->rawSize = static_cast<u32>((sizeof(PayloadHeader) + argVec.size() + (domainObjects.size() * sizeof(handle_t)) + constant::IpcPaddingSum + (isDomain ? sizeof(DomainHeaderRequest) : 0)) / sizeof(u32)); // Size is in 32-bit units because Nintendo header->rawSize = static_cast<u32>((sizeof(PayloadHeader) + argVec.size() + (domainObjects.size() * sizeof(KHandle)) + ipcPaddingSum + (isDomain ? sizeof(DomainHeaderRequest) : 0)) / sizeof(u32)); // Size is in 32-bit units because Nintendo
header->handleDesc = (!copyHandles.empty() || !moveHandles.empty()); header->handleDesc = (!copyHandles.empty() || !moveHandles.empty());
pointer += sizeof(CommandHeader); pointer += sizeof(CommandHeader);
@ -149,17 +157,17 @@ namespace skyline::kernel::ipc {
pointer += sizeof(HandleDescriptor); pointer += sizeof(HandleDescriptor);
for (unsigned int copyHandle : copyHandles) { for (unsigned int copyHandle : copyHandles) {
*reinterpret_cast<handle_t *>(pointer) = copyHandle; *reinterpret_cast<KHandle *>(pointer) = copyHandle;
pointer += sizeof(handle_t); pointer += sizeof(KHandle);
} }
for (unsigned int moveHandle : moveHandles) { for (unsigned int moveHandle : moveHandles) {
*reinterpret_cast<handle_t *>(pointer) = moveHandle; *reinterpret_cast<KHandle *>(pointer) = moveHandle;
pointer += sizeof(handle_t); pointer += sizeof(KHandle);
} }
} }
u64 padding = ((((reinterpret_cast<u64>(pointer) - reinterpret_cast<u64>(tls)) - 1U) & ~(constant::IpcPaddingSum - 1U)) + constant::IpcPaddingSum + (reinterpret_cast<u64>(tls) - reinterpret_cast<u64>(pointer))); // Calculate the amount of padding at the front u64 padding = ((((reinterpret_cast<u64>(pointer) - reinterpret_cast<u64>(tls)) - 1U) & ~(ipcPaddingSum - 1U)) + ipcPaddingSum + (reinterpret_cast<u64>(tls) - reinterpret_cast<u64>(pointer))); // Calculate the amount of padding at the front
pointer += padding; pointer += padding;
if (isDomain) { if (isDomain) {
@ -168,19 +176,22 @@ namespace skyline::kernel::ipc {
pointer += sizeof(DomainHeaderResponse); pointer += sizeof(DomainHeaderResponse);
} }
constexpr auto sfcoMagic = 0x4F434653; // SFCO in reverse, IPC Response Magic
auto payload = reinterpret_cast<PayloadHeader *>(pointer); auto payload = reinterpret_cast<PayloadHeader *>(pointer);
payload->magic = constant::SfcoMagic; payload->magic = sfcoMagic;
payload->version = 1; payload->version = 1;
payload->value = errorCode; payload->value = errorCode;
pointer += sizeof(PayloadHeader); pointer += sizeof(PayloadHeader);
if (!argVec.empty()) if (!argVec.empty())
memcpy(pointer, argVec.data(), argVec.size()); memcpy(pointer, argVec.data(), argVec.size());
pointer += argVec.size(); pointer += argVec.size();
if (isDomain) { if (isDomain) {
for (auto &domainObject : domainObjects) { for (auto &domainObject : domainObjects) {
*reinterpret_cast<handle_t *>(pointer) = domainObject; *reinterpret_cast<KHandle *>(pointer) = domainObject;
pointer += sizeof(handle_t); pointer += sizeof(KHandle);
} }
} }

View File

@ -242,7 +242,6 @@ namespace skyline::kernel::ipc {
u8 *payloadOffset; //!< This is the offset of the data read from the payload u8 *payloadOffset; //!< This is the offset of the data read from the payload
public: public:
//std::array<u8, constant::TlsIpcSize> tls; //!< A static-sized array where TLS data is actually copied to
CommandHeader *header{}; //!< The header of the request CommandHeader *header{}; //!< The header of the request
HandleDescriptor *handleDesc{}; //!< The handle descriptor in case CommandHeader::handle_desc is true in the header HandleDescriptor *handleDesc{}; //!< The handle descriptor in case CommandHeader::handle_desc is true in the header
bool isDomain{}; //!< If this is a domain request bool isDomain{}; //!< If this is a domain request
@ -250,9 +249,9 @@ namespace skyline::kernel::ipc {
PayloadHeader *payload{}; //!< This is the header of the payload PayloadHeader *payload{}; //!< This is the header of the payload
u8 *cmdArg{}; //!< This is a pointer to the data payload (End of PayloadHeader) u8 *cmdArg{}; //!< This is a pointer to the data payload (End of PayloadHeader)
u64 cmdArgSz{}; //!< This is the size of the data payload u64 cmdArgSz{}; //!< This is the size of the data payload
std::vector<handle_t> copyHandles; //!< A vector of handles that should be copied from the server to the client process (The difference is just to match application expectations, there is no real difference b/w copying and moving handles) std::vector<KHandle> copyHandles; //!< A vector of handles that should be copied from the server to the client process (The difference is just to match application expectations, there is no real difference b/w copying and moving handles)
std::vector<handle_t> moveHandles; //!< A vector of handles that should be moved from the server to the client process rather than copied std::vector<KHandle> moveHandles; //!< A vector of handles that should be moved from the server to the client process rather than copied
std::vector<handle_t> domainObjects; //!< A vector of all input domain objects std::vector<KHandle> domainObjects; //!< A vector of all input domain objects
std::vector<InputBuffer> inputBuf; //!< This is a vector of input buffers std::vector<InputBuffer> inputBuf; //!< This is a vector of input buffers
std::vector<OutputBuffer> outputBuf; //!< This is a vector of output buffers std::vector<OutputBuffer> outputBuf; //!< This is a vector of output buffers
@ -295,9 +294,9 @@ namespace skyline::kernel::ipc {
bool nWrite{}; //!< This is to signal the IPC handler to not write this, as it will be manually written bool nWrite{}; //!< This is to signal the IPC handler to not write this, as it will be manually written
bool isDomain{}; //!< If this is a domain request bool isDomain{}; //!< If this is a domain request
u32 errorCode{}; //!< The error code to respond with, it is 0 (Success) by default u32 errorCode{}; //!< The error code to respond with, it is 0 (Success) by default
std::vector<handle_t> copyHandles; //!< A vector of handles to copy std::vector<KHandle> copyHandles; //!< A vector of handles to copy
std::vector<handle_t> moveHandles; //!< A vector of handles to move std::vector<KHandle> moveHandles; //!< A vector of handles to move
std::vector<handle_t> domainObjects; //!< A vector of domain objects to write std::vector<KHandle> domainObjects; //!< A vector of domain objects to write
/** /**
* @param isDomain If the following request is a domain request * @param isDomain If the following request is a domain request

View File

@ -117,24 +117,24 @@ namespace skyline {
struct { struct {
MemoryType type; //!< The MemoryType of this memory block MemoryType type; //!< The MemoryType of this memory block
bool PermissionChangeAllowed : 1; //!< If the application can use svcSetMemoryPermission on this block bool permissionChangeAllowed : 1; //!< If the application can use svcSetMemoryPermission on this block
bool ForceReadWritableByDebugSyscalls : 1; //!< If the application can use svcWriteDebugProcessMemory on this block bool forceReadWritableByDebugSyscalls : 1; //!< If the application can use svcWriteDebugProcessMemory on this block
bool IpcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=0 bool ipcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=0
bool NonDeviceIpcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=3 bool nonDeviceIpcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=3
bool NonSecureIpcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=1 bool nonSecureIpcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=1
bool _pad0_ : 1; bool _pad0_ : 1;
bool ProcessPermissionChangeAllowed : 1; //!< If the application can use svcSetProcessMemoryPermission on this block bool processPermissionChangeAllowed : 1; //!< If the application can use svcSetProcessMemoryPermission on this block
bool MapAllowed : 1; //!< If the application can use svcMapMemory on this block bool mapAllowed : 1; //!< If the application can use svcMapMemory on this block
bool UnmapProcessCodeMemoryAllowed : 1; //!< If the application can use svcUnmapProcessCodeMemory on this block bool unmapProcessCodeMemoryAllowed : 1; //!< If the application can use svcUnmapProcessCodeMemory on this block
bool TransferMemoryAllowed : 1; //!< If the application can use svcCreateTransferMemory on this block bool transferMemoryAllowed : 1; //!< If the application can use svcCreateTransferMemory on this block
bool QueryPhysicalAddressAllowed : 1; //!< If the application can use svcQueryPhysicalAddress on this block bool queryPhysicalAddressAllowed : 1; //!< If the application can use svcQueryPhysicalAddress on this block
bool MapDeviceAllowed : 1; //!< If the application can use svcMapDeviceAddressSpace or svcMapDeviceAddressSpaceByForce on this block bool mapDeviceAllowed : 1; //!< If the application can use svcMapDeviceAddressSpace or svcMapDeviceAddressSpaceByForce on this block
bool MapDeviceAlignedAllowed : 1; //!< If the application can use svcMapDeviceAddressSpaceAligned on this block bool mapDeviceAlignedAllowed : 1; //!< If the application can use svcMapDeviceAddressSpaceAligned on this block
bool IpcBufferAllowed : 1; //!< If the application can use this block with svcSendSyncRequestWithUserBuffer bool ipcBufferAllowed : 1; //!< If the application can use this block with svcSendSyncRequestWithUserBuffer
bool IsReferenceCounted : 1; //!< If the physical memory blocks backing this region are reference counted bool isReferenceCounted : 1; //!< If the physical memory blocks backing this region are reference counted
bool MapProcessAllowed : 1; //!< If the application can use svcMapProcessMemory on this block bool mapProcessAllowed : 1; //!< If the application can use svcMapProcessMemory on this block
bool AttributeChangeAllowed : 1; //!< If the application can use svcSetMemoryAttribute on this block bool attributeChangeAllowed : 1; //!< If the application can use svcSetMemoryAttribute on this block
bool CodeMemoryAllowed : 1; //!< If the application can use svcCreateCodeMemory on this block bool codeMemoryAllowed : 1; //!< If the application can use svcCreateCodeMemory on this block
}; };
u32 value; u32 value;
}; };
@ -143,7 +143,7 @@ namespace skyline {
/** /**
* @brief The preset states that different regions are set to (https://switchbrew.org/wiki/SVC#MemoryType) * @brief The preset states that different regions are set to (https://switchbrew.org/wiki/SVC#MemoryType)
*/ */
namespace MemoryStates { namespace states {
constexpr MemoryState Unmapped = 0x00000000; constexpr MemoryState Unmapped = 0x00000000;
constexpr MemoryState Io = 0x00002001; constexpr MemoryState Io = 0x00002001;
constexpr MemoryState CodeStatic = 0x00DC7E03; constexpr MemoryState CodeStatic = 0x00DC7E03;

View File

@ -3,17 +3,23 @@
namespace skyline::kernel::svc { namespace skyline::kernel::svc {
void SetHeapSize(DeviceState &state) { void SetHeapSize(DeviceState &state) {
constexpr auto heapSizeAlign = 0x200000; // The heap size has to be a multiple of this value
const u32 size = state.ctx->registers.w1; const u32 size = state.ctx->registers.w1;
if (size % constant::HeapSizeDiv != 0) {
if (size % heapSizeAlign != 0) {
state.ctx->registers.w0 = constant::status::InvSize; state.ctx->registers.w0 = constant::status::InvSize;
state.ctx->registers.x1 = 0; state.ctx->registers.x1 = 0;
state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size); state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size);
return; return;
} }
auto &heap = state.process->heap; auto &heap = state.process->heap;
heap->Resize(size); heap->Resize(size);
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
state.ctx->registers.x1 = heap->address; state.ctx->registers.x1 = heap->address;
state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} for 0x{:X} bytes", heap->address, heap->size); state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} for 0x{:X} bytes", heap->address, heap->size);
} }
@ -45,7 +51,7 @@ namespace skyline::kernel::svc {
state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", address); state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", address);
return; return;
} }
if (!chunk->state.AttributeChangeAllowed) { if (!chunk->state.attributeChangeAllowed) {
state.ctx->registers.w0 = constant::status::InvState; state.ctx->registers.w0 = constant::status::InvState;
state.logger->Warn("svcSetMemoryAttribute: Attribute change not allowed for chunk: 0x{:X}", address); state.logger->Warn("svcSetMemoryAttribute: Attribute change not allowed for chunk: 0x{:X}", address);
return; return;
@ -82,12 +88,12 @@ namespace skyline::kernel::svc {
state.logger->Warn("svcMapMemory: Source has no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); state.logger->Warn("svcMapMemory: Source has no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return; return;
} }
if (!descriptor->chunk.state.MapAllowed) { if (!descriptor->chunk.state.mapAllowed) {
state.ctx->registers.w0 = constant::status::InvState; state.ctx->registers.w0 = constant::status::InvState;
state.logger->Warn("svcMapMemory: Source doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, descriptor->chunk.state.value); state.logger->Warn("svcMapMemory: Source doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, descriptor->chunk.state.value);
return; return;
} }
state.process->NewHandle<type::KPrivateMemory>(destination, size, descriptor->block.permission, memory::MemoryStates::Stack); state.process->NewHandle<type::KPrivateMemory>(destination, size, descriptor->block.permission, memory::states::Stack);
state.process->CopyMemory(source, destination, size); state.process->CopyMemory(source, destination, size);
auto object = state.process->GetMemoryObject(source); auto object = state.process->GetMemoryObject(source);
if (!object) if (!object)
@ -124,7 +130,7 @@ namespace skyline::kernel::svc {
state.logger->Warn("svcUnmapMemory: Addresses have no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); state.logger->Warn("svcUnmapMemory: Addresses have no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return; return;
} }
if (!destDesc->chunk.state.MapAllowed) { if (!destDesc->chunk.state.mapAllowed) {
state.ctx->registers.w0 = constant::status::InvState; state.ctx->registers.w0 = constant::status::InvState;
state.logger->Warn("svcUnmapMemory: Destination doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, destDesc->chunk.state.value); state.logger->Warn("svcUnmapMemory: Destination doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, destDesc->chunk.state.value);
return; return;
@ -181,7 +187,7 @@ namespace skyline::kernel::svc {
u64 entryArgument = state.ctx->registers.x2; u64 entryArgument = state.ctx->registers.x2;
u64 stackTop = state.ctx->registers.x3; u64 stackTop = state.ctx->registers.x3;
u8 priority = static_cast<u8>(state.ctx->registers.w4); u8 priority = static_cast<u8>(state.ctx->registers.w4);
if ((priority < constant::PriorityNin.first) || (priority > constant::PriorityNin.second)) { if ((priority < constant::SwitchPriority.first) || (priority > constant::SwitchPriority.second)) {
state.ctx->registers.w0 = constant::status::InvAddress; state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority); state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
return; return;
@ -312,7 +318,7 @@ namespace skyline::kernel::svc {
} }
void CloseHandle(DeviceState &state) { void CloseHandle(DeviceState &state) {
auto handle = static_cast<handle_t>(state.ctx->registers.w0); auto handle = static_cast<KHandle>(state.ctx->registers.w0);
try { try {
state.process->handles.erase(handle); state.process->handles.erase(handle);
state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle); state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle);
@ -350,40 +356,52 @@ namespace skyline::kernel::svc {
} }
void WaitSynchronization(DeviceState &state) { void WaitSynchronization(DeviceState &state) {
constexpr auto maxSyncHandles = 0x40; // The total amount of handles that can be passed to WaitSynchronization
auto numHandles = state.ctx->registers.w2; auto numHandles = state.ctx->registers.w2;
if (numHandles > constant::MaxSyncHandles) { if (numHandles > maxSyncHandles) {
state.ctx->registers.w0 = constant::status::MaxHandles; state.ctx->registers.w0 = constant::status::MaxHandles;
return; return;
} }
std::vector<handle_t> waitHandles(numHandles);
std::vector<std::shared_ptr<type::KSyncObject>> objectTable;
state.process->ReadMemory(waitHandles.data(), state.ctx->registers.x1, numHandles * sizeof(handle_t));
std::string handleStr; std::string handleStr;
std::vector<std::shared_ptr<type::KSyncObject>> objectTable;
std::vector<KHandle> waitHandles(numHandles);
state.process->ReadMemory(waitHandles.data(), state.ctx->registers.x1, numHandles * sizeof(KHandle));
for (const auto &handle : waitHandles) { for (const auto &handle : waitHandles) {
handleStr += fmt::format("* 0x{:X}\n", handle); handleStr += fmt::format("* 0x{:X}\n", handle);
auto object = state.process->handles.at(handle); auto object = state.process->handles.at(handle);
switch (object->objectType) { switch (object->objectType) {
case type::KType::KProcess: case type::KType::KProcess:
case type::KType::KThread: case type::KType::KThread:
case type::KType::KEvent: case type::KType::KEvent:
case type::KType::KSession: case type::KType::KSession:
break; break;
default: { default: {
state.ctx->registers.w0 = constant::status::InvHandle; state.ctx->registers.w0 = constant::status::InvHandle;
return; return;
} }
} }
objectTable.push_back(std::static_pointer_cast<type::KSyncObject>(object)); objectTable.push_back(std::static_pointer_cast<type::KSyncObject>(object));
} }
auto timeout = state.ctx->registers.x3; auto timeout = state.ctx->registers.x3;
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout); state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
auto start = utils::GetTimeNs(); auto start = utils::GetTimeNs();
while (true) { while (true) {
if (state.thread->cancelSync) { if (state.thread->cancelSync) {
state.thread->cancelSync = false; state.thread->cancelSync = false;
state.ctx->registers.w0 = constant::status::Interrupted; state.ctx->registers.w0 = constant::status::Interrupted;
break; break;
} }
uint index{}; uint index{};
for (const auto &object : objectTable) { for (const auto &object : objectTable) {
if (object->signalled) { if (object->signalled) {
@ -394,6 +412,7 @@ namespace skyline::kernel::svc {
} }
index++; index++;
} }
if ((utils::GetTimeNs() - start) >= timeout) { if ((utils::GetTimeNs() - start) >= timeout) {
state.logger->Debug("svcWaitSynchronization: Wait has timed out"); state.logger->Debug("svcWaitSynchronization: Wait has timed out");
state.ctx->registers.w0 = constant::status::Timeout; state.ctx->registers.w0 = constant::status::Timeout;
@ -496,34 +515,41 @@ namespace skyline::kernel::svc {
} }
void ConnectToNamedPort(DeviceState &state) { void ConnectToNamedPort(DeviceState &state) {
char port[constant::PortSize + 1]{0}; constexpr auto portSize = 0x8; //!< The size of a port name string
state.process->ReadMemory(port, state.ctx->registers.x1, constant::PortSize); std::string_view port(state.process->GetPointer<char>(state.ctx->registers.x1), portSize);
handle_t handle{};
if (std::strcmp(port, "sm:") == 0) { KHandle handle{};
if (port.compare("sm:") >= 0) {
handle = state.os->serviceManager.NewSession(service::Service::sm_IUserInterface); handle = state.os->serviceManager.NewSession(service::Service::sm_IUserInterface);
} else { } else {
state.logger->Warn("svcConnectToNamedPort: Connecting to invalid port: '{}'", port); state.logger->Warn("svcConnectToNamedPort: Connecting to invalid port: '{}'", port);
state.ctx->registers.w0 = constant::status::NotFound; state.ctx->registers.w0 = constant::status::NotFound;
return; return;
} }
state.logger->Debug("svcConnectToNamedPort: Connecting to port '{}' at 0x{:X}", port, handle); state.logger->Debug("svcConnectToNamedPort: Connecting to port '{}' at 0x{:X}", port, handle);
state.ctx->registers.w1 = handle; state.ctx->registers.w1 = handle;
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
} }
void SendSyncRequest(DeviceState &state) { void SendSyncRequest(DeviceState &state) {
state.os->serviceManager.SyncRequestHandler(static_cast<handle_t>(state.ctx->registers.x0)); state.os->serviceManager.SyncRequestHandler(static_cast<KHandle>(state.ctx->registers.x0));
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
} }
void GetThreadId(DeviceState &state) { void GetThreadId(DeviceState &state) {
pid_t pid{}; constexpr KHandle threadSelf = 0xFFFF8000; // This is the handle used by threads to refer to themselves
auto handle = state.ctx->registers.w1; auto handle = state.ctx->registers.w1;
if (handle != constant::ThreadSelf) pid_t pid{};
if (handle != threadSelf)
pid = state.process->GetHandle<type::KThread>(handle)->pid; pid = state.process->GetHandle<type::KThread>(handle)->pid;
else else
pid = state.thread->pid; pid = state.thread->pid;
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid); state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid);
state.ctx->registers.x1 = static_cast<u64>(pid); state.ctx->registers.x1 = static_cast<u64>(pid);
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
} }
@ -540,7 +566,11 @@ namespace skyline::kernel::svc {
auto id0 = state.ctx->registers.w1; auto id0 = state.ctx->registers.w1;
auto handle = state.ctx->registers.w2; auto handle = state.ctx->registers.w2;
auto id1 = state.ctx->registers.x3; auto id1 = state.ctx->registers.x3;
constexpr auto totalPhysicalMemory = 0xF8000000; // ~4 GB of RAM
u64 out{}; u64 out{};
switch (id0) { switch (id0) {
case constant::infoState::AllowedCpuIdBitmask: case constant::infoState::AllowedCpuIdBitmask:
case constant::infoState::AllowedThreadPriorityMask: case constant::infoState::AllowedThreadPriorityMask:
@ -548,57 +578,75 @@ namespace skyline::kernel::svc {
case constant::infoState::TitleId: case constant::infoState::TitleId:
case constant::infoState::PrivilegedProcessId: case constant::infoState::PrivilegedProcessId:
break; break;
case constant::infoState::AliasRegionBaseAddr: case constant::infoState::AliasRegionBaseAddr:
out = state.os->memory.GetRegion(memory::Regions::Alias).address; out = state.os->memory.GetRegion(memory::Regions::Alias).address;
break; break;
case constant::infoState::AliasRegionSize: case constant::infoState::AliasRegionSize:
out = state.os->memory.GetRegion(memory::Regions::Alias).size; out = state.os->memory.GetRegion(memory::Regions::Alias).size;
break; break;
case constant::infoState::HeapRegionBaseAddr: case constant::infoState::HeapRegionBaseAddr:
out = state.os->memory.GetRegion(memory::Regions::Heap).address; out = state.os->memory.GetRegion(memory::Regions::Heap).address;
break; break;
case constant::infoState::HeapRegionSize: case constant::infoState::HeapRegionSize:
out = state.os->memory.GetRegion(memory::Regions::Heap).size; out = state.os->memory.GetRegion(memory::Regions::Heap).size;
break; break;
case constant::infoState::TotalMemoryAvailable: case constant::infoState::TotalMemoryAvailable:
out = constant::TotalPhyMem; out = totalPhysicalMemory;
break; break;
case constant::infoState::TotalMemoryUsage: case constant::infoState::TotalMemoryUsage:
out = state.process->heap->address + constant::DefStackSize + state.os->memory.GetProgramSize(); out = state.process->heap->address + constant::DefStackSize + state.os->memory.GetProgramSize();
break; break;
case constant::infoState::AddressSpaceBaseAddr: case constant::infoState::AddressSpaceBaseAddr:
out = state.os->memory.GetRegion(memory::Regions::Base).address; out = state.os->memory.GetRegion(memory::Regions::Base).address;
break; break;
case constant::infoState::AddressSpaceSize: case constant::infoState::AddressSpaceSize:
out = state.os->memory.GetRegion(memory::Regions::Base).size; out = state.os->memory.GetRegion(memory::Regions::Base).size;
break; break;
case constant::infoState::StackRegionBaseAddr: case constant::infoState::StackRegionBaseAddr:
out = state.os->memory.GetRegion(memory::Regions::Stack).address; out = state.os->memory.GetRegion(memory::Regions::Stack).address;
break; break;
case constant::infoState::StackRegionSize: case constant::infoState::StackRegionSize:
out = state.os->memory.GetRegion(memory::Regions::Stack).size; out = state.os->memory.GetRegion(memory::Regions::Stack).size;
break; break;
case constant::infoState::PersonalMmHeapSize: case constant::infoState::PersonalMmHeapSize:
out = constant::TotalPhyMem; out = totalPhysicalMemory;
break; break;
case constant::infoState::PersonalMmHeapUsage: case constant::infoState::PersonalMmHeapUsage:
out = state.process->heap->address + constant::DefStackSize; out = state.process->heap->address + constant::DefStackSize;
break; break;
case constant::infoState::TotalMemoryAvailableWithoutMmHeap: case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
out = constant::TotalPhyMem; // TODO: NPDM specifies SystemResourceSize, subtract that from this out = totalPhysicalMemory; // TODO: NPDM specifies SystemResourceSize, subtract that from this
break; break;
case constant::infoState::TotalMemoryUsedWithoutMmHeap: case constant::infoState::TotalMemoryUsedWithoutMmHeap:
out = state.process->heap->size + constant::DefStackSize; // TODO: Same as above out = state.process->heap->size + constant::DefStackSize; // TODO: Same as above
break; break;
case constant::infoState::UserExceptionContextAddr: case constant::infoState::UserExceptionContextAddr:
out = state.process->tlsPages[0]->Get(0); out = state.process->tlsPages[0]->Get(0);
break; break;
default: default:
state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", id0, id1); state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", id0, id1);
state.ctx->registers.w0 = constant::status::Unimpl; state.ctx->registers.w0 = constant::status::Unimpl;
return; return;
} }
state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", id0, id1, out); state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", id0, id1, out);
state.ctx->registers.x1 = out; state.ctx->registers.x1 = out;
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
} }

View File

@ -34,7 +34,8 @@ namespace skyline {
// 6.0.0+ // 6.0.0+
constexpr u8 TotalMemoryAvailableWithoutMmHeap = 0x15; constexpr u8 TotalMemoryAvailableWithoutMmHeap = 0x15;
constexpr u8 TotalMemoryUsedWithoutMmHeap = 0x16; constexpr u8 TotalMemoryUsedWithoutMmHeap = 0x16;
}; }
namespace kernel::svc { namespace kernel::svc {
/** /**
* @brief Sets the process heap to a given Size. It can both extend and shrink the heap. (https://switchbrew.org/wiki/SVC#SetHeapSize) * @brief Sets the process heap to a given Size. It can both extend and shrink the heap. (https://switchbrew.org/wiki/SVC#SetHeapSize)

View File

@ -38,23 +38,29 @@ namespace skyline::kernel::type {
} else { } else {
address = (*(tlsPages.end() - 1))->address + PAGE_SIZE; address = (*(tlsPages.end() - 1))->address + PAGE_SIZE;
} }
auto tlsMem = NewHandle<KPrivateMemory>(address, PAGE_SIZE, memory::Permission(true, true, false), memory::MemoryStates::ThreadLocal).item; auto tlsMem = NewHandle<KPrivateMemory>(address, PAGE_SIZE, memory::Permission(true, true, false), memory::states::ThreadLocal).item;
tlsPages.push_back(std::make_shared<TlsPage>(tlsMem->address)); tlsPages.push_back(std::make_shared<TlsPage>(tlsMem->address));
auto &tlsPage = tlsPages.back(); auto &tlsPage = tlsPages.back();
if (tlsPages.empty()) if (tlsPages.empty())
tlsPage->ReserveSlot(); // User-mode exception handling tlsPage->ReserveSlot(); // User-mode exception handling
return tlsPage->ReserveSlot(); return tlsPage->ReserveSlot();
} }
void KProcess::InitializeMemory() { void KProcess::InitializeMemory() {
heap = NewHandle<KPrivateMemory>(state.os->memory.GetRegion(memory::Regions::Heap).address, constant::DefHeapSize, memory::Permission{true, true, false}, memory::MemoryStates::Heap).item; constexpr size_t DefHeapSize = 0x200000; // The default amount of heap
heap = NewHandle<KPrivateMemory>(state.os->memory.GetRegion(memory::Regions::Heap).address, DefHeapSize, memory::Permission{true, true, false}, memory::states::Heap).item;
threads[pid]->tls = GetTlsSlot(); threads[pid]->tls = GetTlsSlot();
} }
KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, std::shared_ptr<type::KSharedMemory> &stack, std::shared_ptr<type::KSharedMemory> &tlsMemory) : pid(pid), stack(stack), KSyncObject(state, KType::KProcess) { KProcess::KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, std::shared_ptr<type::KSharedMemory> &stack, std::shared_ptr<type::KSharedMemory> &tlsMemory) : pid(pid), stack(stack), KSyncObject(state, KType::KProcess) {
auto thread = NewHandle<KThread>(pid, entryPoint, 0x0, stack->guest.address + stack->guest.size, 0, constant::DefaultPriority, this, tlsMemory).item; constexpr auto DefaultPriority = 44; // The default priority of a process
auto thread = NewHandle<KThread>(pid, entryPoint, 0x0, stack->guest.address + stack->guest.size, 0, DefaultPriority, this, tlsMemory).item;
threads[pid] = thread; threads[pid] = thread;
state.nce->WaitThreadInit(thread); state.nce->WaitThreadInit(thread);
memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC); memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC);
if (memFd == -1) if (memFd == -1)
throw exception("Cannot open file descriptor to /proc/{}/mem, \"{}\"", pid, strerror(errno)); throw exception("Cannot open file descriptor to /proc/{}/mem, \"{}\"", pid, strerror(errno));
@ -67,7 +73,7 @@ namespace skyline::kernel::type {
std::shared_ptr<KThread> KProcess::CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, u8 priority) { std::shared_ptr<KThread> KProcess::CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, u8 priority) {
auto size = (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); auto size = (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, size, memory::Permission{true, true, false}, memory::MemoryStates::Reserved); auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, size, memory::Permission{true, true, false}, memory::states::Reserved);
Registers fregs{}; Registers fregs{};
fregs.x0 = CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO; fregs.x0 = CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO;
fregs.x1 = stackTop; fregs.x1 = stackTop;
@ -166,7 +172,7 @@ namespace skyline::kernel::type {
return std::nullopt; return std::nullopt;
} }
bool KProcess::MutexLock(u64 address, handle_t owner) { bool KProcess::MutexLock(u64 address, KHandle owner) {
std::unique_lock lock(mutexLock); std::unique_lock lock(mutexLock);
auto mtx = GetPointer<u32>(address); auto mtx = GetPointer<u32>(address);
auto &mtxWaiters = mutexes[address]; auto &mtxWaiters = mutexes[address];

View File

@ -9,372 +9,381 @@
#include <kernel/memory.h> #include <kernel/memory.h>
#include <list> #include <list>
namespace skyline::kernel::type { namespace skyline {
/** namespace constant {
* @brief The KProcess class is responsible for holding the state of a process constexpr auto TlsSlotSize = 0x200; //!< The size of a single TLS slot
*/ constexpr auto TlsSlots = PAGE_SIZE / TlsSlotSize; //!< The amount of TLS slots in a single page
class KProcess : public KSyncObject { constexpr KHandle BaseHandleIndex = 0xD000; // The index of the base handle
private: constexpr u32 MtxOwnerMask = 0xBFFFFFFF; //!< The mask of values which contain the owner of a mutex
}
namespace kernel::type {
/** /**
* @brief This class holds a single TLS page's status * @brief The KProcess class is responsible for holding the state of a process
* @details tls_page_t holds the status of a single TLS page (A page is 4096 bytes on ARMv8). */
* Each TLS page has 8 slots, each 0x200 (512) bytes in size. class KProcess : public KSyncObject {
* The first slot of the first page is reserved for user-mode exception handling. private:
* Read more about TLS here: https://switchbrew.org/wiki/Thread_Local_Storage /**
*/ * @brief This class holds a single TLS page's status
struct TlsPage { * @details tls_page_t holds the status of a single TLS page (A page is 4096 bytes on ARMv8).
u64 address; //!< The address of the page allocated for TLS * Each TLS page has 8 slots, each 0x200 (512) bytes in size.
u8 index = 0; //!< The slots are assigned sequentially, this holds the index of the last TLS slot reserved * The first slot of the first page is reserved for user-mode exception handling.
bool slot[constant::TlsSlots]{0}; //!< An array of booleans denoting which TLS slots are reserved * Read more about TLS here: https://switchbrew.org/wiki/Thread_Local_Storage
*/
struct TlsPage {
u64 address; //!< The address of the page allocated for TLS
u8 index = 0; //!< The slots are assigned sequentially, this holds the index of the last TLS slot reserved
bool slot[constant::TlsSlots]{0}; //!< An array of booleans denoting which TLS slots are reserved
/**
* @param address The address of the allocated page
*/
TlsPage(u64 address);
/**
* @brief Reserves a single 0x200 byte TLS slot
* @return The address of the reserved slot
*/
u64 ReserveSlot();
/**
* @brief Returns the address of a particular slot
* @param slotNo The number of the slot to be returned
* @return The address of the specified slot
*/
u64 Get(u8 slotNo);
/**
* @brief Returns boolean on if the TLS page has free slots or not
* @return If the whole page is full or not
*/
bool Full();
};
/** /**
* @param address The address of the allocated page * @brief Returns a TLS slot from an arbitrary TLS page
*/ * @return The address of a free TLS slot
TlsPage(u64 address); */
u64 GetTlsSlot();
/** /**
* @brief Reserves a single 0x200 byte TLS slot * @brief This initializes heap and the initial TLS page
* @return The address of the reserved slot */
*/ void InitializeMemory();
u64 ReserveSlot();
public:
friend OS;
/** /**
* @brief Returns the address of a particular slot * @brief This is used as the output for functions that return created kernel objects
* @param slotNo The number of the slot to be returned * @tparam objectClass The class of the kernel object
* @return The address of the specified slot */
*/ template<typename objectClass>
u64 Get(u8 slotNo); struct HandleOut {
std::shared_ptr<objectClass> item; //!< A shared pointer to the object
KHandle handle; //!< The handle of the object in the process
};
/** /**
* @brief Returns boolean on if the TLS page has free slots or not * @brief This enum is used to describe the current status of the process
* @return If the whole page is full or not */
*/ enum class Status {
bool Full(); Created, //!< The process was created but the main thread has not started yet
}; Started, //!< The process has been started
Exiting //!< The process is exiting
} status = Status::Created; //!< The state of the process
/** /**
* @brief Returns a TLS slot from an arbitrary TLS page * @brief This is used to hold information about a single waiting thread for mutexes and conditional variables
* @return The address of a free TLS slot */
*/ struct WaitStatus {
u64 GetTlsSlot(); std::atomic_bool flag{false}; //!< The underlying atomic flag of the thread
u8 priority; //!< The priority of the thread
KHandle handle; //!< The handle of the thread
u64 mutexAddress{}; //!< The address of the mutex
/** WaitStatus(u8 priority, KHandle handle) : priority(priority), handle(handle) {}
* @brief This initializes heap and the initial TLS page
*/
void InitializeMemory();
public: WaitStatus(u8 priority, KHandle handle, u64 mutexAddress) : priority(priority), handle(handle), mutexAddress(mutexAddress) {}
friend OS; };
/** KHandle handleIndex = constant::BaseHandleIndex; //!< This is used to keep track of what to map as an handle
* @brief This is used as the output for functions that return created kernel objects pid_t pid; //!< The PID of the main thread
* @tparam objectClass The class of the kernel object int memFd; //!< The file descriptor to the memory of the process
*/ std::unordered_map<KHandle, std::shared_ptr<KObject>> handles; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object
template<typename objectClass> std::unordered_map<pid_t, std::shared_ptr<KThread>> threads; //!< A mapping from a PID to it's corresponding KThread object
struct HandleOut { std::unordered_map<u64, std::vector<std::shared_ptr<WaitStatus>>> mutexes; //!< A map from a mutex's address to a vector of Mutex objects for threads waiting on it
std::shared_ptr<objectClass> item; //!< A shared pointer to the object std::unordered_map<u64, std::list<std::shared_ptr<WaitStatus>>> conditionals; //!< A map from a conditional variable's address to a vector of threads waiting on it
handle_t handle; //!< The handle of the object in the process std::vector<std::shared_ptr<TlsPage>> tlsPages; //!< A vector of all allocated TLS pages
}; std::shared_ptr<type::KSharedMemory> stack; //!< The shared memory used to hold the stack of the main thread
std::shared_ptr<KPrivateMemory> heap; //!< The kernel memory object backing the allocated heap
Mutex mutexLock; //!< This mutex is to prevent concurrent mutex operations to happen at once
Mutex conditionalLock; //!< This mutex is to prevent concurrent conditional variable operations to happen at once
/** /**
* @brief This enum is used to describe the current status of the process * @brief Creates a KThread object for the main thread and opens the process's memory file
*/ * @param state The state of the device
enum class Status { * @param pid The PID of the main thread
Created, //!< The process was created but the main thread has not started yet * @param entryPoint The address to start execution at
Started, //!< The process has been started * @param stack The KSharedMemory object for Stack memory allocated by the guest process
Exiting //!< The process is exiting * @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process
} status = Status::Created; //!< The state of the process */
KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, std::shared_ptr<type::KSharedMemory> &stack, std::shared_ptr<type::KSharedMemory> &tlsMemory);
/** /**
* @brief This is used to hold information about a single waiting thread for mutexes and conditional variables * Close the file descriptor to the process's memory
*/ */
struct WaitStatus { ~KProcess();
std::atomic_bool flag{false}; //!< The underlying atomic flag of the thread
u8 priority; //!< The priority of the thread
handle_t handle; //!< The handle of the thread
u64 mutexAddress{}; //!< The address of the mutex
WaitStatus(u8 priority, handle_t handle) : priority(priority), handle(handle) {} /**
* @brief Create a thread in this process
* @param entryPoint The address of the initial function
* @param entryArg An argument to the function
* @param stackTop The top of the stack
* @param priority The priority of the thread
* @return An instance of KThread class for the corresponding thread
*/
std::shared_ptr<KThread> CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, u8 priority);
WaitStatus(u8 priority, handle_t handle, u64 mutexAddress) : priority(priority), handle(handle), mutexAddress(mutexAddress) {} /**
}; * @brief This returns the host address for a specific address in guest memory
* @param address The corresponding guest address
* @return The corresponding host address
*/
u64 GetHostAddress(const u64 address) const;
handle_t handleIndex = constant::BaseHandleIndex; //!< This is used to keep track of what to map as an handle /**
pid_t pid; //!< The PID of the main thread * @tparam Type The type of the pointer to return
int memFd; //!< The file descriptor to the memory of the process * @param address The address on the guest
std::unordered_map<handle_t, std::shared_ptr<KObject>> handles; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object * @return A pointer corresponding to a certain address on the guest
std::unordered_map<pid_t, std::shared_ptr<KThread>> threads; //!< A mapping from a PID to it's corresponding KThread object * @note This can return a nullptr if the address is invalid
std::unordered_map<u64, std::vector<std::shared_ptr<WaitStatus>>> mutexes; //!< A map from a mutex's address to a vector of Mutex objects for threads waiting on it */
std::unordered_map<u64, std::list<std::shared_ptr<WaitStatus>>> conditionals; //!< A map from a conditional variable's address to a vector of threads waiting on it template<typename Type>
std::vector<std::shared_ptr<TlsPage>> tlsPages; //!< A vector of all allocated TLS pages inline Type *GetPointer(const u64 address) const {
std::shared_ptr<type::KSharedMemory> stack; //!< The shared memory used to hold the stack of the main thread return reinterpret_cast<Type *>(GetHostAddress(address));
std::shared_ptr<KPrivateMemory> heap; //!< The kernel memory object backing the allocated heap
Mutex mutexLock; //!< This mutex is to prevent concurrent mutex operations to happen at once
Mutex conditionalLock; //!< This mutex is to prevent concurrent conditional variable operations to happen at once
/**
* @brief Creates a KThread object for the main thread and opens the process's memory file
* @param state The state of the device
* @param pid The PID of the main thread
* @param entryPoint The address to start execution at
* @param stack The KSharedMemory object for Stack memory allocated by the guest process
* @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process
*/
KProcess(const DeviceState &state, pid_t pid, u64 entryPoint, std::shared_ptr<type::KSharedMemory> &stack, std::shared_ptr<type::KSharedMemory> &tlsMemory);
/**
* Close the file descriptor to the process's memory
*/
~KProcess();
/**
* @brief Create a thread in this process
* @param entryPoint The address of the initial function
* @param entryArg An argument to the function
* @param stackTop The top of the stack
* @param priority The priority of the thread
* @return An instance of KThread class for the corresponding thread
*/
std::shared_ptr<KThread> CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, u8 priority);
/**
* @brief This returns the host address for a specific address in guest memory
* @param address The corresponding guest address
* @return The corresponding host address
*/
u64 GetHostAddress(const u64 address) const;
/**
* @tparam Type The type of the pointer to return
* @param address The address on the guest
* @return A pointer corresponding to a certain address on the guest
* @note This can return a nullptr if the address is invalid
*/
template<typename Type>
inline Type *GetPointer(const u64 address) const {
return reinterpret_cast<Type *>(GetHostAddress(address));
}
/**
* @brief Returns a reference to an object from guest memory
* @tparam Type The type of the object to be read
* @param address The address of the object
* @return A reference to object with type T
*/
template<typename Type>
inline Type &GetReference(u64 address) const {
auto source = GetPointer<Type>(address);
if (source)
return *source;
else
throw exception("Cannot retrieve reference to object not in shared guest memory");
}
/**
* @brief Returns a copy of an object from guest memory
* @tparam Type The type of the object to be read
* @param address The address of the object
* @return A copy of the object from guest memory
*/
template<typename Type>
inline Type GetObject(u64 address) const {
auto source = GetPointer<Type>(address);
if (source) {
return *source;
} else {
Type item{};
ReadMemory(&item, address, sizeof(Type));
return item;
} }
}
/** /**
* @brief Returns a string from guest memory * @brief Returns a reference to an object from guest memory
* @param address The address of the object * @tparam Type The type of the object to be read
* @param maxSize The maximum size of the string * @param address The address of the object
* @return A copy of a string in guest memory * @return A reference to object with type T
*/ */
inline std::string GetString(u64 address, const size_t maxSize) const { template<typename Type>
auto source = GetPointer<char>(address); inline Type &GetReference(u64 address) const {
if (source) auto source = GetPointer<Type>(address);
return std::string(source, maxSize); if (source)
std::string debug(maxSize, '\0'); return *source;
ReadMemory(debug.data(), address, maxSize);
return debug;
}
/**
* @brief Writes an object to guest memory
* @tparam Type The type of the object to be written
* @param item The object to write
* @param address The address of the object
*/
template<typename Type>
inline void WriteMemory(Type &item, u64 address) const {
auto destination = GetPointer<Type>(address);
if (destination) {
*destination = item;
} else {
WriteMemory(&item, address, sizeof(Type));
}
}
/**
* @brief Writes an object to guest memory
* @tparam Type The type of the object to be written
* @param item The object to write
* @param address The address of the object
*/
template<typename Type>
inline void WriteMemory(const Type &item, u64 address) const {
auto destination = GetPointer<Type>(address);
if (destination) {
*destination = item;
} else {
WriteMemory(&item, address, sizeof(Type));
}
}
/**
* @brief Read data from the guest's memory
* @param destination The address to the location where the process memory is written
* @param offset The address to read from in process memory
* @param size The amount of memory to be read
* @param forceGuest This flag forces the write to be performed in guest address space
*/
void ReadMemory(void *destination, const u64 offset, const size_t size, const bool forceGuest = false) const;
/**
* @brief Write to the guest's memory
* @param source The address of where the data to be written is present
* @param offset The address to write to in process memory
* @param size The amount of memory to be written
* @param forceGuest This flag forces the write to be performed in guest address space
*/
void WriteMemory(void *source, const u64 offset, const size_t size, const bool forceGuest = false) const;
/**
* @brief Copy one chunk to another in the guest's memory
* @param source The address of where the data to read is present
* @param destination The address to write the read data to
* @param size The amount of memory to be copied
*/
void CopyMemory(const u64 source, const u64 destination, const size_t size) const;
/**
* @brief Creates a new handle to a KObject and adds it to the process handle_table
* @tparam objectClass The class of the kernel object to create
* @param args The arguments for the kernel object except handle, pid and state
* @return A shared pointer to the corresponding object
*/
template<typename objectClass, typename ...objectArgs>
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...);
handles[handleIndex] = std::static_pointer_cast<KObject>(item);
return {item, handleIndex++};
}
/**
* @brief 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) {
handles[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 constexpr(std::is_same<objectClass, KThread>())
objectType = KType::KThread;
else if constexpr(std::is_same<objectClass, KProcess>())
objectType = KType::KProcess;
else if constexpr(std::is_same<objectClass, KSharedMemory>())
objectType = KType::KSharedMemory;
else if constexpr(std::is_same<objectClass, KTransferMemory>())
objectType = KType::KTransferMemory;
else if constexpr(std::is_same<objectClass, KPrivateMemory>())
objectType = KType::KPrivateMemory;
else if constexpr(std::is_same<objectClass, KSession>())
objectType = KType::KSession;
else if constexpr(std::is_same<objectClass, KEvent>())
objectType = KType::KEvent;
else
throw exception("KProcess::GetHandle couldn't determine object type");
try {
auto item = handles.at(handle);
if (item->objectType == objectType)
return std::static_pointer_cast<objectClass>(item);
else else
throw exception("Tried to get kernel object (0x{:X}) with different type: {} when object is {}", handle, objectType, item->objectType); throw exception("Cannot retrieve reference to object not in shared guest memory");
} catch (std::out_of_range) {
throw exception("GetHandle was called with invalid handle: 0x{:X}", handle);
} }
}
/** /**
* @brief Retrieves a kernel memory object that owns the specified address * @brief Returns a copy of an object from guest memory
* @param address The address to look for * @tparam Type The type of the object to be read
* @return A shared pointer to the corresponding KMemory object * @param address The address of the object
*/ * @return A copy of the object from guest memory
std::optional<HandleOut<KMemory>> GetMemoryObject(u64 address); */
template<typename Type>
inline Type GetObject(u64 address) const {
auto source = GetPointer<Type>(address);
if (source) {
return *source;
} else {
Type item{};
ReadMemory(&item, address, sizeof(Type));
return item;
}
}
/** /**
* @brief This deletes a certain handle from the handle table * @brief Returns a string from guest memory
* @param handle The handle to delete * @param address The address of the object
*/ * @param maxSize The maximum size of the string
inline void DeleteHandle(handle_t handle) { * @return A copy of a string in guest memory
handles.erase(handle); */
} inline std::string GetString(u64 address, const size_t maxSize) const {
auto source = GetPointer<char>(address);
if (source)
return std::string(source, maxSize);
std::string debug(maxSize, '\0');
ReadMemory(debug.data(), address, maxSize);
return debug;
}
/** /**
* @brief This locks the Mutex at the specified address * @brief Writes an object to guest memory
* @param address The address of the mutex * @tparam Type The type of the object to be written
* @param owner The handle of the current mutex owner * @param item The object to write
* @return If the mutex was successfully locked * @param address The address of the object
*/ */
bool MutexLock(u64 address, handle_t owner); template<typename Type>
inline void WriteMemory(Type &item, u64 address) const {
auto destination = GetPointer<Type>(address);
if (destination) {
*destination = item;
} else {
WriteMemory(&item, address, sizeof(Type));
}
}
/** /**
* @brief This unlocks the Mutex at the specified address * @brief Writes an object to guest memory
* @param address The address of the mutex * @tparam Type The type of the object to be written
* @return If the mutex was successfully unlocked * @param item The object to write
*/ * @param address The address of the object
bool MutexUnlock(u64 address); */
template<typename Type>
inline void WriteMemory(const Type &item, u64 address) const {
auto destination = GetPointer<Type>(address);
if (destination) {
*destination = item;
} else {
WriteMemory(&item, address, sizeof(Type));
}
}
/** /**
* @param conditionalAddress The address of the conditional variable * @brief Read data from the guest's memory
* @param mutexAddress The address of the mutex * @param destination The address to the location where the process memory is written
* @param timeout The amount of time to wait for the conditional variable * @param offset The address to read from in process memory
* @return If the conditional variable was successfully waited for or timed out * @param size The amount of memory to be read
*/ * @param forceGuest This flag forces the write to be performed in guest address space
bool ConditionalVariableWait(u64 conditionalAddress, u64 mutexAddress, u64 timeout); */
void ReadMemory(void *destination, const u64 offset, const size_t size, const bool forceGuest = false) const;
/** /**
* @brief This signals a number of conditional variable waiters * @brief Write to the guest's memory
* @param address The address of the conditional variable * @param source The address of where the data to be written is present
* @param amount The amount of waiters to signal * @param offset The address to write to in process memory
*/ * @param size The amount of memory to be written
void ConditionalVariableSignal(u64 address, u64 amount); * @param forceGuest This flag forces the write to be performed in guest address space
*/
void WriteMemory(void *source, const u64 offset, const size_t size, const bool forceGuest = false) const;
/** /**
* @brief This resets the object to an unsignalled state * @brief Copy one chunk to another in the guest's memory
*/ * @param source The address of where the data to read is present
inline void ResetSignal() { * @param destination The address to write the read data to
signalled = false; * @param size The amount of memory to be copied
} */
}; void CopyMemory(const u64 source, const u64 destination, const size_t size) const;
/**
* @brief Creates a new handle to a KObject and adds it to the process handle_table
* @tparam objectClass The class of the kernel object to create
* @param args The arguments for the kernel object except handle, pid and state
* @return A shared pointer to the corresponding object
*/
template<typename objectClass, typename ...objectArgs>
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...);
handles[handleIndex] = std::static_pointer_cast<KObject>(item);
return {item, handleIndex++};
}
/**
* @brief 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>
KHandle InsertItem(std::shared_ptr<objectClass> &item) {
handles[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(KHandle handle) {
KType objectType;
if constexpr(std::is_same<objectClass, KThread>())
objectType = KType::KThread;
else if constexpr(std::is_same<objectClass, KProcess>())
objectType = KType::KProcess;
else if constexpr(std::is_same<objectClass, KSharedMemory>())
objectType = KType::KSharedMemory;
else if constexpr(std::is_same<objectClass, KTransferMemory>())
objectType = KType::KTransferMemory;
else if constexpr(std::is_same<objectClass, KPrivateMemory>())
objectType = KType::KPrivateMemory;
else if constexpr(std::is_same<objectClass, KSession>())
objectType = KType::KSession;
else if constexpr(std::is_same<objectClass, KEvent>())
objectType = KType::KEvent;
else
throw exception("KProcess::GetHandle couldn't determine object type");
try {
auto item = handles.at(handle);
if (item->objectType == objectType)
return std::static_pointer_cast<objectClass>(item);
else
throw exception("Tried to get kernel object (0x{:X}) with different type: {} when object is {}", handle, objectType, item->objectType);
} catch (std::out_of_range) {
throw exception("GetHandle was called with invalid handle: 0x{:X}", handle);
}
}
/**
* @brief Retrieves a kernel memory object that owns the specified address
* @param address The address to look for
* @return A shared pointer to the corresponding KMemory object
*/
std::optional<HandleOut<KMemory>> GetMemoryObject(u64 address);
/**
* @brief This deletes a certain handle from the handle table
* @param handle The handle to delete
*/
inline void DeleteHandle(KHandle handle) {
handles.erase(handle);
}
/**
* @brief This locks the Mutex at the specified address
* @param address The address of the mutex
* @param owner The handle of the current mutex owner
* @return If the mutex was successfully locked
*/
bool MutexLock(u64 address, KHandle owner);
/**
* @brief This unlocks the Mutex at the specified address
* @param address The address of the mutex
* @return If the mutex was successfully unlocked
*/
bool MutexUnlock(u64 address);
/**
* @param conditionalAddress The address of the conditional variable
* @param mutexAddress The address of the mutex
* @param timeout The amount of time to wait for the conditional variable
* @return If the conditional variable was successfully waited for or timed out
*/
bool ConditionalVariableWait(u64 conditionalAddress, u64 mutexAddress, u64 timeout);
/**
* @brief This signals a number of conditional variable waiters
* @param address The address of the conditional variable
* @param amount The amount of waiters to signal
*/
void ConditionalVariableSignal(u64 address, u64 amount);
/**
* @brief This resets the object to an unsignalled state
*/
inline void ResetSignal() {
signalled = false;
}
};
}
} }

View File

@ -11,8 +11,8 @@ namespace skyline::kernel::type {
class KSession : public KSyncObject { class KSession : public KSyncObject {
public: public:
const std::shared_ptr<service::BaseService> serviceObject; //!< A shared pointer to the service class const std::shared_ptr<service::BaseService> serviceObject; //!< A shared pointer to the service class
std::unordered_map<handle_t, std::shared_ptr<service::BaseService>> domainTable; //!< This maps from a virtual handle to it's service std::unordered_map<KHandle, std::shared_ptr<service::BaseService>> domainTable; //!< This maps from a virtual handle to it's service
handle_t handleIndex = constant::BaseVirtualHandleIndex; KHandle handleIndex{0x1}; //!< The currently allocated handle index
const service::Service serviceType; //!< The type of the service const service::Service serviceType; //!< The type of the service
enum class ServiceStatus { Open, Closed } serviceStatus{ServiceStatus::Open}; //!< If the session is open or closed enum class ServiceStatus { Open, Closed } serviceStatus{ServiceStatus::Open}; //!< If the session is open or closed
bool isDomain{}; //!< Holds if this is a domain session or not bool isDomain{}; //!< Holds if this is a domain session or not
@ -27,7 +27,7 @@ namespace skyline::kernel::type {
* This converts this session into a domain session (https://switchbrew.org/wiki/IPC_Marshalling#Domains) * This converts this session into a domain session (https://switchbrew.org/wiki/IPC_Marshalling#Domains)
* @return The virtual handle of this service in the domain * @return The virtual handle of this service in the domain
*/ */
handle_t ConvertDomain() { KHandle ConvertDomain() {
isDomain = true; isDomain = true;
domainTable[handleIndex] = serviceObject; domainTable[handleIndex] = serviceObject;
return handleIndex++; return handleIndex++;

View File

@ -50,7 +50,7 @@ namespace skyline::kernel::type {
} }
void KSharedMemory::Resize(size_t size) { void KSharedMemory::Resize(size_t size) {
if (guest.valid() && kernel.valid()) { if (guest.Valid() && kernel.Valid()) {
if (close(fd) < 0) if (close(fd) < 0)
throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno)); throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno));
fd = ASharedMemory_create("KSharedMemory", size); fd = ASharedMemory_create("KSharedMemory", size);
@ -98,7 +98,7 @@ namespace skyline::kernel::type {
throw exception("An occurred while mapping shared memory: {}", strerror(errno)); throw exception("An occurred while mapping shared memory: {}", strerror(errno));
guest.size = size; guest.size = size;
MemoryManager::ResizeChunk(chunk, size); MemoryManager::ResizeChunk(chunk, size);
} else if (kernel.valid()) { } else if (kernel.Valid()) {
if (close(fd) < 0) if (close(fd) < 0)
throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno)); throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno));
fd = ASharedMemory_create("KSharedMemory", size); fd = ASharedMemory_create("KSharedMemory", size);
@ -119,7 +119,7 @@ namespace skyline::kernel::type {
} }
void KSharedMemory::UpdatePermission(u64 address, u64 size, memory::Permission permission, bool host) { void KSharedMemory::UpdatePermission(u64 address, u64 size, memory::Permission permission, bool host) {
if (guest.valid() && !host) { if (guest.Valid() && !host) {
Registers fregs{ Registers fregs{
.x0 = address, .x0 = address,
.x1 = size, .x1 = size,
@ -137,7 +137,7 @@ namespace skyline::kernel::type {
}; };
MemoryManager::InsertBlock(chunk, block); MemoryManager::InsertBlock(chunk, block);
} }
if (kernel.valid() && host) { if (kernel.Valid() && host) {
if (mprotect(reinterpret_cast<void *>(kernel.address), kernel.size, permission.Get()) == reinterpret_cast<u64>(MAP_FAILED)) if (mprotect(reinterpret_cast<void *>(kernel.address), kernel.size, permission.Get()) == reinterpret_cast<u64>(MAP_FAILED))
throw exception("An error occurred while remapping shared memory: {}", strerror(errno)); throw exception("An error occurred while remapping shared memory: {}", strerror(errno));
kernel.permission = permission; kernel.permission = permission;
@ -146,7 +146,7 @@ namespace skyline::kernel::type {
KSharedMemory::~KSharedMemory() { KSharedMemory::~KSharedMemory() {
try { try {
if (guest.valid() && state.process) { if (guest.Valid() && state.process) {
Registers fregs{ Registers fregs{
.x0 = guest.address, .x0 = guest.address,
.x1 = guest.size, .x1 = guest.size,
@ -156,7 +156,7 @@ namespace skyline::kernel::type {
} }
} catch (const std::exception &) { } catch (const std::exception &) {
} }
if (kernel.valid()) if (kernel.Valid())
munmap(reinterpret_cast<void *>(kernel.address), kernel.size); munmap(reinterpret_cast<void *>(kernel.address), kernel.size);
state.os->memory.DeleteChunk(guest.address); state.os->memory.DeleteChunk(guest.address);
close(fd); close(fd);

View File

@ -24,7 +24,7 @@ namespace skyline::kernel::type {
* @brief Returns if the object is valid * @brief Returns if the object is valid
* @return If the MapInfo object is valid * @return If the MapInfo object is valid
*/ */
inline bool valid() { return address && size && permission.Get(); } inline bool Valid() { return address && size && permission.Get(); }
} kernel, guest; } kernel, guest;
/** /**
@ -35,7 +35,7 @@ namespace skyline::kernel::type {
* @param memState The MemoryState of the chunk of memory * @param memState The MemoryState of the chunk of memory
* @param mmapFlags Additional flags to pass to mmap * @param mmapFlags Additional flags to pass to mmap
*/ */
KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState = memory::MemoryStates::SharedMemory, int mmapFlags = 0); KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState = memory::states::SharedMemory, int mmapFlags = 0);
/** /**
* @brief Maps the shared memory in the guest * @brief Maps the shared memory in the guest

View File

@ -4,8 +4,7 @@
#include <nce.h> #include <nce.h>
namespace skyline::kernel::type { namespace skyline::kernel::type {
KThread::KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr<type::KSharedMemory> &tlsMemory) : handle(handle), pid(self_pid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), ctxMemory(tlsMemory), KSyncObject(state, KThread::KThread(const DeviceState &state, KHandle handle, pid_t selfPid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr<type::KSharedMemory> &tlsMemory) : handle(handle), pid(selfPid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), ctxMemory(tlsMemory), KSyncObject(state, KType::KThread) {
KType::KThread) {
UpdatePriority(priority); UpdatePriority(priority);
} }
@ -31,8 +30,9 @@ namespace skyline::kernel::type {
void KThread::UpdatePriority(u8 priority) { void KThread::UpdatePriority(u8 priority) {
this->priority = priority; this->priority = priority;
auto liPriority = static_cast<int8_t>(constant::PriorityAn.first + ((static_cast<float>(constant::PriorityAn.second - constant::PriorityAn.first) / static_cast<float>(constant::PriorityNin.second - constant::PriorityNin.first)) * (static_cast<float>(priority) - constant::PriorityNin.first))); // Resize range PriorityNin (Nintendo Priority) to PriorityAn (Android Priority) auto linuxPriority = static_cast<int8_t>(constant::AndroidPriority.first + ((static_cast<float>(constant::AndroidPriority.second - constant::AndroidPriority.first) / static_cast<float>(constant::SwitchPriority.second - constant::SwitchPriority.first)) * (static_cast<float>(priority) - constant::SwitchPriority.first))); // Resize range SwitchPriority (Nintendo Priority) to AndroidPriority (Android Priority)
if (setpriority(PRIO_PROCESS, static_cast<id_t>(pid), liPriority) == -1)
throw exception("Couldn't set process priority to {} for PID: {}", liPriority, pid); if (setpriority(PRIO_PROCESS, static_cast<id_t>(pid), linuxPriority) == -1)
throw exception("Couldn't set process priority to {} for PID: {}", linuxPriority, pid);
} }
} }

View File

@ -21,7 +21,7 @@ namespace skyline::kernel::type {
} status = Status::Created; //!< The state of the thread } status = Status::Created; //!< The state of the thread
std::atomic<bool> cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in std::atomic<bool> cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in
std::shared_ptr<type::KSharedMemory> ctxMemory; //!< The KSharedMemory of the shared memory allocated by the guest process TLS std::shared_ptr<type::KSharedMemory> ctxMemory; //!< The KSharedMemory of the shared memory allocated by the guest process TLS
handle_t handle; // The handle of the object in the handle table KHandle handle; // The handle of the object in the handle table
pid_t pid; //!< The PID of the current thread (As in kernel PID and not PGID [In short, Linux implements threads as processes that share a lot of stuff at the kernel level]) pid_t pid; //!< The PID of the current thread (As in kernel PID and not PGID [In short, Linux implements threads as processes that share a lot of stuff at the kernel level])
u64 stackTop; //!< The top of the stack (Where it starts growing downwards from) u64 stackTop; //!< The top of the stack (Where it starts growing downwards from)
u64 tls; //!< The address of TLS (Thread Local Storage) slot assigned to the current thread u64 tls; //!< The address of TLS (Thread Local Storage) slot assigned to the current thread
@ -30,7 +30,7 @@ namespace skyline::kernel::type {
/** /**
* @param state The state of the device * @param state The state of the device
* @param handle The handle of the current thread * @param handle The handle of the current thread
* @param self_pid The PID of this thread * @param selfPid The PID of this thread
* @param entryPoint The address to start execution at * @param entryPoint The address to start execution at
* @param entryArg An argument to pass to the process on entry * @param entryArg An argument to pass to the process on entry
* @param stackTop The top of the stack * @param stackTop The top of the stack
@ -39,7 +39,7 @@ namespace skyline::kernel::type {
* @param parent The parent process of this thread * @param parent The parent process of this thread
* @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process * @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process
*/ */
KThread(const DeviceState &state, handle_t handle, pid_t self_pid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr<type::KSharedMemory> &tlsMemory); KThread(const DeviceState &state, KHandle handle, pid_t selfPid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr<type::KSharedMemory> &tlsMemory);
/** /**
* @brief Kills the thread and deallocates the memory allocated for stack. * @brief Kills the thread and deallocates the memory allocated for stack.

View File

@ -23,7 +23,7 @@ namespace skyline::kernel::type {
* @param type The type of the memory * @param type The type of the memory
* @param memState The MemoryState of the chunk of memory * @param memState The MemoryState of the chunk of memory
*/ */
KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState = memory::MemoryStates::TransferMemory); KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState = memory::states::TransferMemory);
/** /**
* @brief Transfers this piece of memory to another process * @brief Transfers this piece of memory to another process

View File

@ -5,7 +5,10 @@
namespace skyline::loader { namespace skyline::loader {
NroLoader::NroLoader(const int romFd) : Loader(romFd) { NroLoader::NroLoader(const int romFd) : Loader(romFd) {
ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader)); ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader));
if (header.magic != constant::NroMagic)
constexpr auto nroMagic = 0x304F524E; // "NRO0" in reverse, this is written at the start of every NRO file
if (header.magic != nroMagic)
throw exception("Invalid NRO magic! 0x{0:X}", header.magic); throw exception("Invalid NRO magic! 0x{0:X}", header.magic);
} }
@ -26,19 +29,19 @@ namespace skyline::loader {
u64 patchSize = patch.size() * sizeof(u32); u64 patchSize = patch.size() * sizeof(u32);
u64 padding = utils::AlignUp(textSize + rodataSize + dataSize + header.bssSize + patchSize, PAGE_SIZE) - (textSize + rodataSize + dataSize + header.bssSize + patchSize); u64 padding = utils::AlignUp(textSize + rodataSize + dataSize + header.bssSize + patchSize, PAGE_SIZE) - (textSize + rodataSize + dataSize + header.bssSize + patchSize);
process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress, textSize, memory::Permission{true, true, true}, memory::MemoryStates::CodeStatic); // R-X process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress, textSize, memory::Permission{true, true, true}, memory::states::CodeStatic); // R-X
state.logger->Debug("Successfully mapped section .text @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress, textSize); state.logger->Debug("Successfully mapped section .text @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress, textSize);
process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize, rodataSize, memory::Permission{true, false, false}, memory::MemoryStates::CodeReadOnly); // R-- process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize, rodataSize, memory::Permission{true, false, false}, memory::states::CodeReadOnly); // R--
state.logger->Debug("Successfully mapped section .ro @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize, rodataSize); state.logger->Debug("Successfully mapped section .ro @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize, rodataSize);
process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize + rodataSize, dataSize, memory::Permission{true, true, false}, memory::MemoryStates::CodeStatic); // RW- process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize + rodataSize, dataSize, memory::Permission{true, true, false}, memory::states::CodeStatic); // RW-
state.logger->Debug("Successfully mapped section .data @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize + rodataSize, dataSize); state.logger->Debug("Successfully mapped section .data @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize + rodataSize, dataSize);
process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize + rodataSize + dataSize, header.bssSize, memory::Permission{true, true, true}, memory::MemoryStates::CodeMutable); // RWX process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize + rodataSize + dataSize, header.bssSize, memory::Permission{true, true, true}, memory::states::CodeMutable); // RWX
state.logger->Debug("Successfully mapped section .bss @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize + rodataSize + dataSize, header.bssSize); state.logger->Debug("Successfully mapped section .bss @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize + rodataSize + dataSize, header.bssSize);
process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize + rodataSize + dataSize + header.bssSize, patchSize + padding, memory::Permission{true, true, true}, memory::MemoryStates::CodeStatic); // RWX process->NewHandle<kernel::type::KPrivateMemory>(constant::BaseAddress + textSize + rodataSize + dataSize + header.bssSize, patchSize + padding, memory::Permission{true, true, true}, memory::states::CodeStatic); // RWX
state.logger->Debug("Successfully mapped section .patch @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize + rodataSize + dataSize + header.bssSize, patchSize); state.logger->Debug("Successfully mapped section .patch @ 0x{0:X}, Size = 0x{1:X}", constant::BaseAddress + textSize + rodataSize + dataSize + header.bssSize, patchSize);
process->WriteMemory(text.data(), constant::BaseAddress, textSize); process->WriteMemory(text.data(), constant::BaseAddress, textSize);

View File

@ -189,7 +189,9 @@ namespace skyline {
if (ctx->sp) if (ctx->sp)
regStr += fmt::format("\nStack Pointer: 0x{:X}", ctx->sp); regStr += fmt::format("\nStack Pointer: 0x{:X}", ctx->sp);
for (u16 index = 0; index < constant::NumRegs - 1; index += 2) { constexpr auto numRegisters = 31; //!< The amount of general-purpose registers in ARMv8
for (u16 index = 0; index < numRegisters - 2; index += 2) {
auto xStr = index < 10 ? " X" : "X"; auto xStr = index < 10 ? " X" : "X";
regStr += fmt::format("\n{}{}: 0x{:<16X} {}{}: 0x{:X}", xStr, index, ctx->registers.regs[index], xStr, index + 1, ctx->registers.regs[index + 1]); regStr += fmt::format("\n{}{}: 0x{:<16X} {}{}: 0x{:X}", xStr, index, ctx->registers.regs[index], xStr, index + 1, ctx->registers.regs[index + 1]);
} }
@ -204,20 +206,26 @@ namespace skyline {
} }
std::vector<u32> NCE::PatchCode(std::vector<u8> &code, u64 baseAddress, i64 offset) { std::vector<u32> NCE::PatchCode(std::vector<u8> &code, u64 baseAddress, i64 offset) {
constexpr auto TpidrroEl0 = 0x5E83; // ID of TPIDRRO_EL0 in MRS
constexpr auto CntfrqEl0 = 0x5F00; // ID of CNTFRQ_EL0 in MRS
constexpr auto CntpctEl0 = 0x5F01; // ID of CNTPCT_EL0 in MRS
constexpr auto CntvctEl0 = 0x5F02; // ID of CNTVCT_EL0 in MRS
constexpr auto TegraX1Freq = 19200000; // The clock frequency of the Tegra X1 (19.2 MHz)
u32 *start = reinterpret_cast<u32 *>(code.data()); u32 *start = reinterpret_cast<u32 *>(code.data());
u32 *end = start + (code.size() / sizeof(u32)); u32 *end = start + (code.size() / sizeof(u32));
i64 patchOffset = offset; i64 patchOffset = offset;
std::vector<u32> patch((guest::saveCtxSize + guest::loadCtxSize + guest::svcHandlerSize) / sizeof(u32)); std::vector<u32> patch((guest::SaveCtxSize + guest::LoadCtxSize + guest::SvcHandlerSize) / sizeof(u32));
std::memcpy(patch.data(), reinterpret_cast<void *>(&guest::SaveCtx), guest::saveCtxSize); std::memcpy(patch.data(), reinterpret_cast<void *>(&guest::SaveCtx), guest::SaveCtxSize);
offset += guest::saveCtxSize; offset += guest::SaveCtxSize;
std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::saveCtxSize, reinterpret_cast<void *>(&guest::LoadCtx), guest::loadCtxSize); std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::SaveCtxSize, reinterpret_cast<void *>(&guest::LoadCtx), guest::LoadCtxSize);
offset += guest::loadCtxSize; offset += guest::LoadCtxSize;
std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::saveCtxSize + guest::loadCtxSize, reinterpret_cast<void *>(&guest::SvcHandler), guest::svcHandlerSize); std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::SaveCtxSize + guest::LoadCtxSize, reinterpret_cast<void *>(&guest::SvcHandler), guest::SvcHandlerSize);
offset += guest::svcHandlerSize; offset += guest::SvcHandlerSize;
static u64 frequency{}; static u64 frequency{};
if (!frequency) if (!frequency)
@ -238,10 +246,10 @@ namespace skyline {
offset += sizeof(u32) * movPc.size(); offset += sizeof(u32) * movPc.size();
instr::Movz movCmd(regs::W1, static_cast<u16>(instrSvc->value)); instr::Movz movCmd(regs::W1, static_cast<u16>(instrSvc->value));
offset += sizeof(movCmd); offset += sizeof(movCmd);
instr::BL bSvcHandler((patchOffset + guest::saveCtxSize + guest::loadCtxSize) - offset); instr::BL bSvcHandler((patchOffset + guest::SaveCtxSize + guest::LoadCtxSize) - offset);
offset += sizeof(bSvcHandler); offset += sizeof(bSvcHandler);
instr::BL bLdCtx((patchOffset + guest::saveCtxSize) - offset); instr::BL bLdCtx((patchOffset + guest::SaveCtxSize) - offset);
offset += sizeof(bLdCtx); offset += sizeof(bLdCtx);
constexpr u32 ldrLr = 0xF84107FE; // LDR LR, [SP], #16 constexpr u32 ldrLr = 0xF84107FE; // LDR LR, [SP], #16
offset += sizeof(ldrLr); offset += sizeof(ldrLr);
@ -259,7 +267,7 @@ namespace skyline {
patch.push_back(ldrLr); patch.push_back(ldrLr);
patch.push_back(bret.raw); patch.push_back(bret.raw);
} else if (instrMrs->Verify()) { } else if (instrMrs->Verify()) {
if (instrMrs->srcReg == constant::TpidrroEl0) { if (instrMrs->srcReg == TpidrroEl0) {
instr::B bJunc(offset); instr::B bJunc(offset);
u32 strX0{}; u32 strX0{};
if (instrMrs->destReg != regs::X0) { if (instrMrs->destReg != regs::X0) {
@ -291,10 +299,10 @@ namespace skyline {
if (ldrX0) if (ldrX0)
patch.push_back(ldrX0); patch.push_back(ldrX0);
patch.push_back(bret.raw); patch.push_back(bret.raw);
} else if (frequency != constant::TegraX1Freq) { } else if (frequency != TegraX1Freq) {
if (instrMrs->srcReg == constant::CntpctEl0) { if (instrMrs->srcReg == CntpctEl0) {
instr::B bJunc(offset); instr::B bJunc(offset);
offset += guest::rescaleClockSize; offset += guest::RescaleClockSize;
instr::Ldr ldr(0xF94003E0); // LDR XOUT, [SP] instr::Ldr ldr(0xF94003E0); // LDR XOUT, [SP]
ldr.destReg = instrMrs->destReg; ldr.destReg = instrMrs->destReg;
offset += sizeof(ldr); offset += sizeof(ldr);
@ -305,14 +313,14 @@ namespace skyline {
*address = bJunc.raw; *address = bJunc.raw;
auto size = patch.size(); auto size = patch.size();
patch.resize(size + (guest::rescaleClockSize / sizeof(u32))); patch.resize(size + (guest::RescaleClockSize / sizeof(u32)));
std::memcpy(patch.data() + size, reinterpret_cast<void *>(&guest::RescaleClock), guest::rescaleClockSize); std::memcpy(patch.data() + size, reinterpret_cast<void *>(&guest::RescaleClock), guest::RescaleClockSize);
patch.push_back(ldr.raw); patch.push_back(ldr.raw);
patch.push_back(addSp); patch.push_back(addSp);
patch.push_back(bret.raw); patch.push_back(bret.raw);
} else if (instrMrs->srcReg == constant::CntfrqEl0) { } else if (instrMrs->srcReg == CntfrqEl0) {
instr::B bJunc(offset); instr::B bJunc(offset);
auto movFreq = instr::MoveU32Reg(static_cast<regs::X>(instrMrs->destReg), constant::TegraX1Freq); auto movFreq = instr::MoveU32Reg(static_cast<regs::X>(instrMrs->destReg), TegraX1Freq);
offset += sizeof(u32) * movFreq.size(); offset += sizeof(u32) * movFreq.size();
instr::B bret(-offset + sizeof(u32)); instr::B bret(-offset + sizeof(u32));
offset += sizeof(bret); offset += sizeof(bret);
@ -323,8 +331,8 @@ namespace skyline {
patch.push_back(bret.raw); patch.push_back(bret.raw);
} }
} else { } else {
if (instrMrs->srcReg == constant::CntpctEl0) { if (instrMrs->srcReg == CntpctEl0) {
instr::Mrs mrs(constant::CntvctEl0, regs::X(instrMrs->destReg)); instr::Mrs mrs(CntvctEl0, regs::X(instrMrs->destReg));
*address = mrs.raw; *address = mrs.raw;
} }
} }

View File

@ -2,13 +2,13 @@
namespace skyline { namespace skyline {
namespace guest { namespace guest {
constexpr size_t saveCtxSize = 20 * sizeof(u32); //!< The size of the SaveCtx function in 32-bit ARMv8 instructions constexpr size_t SaveCtxSize = 20 * sizeof(u32); //!< The size of the SaveCtx function in 32-bit ARMv8 instructions
constexpr size_t loadCtxSize = 20 * sizeof(u32); //!< The size of the LoadCtx function in 32-bit ARMv8 instructions constexpr size_t LoadCtxSize = 20 * sizeof(u32); //!< The size of the LoadCtx function in 32-bit ARMv8 instructions
constexpr size_t rescaleClockSize = 16 * sizeof(u32); //!< The size of the RescaleClock function in 32-bit ARMv8 instructions constexpr size_t RescaleClockSize = 16 * sizeof(u32); //!< The size of the RescaleClock function in 32-bit ARMv8 instructions
#ifdef NDEBUG #ifdef NDEBUG
constexpr size_t svcHandlerSize = 225 * sizeof(u32); //!< The size of the SvcHandler (Release) function in 32-bit ARMv8 instructions constexpr size_t SvcHandlerSize = 225 * sizeof(u32); //!< The size of the SvcHandler (Release) function in 32-bit ARMv8 instructions
#else #else
constexpr size_t svcHandlerSize = 400 * sizeof(u32); //!< The size of the SvcHandler (Debug) function in 32-bit ARMv8 instructions constexpr size_t SvcHandlerSize = 400 * sizeof(u32); //!< The size of the SvcHandler (Debug) function in 32-bit ARMv8 instructions
#endif #endif
/** /**

View File

@ -19,11 +19,11 @@ namespace skyline::kernel {
} }
std::shared_ptr<type::KProcess> OS::CreateProcess(u64 entry, u64 argument, size_t stackSize) { std::shared_ptr<type::KProcess> OS::CreateProcess(u64 entry, u64 argument, size_t stackSize) {
auto stack = std::make_shared<type::KSharedMemory>(state, 0, stackSize, memory::Permission{true, true, false}, memory::MemoryStates::Reserved, MAP_NORESERVE | MAP_STACK); auto stack = std::make_shared<type::KSharedMemory>(state, 0, stackSize, memory::Permission{true, true, false}, memory::states::Reserved, MAP_NORESERVE | MAP_STACK);
stack->guest = stack->kernel; stack->guest = stack->kernel;
if (mprotect(reinterpret_cast<void *>(stack->guest.address), PAGE_SIZE, PROT_NONE)) if (mprotect(reinterpret_cast<void *>(stack->guest.address), PAGE_SIZE, PROT_NONE))
throw exception("Failed to create guard pages"); throw exception("Failed to create guard pages");
auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission{true, true, false}, memory::MemoryStates::Reserved); auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission{true, true, false}, memory::states::Reserved);
tlsMem->guest = tlsMem->kernel; tlsMem->guest = tlsMem->kernel;
pid_t pid = clone(reinterpret_cast<int (*)(void *)>(&guest::GuestEntry), reinterpret_cast<void *>(stack->guest.address + stackSize), CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, reinterpret_cast<void *>(tlsMem->guest.address)); pid_t pid = clone(reinterpret_cast<int (*)(void *)>(&guest::GuestEntry), reinterpret_cast<void *>(stack->guest.address + stackSize), CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, reinterpret_cast<void *>(tlsMem->guest.address));
if (pid == -1) if (pid == -1)

View File

@ -11,7 +11,7 @@ namespace skyline::service::audio {
{0x5, SFUNC(IAudioOut::GetReleasedAudioOutBuffer)}, {0x5, SFUNC(IAudioOut::GetReleasedAudioOutBuffer)},
{0x6, SFUNC(IAudioOut::ContainsAudioOutBuffer)} {0x6, SFUNC(IAudioOut::ContainsAudioOutBuffer)}
}) { }) {
track = state.audio->OpenTrack(channelCount, skyline::audio::constant::SampleRate, [this]() { this->releaseEvent->Signal(); }); track = state.audio->OpenTrack(channelCount, constant::SampleRate, [this]() { this->releaseEvent->Signal(); });
} }
IAudioOut::~IAudioOut() { IAudioOut::~IAudioOut() {
@ -46,7 +46,7 @@ namespace skyline::service::audio {
tmpSampleBuffer.resize(data.sampleSize / sizeof(i16)); tmpSampleBuffer.resize(data.sampleSize / sizeof(i16));
state.process->ReadMemory(tmpSampleBuffer.data(), data.sampleBufferPtr, data.sampleSize); state.process->ReadMemory(tmpSampleBuffer.data(), data.sampleBufferPtr, data.sampleSize);
resampler.ResampleBuffer(tmpSampleBuffer, static_cast<double>(sampleRate) / skyline::audio::constant::SampleRate, channelCount); resampler.ResampleBuffer(tmpSampleBuffer, static_cast<double>(sampleRate) / constant::SampleRate, channelCount);
track->AppendBuffer(tmpSampleBuffer, tag); track->AppendBuffer(tmpSampleBuffer, tag);
} }

View File

@ -20,8 +20,8 @@ namespace skyline::service::audio {
state.logger->Debug("audoutU: Opening an IAudioOut with sample rate: {}, channel count: {}", sampleRate, channelCount); state.logger->Debug("audoutU: Opening an IAudioOut with sample rate: {}, channel count: {}", sampleRate, channelCount);
sampleRate = sampleRate ? sampleRate : skyline::audio::constant::SampleRate; sampleRate = sampleRate ? sampleRate : constant::SampleRate;
channelCount = channelCount ? channelCount : static_cast<u16>(skyline::audio::constant::ChannelCount); channelCount = channelCount ? channelCount : static_cast<u16>(constant::ChannelCount);
manager.RegisterService(std::make_shared<IAudioOut>(state, manager, channelCount, sampleRate), session, response); manager.RegisterService(std::make_shared<IAudioOut>(state, manager, channelCount, sampleRate), session, response);
response.Push<u32>(sampleRate); response.Push<u32>(sampleRate);

View File

@ -3,27 +3,28 @@
#include <kernel/types/KEvent.h> #include <kernel/types/KEvent.h>
#include <services/base_service.h> #include <services/base_service.h>
#include <services/serviceman.h> #include <services/serviceman.h>
namespace skyline {
namespace skyline::service::audio {
namespace constant { namespace constant {
constexpr std::string_view DefaultAudioOutName = "DeviceOut"; //!< The default audio output device name constexpr std::string_view DefaultAudioOutName = "DeviceOut"; //!< The default audio output device name
}; };
/** namespace service::audio {
* @brief IAudioOutManager or audout:u is used to manage audio outputs (https://switchbrew.org/wiki/Audio_services#audout:u)
*/
class IAudioOutManager : public BaseService {
public:
IAudioOutManager(const DeviceState &state, ServiceManager &manager);
/** /**
* @brief Returns a list of all available audio outputs (https://switchbrew.org/wiki/Audio_services#ListAudioOuts) * @brief IAudioOutManager or audout:u is used to manage audio outputs (https://switchbrew.org/wiki/Audio_services#audout:u)
*/ */
void ListAudioOuts(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); class IAudioOutManager : public BaseService {
public:
IAudioOutManager(const DeviceState &state, ServiceManager &manager);
/** /**
* @brief Creates a new audoutU::IAudioOut object and returns a handle to it (https://switchbrew.org/wiki/Audio_services#OpenAudioOut) * @brief Returns a list of all available audio outputs (https://switchbrew.org/wiki/Audio_services#ListAudioOuts)
*/ */
void OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); void ListAudioOuts(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
};
/**
* @brief Creates a new audoutU::IAudioOut object and returns a handle to it (https://switchbrew.org/wiki/Audio_services#OpenAudioOut)
*/
void OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
};
}
} }

View File

@ -13,7 +13,7 @@ namespace skyline::service::audio::IAudioRenderer {
{0x6, SFUNC(IAudioRenderer::Stop)}, {0x6, SFUNC(IAudioRenderer::Stop)},
{0x7, SFUNC(IAudioRenderer::QuerySystemEvent)}, {0x7, SFUNC(IAudioRenderer::QuerySystemEvent)},
}) { }) {
track = state.audio->OpenTrack(skyline::audio::constant::ChannelCount, params.sampleRate, [this]() { this->releaseEvent->Signal(); }); track = state.audio->OpenTrack(constant::ChannelCount, params.sampleRate, [this]() { this->releaseEvent->Signal(); });
track->Start(); track->Start();
memoryPools.resize(memoryPoolCount); memoryPools.resize(memoryPoolCount);
@ -132,7 +132,7 @@ namespace skyline::service::audio::IAudioRenderer {
void IAudioRenderer::MixFinalBuffer() { void IAudioRenderer::MixFinalBuffer() {
int setIndex = 0; int setIndex = 0;
sampleBuffer.resize(samplesPerBuffer * skyline::audio::constant::ChannelCount); sampleBuffer.resize(static_cast<size_t>(samplesPerBuffer * constant::ChannelCount));
for (auto &voice : voices) { for (auto &voice : voices) {
if (!voice.Playable()) if (!voice.Playable())
@ -149,7 +149,7 @@ namespace skyline::service::audio::IAudioRenderer {
if (voiceBufferSize == 0) if (voiceBufferSize == 0)
break; break;
pendingSamples -= voiceBufferSize / skyline::audio::constant::ChannelCount; pendingSamples -= voiceBufferSize / constant::ChannelCount;
for (int i = voiceBufferOffset; i < voiceBufferOffset + voiceBufferSize; i++) { for (int i = voiceBufferOffset; i < voiceBufferOffset + voiceBufferSize; i++) {
if (setIndex == bufferOffset) { if (setIndex == bufferOffset) {

View File

@ -9,127 +9,130 @@
#include "voice.h" #include "voice.h"
#include "revisionInfo.h" #include "revisionInfo.h"
namespace skyline::service::audio::IAudioRenderer { namespace skyline {
namespace constant { namespace constant {
constexpr int BufferAlignment = 0x40; //!< The alignment for all audren buffers constexpr auto BufferAlignment = 0x40; //!< The alignment for all audren buffers
} }
/**
* @brief The parameters used to configure an IAudioRenderer
*/
struct AudioRendererParams {
u32 sampleRate; //!< The sample rate to use for the renderer
u32 sampleCount; //!< The buffer sample count
u32 mixBufferCount; //!< The amount of mix buffers to use
u32 subMixCount; //!< The amount of sub mixes to use
u32 voiceCount; //!< The amount of voices to use
u32 sinkCount; //!< The amount of sinks to use
u32 effectCount; //!< The amount of effects to use
u32 performanceManagerCount; //!< The amount of performance managers to use
u32 voiceDropEnable; //!< Whether to enable voice drop
u32 splitterCount; //!< The amount of splitters to use
u32 splitterDestinationDataCount; //!< The amount of splitter destination outputs to use
u32 _unk0_;
u32 revision; //!< The revision of audren to use
};
static_assert(sizeof(AudioRendererParams) == 0x34);
/**
* @brief Header containing information about the software side audren implementation
*/
struct UpdateDataHeader {
u32 revision; //!< Revision of the software implementation
u32 behaviorSize; //!< The total size of the behaviour info
u32 memoryPoolSize; //!< The total size of all MemoryPoolIn structs
u32 voiceSize; //!< The total size of all VoiceIn structs
u32 voiceResourceSize; //!< The total size of the voice resources
u32 effectSize; //!< The total size of all EffectIn structs
u32 mixSize; //!< The total size of all mixer descriptors in the input
u32 sinkSize; //!< The total size of all sink descriptors in the input
u32 performanceManagerSize; //!< The total size of all performance manager descriptors in the input
u32 _unk0_;
u32 elapsedFrameCountInfoSize; //!< The total size of all the elapsed frame info
u32 _unk1_[4];
u32 totalSize; //!< The total size of the whole input
};
static_assert(sizeof(UpdateDataHeader) == 0x40);
/**
* @brief IAudioRenderer is used to control an audio renderer output (https://switchbrew.org/wiki/Audio_services#IAudioRenderer)
*/
class IAudioRenderer : public BaseService {
private:
AudioRendererParams rendererParams; //!< The parameters to use for the renderer
RevisionInfo revisionInfo{}; //!< Stores info about supported features for the audren revision used
std::shared_ptr<skyline::audio::AudioTrack> track; //!< The audio track associated with the audio renderer
std::shared_ptr<type::KEvent> releaseEvent; //!< The KEvent that is signalled when a buffer has been released
std::vector<MemoryPool> memoryPools; //!< An vector of all memory pools that the guest may need
std::vector<Effect> effects; //!< An vector of all effects that the guest may need
std::vector<Voice> voices; //!< An vector of all voices that the guest may need
std::vector<i16> sampleBuffer; //!< The final output data that is appended to the stream
skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped}; //!< The current state of playback
const size_t memoryPoolCount; //!< The amount of memory pools the guest may need
const int samplesPerBuffer; //!< The amount of samples each appended buffer should contain
namespace service::audio::IAudioRenderer {
/** /**
* @brief Obtains new sample data from voices and mixes it together into the sample buffer * @brief The parameters used to configure an IAudioRenderer
*/ */
void MixFinalBuffer(); struct AudioRendererParams {
u32 sampleRate; //!< The sample rate to use for the renderer
u32 sampleCount; //!< The buffer sample count
u32 mixBufferCount; //!< The amount of mix buffers to use
u32 subMixCount; //!< The amount of sub mixes to use
u32 voiceCount; //!< The amount of voices to use
u32 sinkCount; //!< The amount of sinks to use
u32 effectCount; //!< The amount of effects to use
u32 performanceManagerCount; //!< The amount of performance managers to use
u32 voiceDropEnable; //!< Whether to enable voice drop
u32 splitterCount; //!< The amount of splitters to use
u32 splitterDestinationDataCount; //!< The amount of splitter destination outputs to use
u32 _unk0_;
u32 revision; //!< The revision of audren to use
};
static_assert(sizeof(AudioRendererParams) == 0x34);
/** /**
* @brief Appends all released buffers with new mixed sample data * @brief Header containing information about the software side audren implementation
*/ */
void UpdateAudio(); struct UpdateDataHeader {
u32 revision; //!< Revision of the software implementation
public: u32 behaviorSize; //!< The total size of the behaviour info
/** u32 memoryPoolSize; //!< The total size of all MemoryPoolIn structs
* @param params The parameters to use for rendering u32 voiceSize; //!< The total size of all VoiceIn structs
*/ u32 voiceResourceSize; //!< The total size of the voice resources
IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParams &params); u32 effectSize; //!< The total size of all EffectIn structs
u32 mixSize; //!< The total size of all mixer descriptors in the input
u32 sinkSize; //!< The total size of all sink descriptors in the input
u32 performanceManagerSize; //!< The total size of all performance manager descriptors in the input
u32 _unk0_;
u32 elapsedFrameCountInfoSize; //!< The total size of all the elapsed frame info
u32 _unk1_[4];
u32 totalSize; //!< The total size of the whole input
};
static_assert(sizeof(UpdateDataHeader) == 0x40);
/** /**
* @brief Closes the audio track * @brief IAudioRenderer is used to control an audio renderer output (https://switchbrew.org/wiki/Audio_services#IAudioRenderer)
*/
~IAudioRenderer();
/**
* @brief Returns the sample rate (https://switchbrew.org/wiki/Audio_services#GetSampleRate)
*/
void GetSampleRate(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Returns the sample count (https://switchbrew.org/wiki/Audio_services#GetSampleCount)
*/ */
void GetSampleCount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); class IAudioRenderer : public BaseService {
private:
AudioRendererParams rendererParams; //!< The parameters to use for the renderer
RevisionInfo revisionInfo{}; //!< Stores info about supported features for the audren revision used
std::shared_ptr<skyline::audio::AudioTrack> track; //!< The audio track associated with the audio renderer
std::shared_ptr<type::KEvent> releaseEvent; //!< The KEvent that is signalled when a buffer has been released
std::vector<MemoryPool> memoryPools; //!< An vector of all memory pools that the guest may need
std::vector<Effect> effects; //!< An vector of all effects that the guest may need
std::vector<Voice> voices; //!< An vector of all voices that the guest may need
std::vector<i16> sampleBuffer; //!< The final output data that is appended to the stream
/** skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped}; //!< The current state of playback
* @brief Returns the number of mix buffers (https://switchbrew.org/wiki/Audio_services#GetMixBufferCount) const size_t memoryPoolCount; //!< The amount of memory pools the guest may need
*/ const int samplesPerBuffer; //!< The amount of samples each appended buffer should contain
void GetMixBufferCount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/** /**
* @brief Returns the state of the renderer (https://switchbrew.org/wiki/Audio_services#GetAudioRendererState) (stubbed)? * @brief Obtains new sample data from voices and mixes it together into the sample buffer
*/ */
void GetState(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); void MixFinalBuffer();
/** /**
* @brief Updates the audio renderer state and appends new data to playback buffers * @brief Appends all released buffers with new mixed sample data
*/ */
void RequestUpdate(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); void UpdateAudio();
/** public:
* @brief Start the audio stream from the renderer /**
*/ * @param params The parameters to use for rendering
void Start(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); */
IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParams &params);
/** /**
* @brief Stop the audio stream from the renderer * @brief Closes the audio track
*/ */
void Stop(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); ~IAudioRenderer();
/** /**
* @brief Returns a handle to the sample release KEvent * @brief Returns the sample rate (https://switchbrew.org/wiki/Audio_services#GetSampleRate)
*/ */
void QuerySystemEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); void GetSampleRate(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
};
/**
* @brief Returns the sample count (https://switchbrew.org/wiki/Audio_services#GetSampleCount)
*/
void GetSampleCount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Returns the number of mix buffers (https://switchbrew.org/wiki/Audio_services#GetMixBufferCount)
*/
void GetMixBufferCount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Returns the state of the renderer (https://switchbrew.org/wiki/Audio_services#GetAudioRendererState) (stubbed)?
*/
void GetState(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Updates the audio renderer state and appends new data to playback buffers
*/
void RequestUpdate(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Start the audio stream from the renderer
*/
void Start(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Stop the audio stream from the renderer
*/
void Stop(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Returns a handle to the sample release KEvent
*/
void QuerySystemEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
};
}
} }

View File

@ -1,8 +1,7 @@
#pragma once #pragma once
#include <common.h> #include <common.h>
namespace skyline {
namespace skyline::service::audio::IAudioRenderer {
namespace constant { namespace constant {
constexpr u32 SupportedRevision = 7; //!< The audren revision our implementation supports constexpr u32 SupportedRevision = 7; //!< The audren revision our implementation supports
constexpr u32 Rev0Magic = ('R' << 0) | ('E' << 8) | ('V' << 16) | ('0' << 24); //!< The HOS 1.0 revision magic constexpr u32 Rev0Magic = ('R' << 0) | ('E' << 8) | ('V' << 16) | ('0' << 24); //!< The HOS 1.0 revision magic
@ -17,67 +16,69 @@ namespace skyline::service::audio::IAudioRenderer {
}; };
} }
/** namespace service::audio::IAudioRenderer {
* @brief Extracts the audren version from a revision magic /**
* @param revision The revision magic * @brief Extracts the audren version from a revision magic
* @return The revision number from the magic * @param revision The revision magic
*/ * @return The revision number from the magic
inline u32 ExtractVersionFromRevision(u32 revision) { */
return (revision - constant::Rev0Magic) >> 24; inline u32 ExtractVersionFromRevision(u32 revision) {
return (revision - constant::Rev0Magic) >> 24;
}
/**
* @brief The RevisionInfo class is used to query the supported features of various audren revisions
*/
class RevisionInfo {
private:
u32 userRevision; //!< The current audren revision of the guest
public:
/**
* @brief Extracts the audren revision from the magic and sets the behaviour revision to it
* @param revision The revision magic from guest
*/
inline void SetUserRevision(u32 revision) {
userRevision = ExtractVersionFromRevision(revision);
if (userRevision > constant::SupportedRevision)
throw exception("Unsupported audren revision: {}", userRevision);
}
/**
* @return Whether the splitter is supported
*/
inline bool SplitterSupported() {
return userRevision >= constant::supportTags::Splitter;
}
/**
* @return Whether the splitter is fixed
*/
inline bool SplitterBugFixed() {
return userRevision >= constant::supportTags::SplitterBugFix;
}
/**
* @return Whether the new performance metrics format is used
*/
inline bool UsesPerformanceMetricDataFormatV2() {
return userRevision >= constant::supportTags::PerformanceMetricsDataFormatV2;
}
/**
* @return Whether varying command buffer sizes are supported
*/
inline bool VaradicCommandBufferSizeSupported() {
return userRevision >= constant::supportTags::VaradicCommandBufferSize;
}
/**
* @return Whether elapsed frame counting is supported
*/
inline bool ElapsedFrameCountSupported() {
return userRevision >= constant::supportTags::ElapsedFrameCount;
}
};
} }
/**
* @brief The RevisionInfo class is used to query the supported features of various audren revisions
*/
class RevisionInfo {
private:
u32 userRevision; //!< The current audren revision of the guest
public:
/**
* @brief Extracts the audren revision from the magic and sets the behaviour revision to it
* @param revision The revision magic from guest
*/
inline void SetUserRevision(u32 revision) {
userRevision = ExtractVersionFromRevision(revision);
if (userRevision > constant::SupportedRevision)
throw exception("Unsupported audren revision: {}", userRevision);
}
/**
* @return Whether the splitter is supported
*/
inline bool SplitterSupported() {
return userRevision >= constant::supportTags::Splitter;
}
/**
* @return Whether the splitter is fixed
*/
inline bool SplitterBugFixed() {
return userRevision >= constant::supportTags::SplitterBugFix;
}
/**
* @return Whether the new performance metrics format is used
*/
inline bool UsesPerformanceMetricDataFormatV2() {
return userRevision >= constant::supportTags::PerformanceMetricsDataFormatV2;
}
/**
* @return Whether varying command buffer sizes are supported
*/
inline bool VaradicCommandBufferSizeSupported() {
return userRevision >= constant::supportTags::VaradicCommandBufferSize;
}
/**
* @return Whether elapsed frame counting is supported
*/
inline bool ElapsedFrameCountSupported() {
return userRevision >= constant::supportTags::ElapsedFrameCount;
}
};
} }

View File

@ -55,15 +55,15 @@ namespace skyline::service::audio::IAudioRenderer {
throw exception("Unsupported voice PCM format: {}", pcmFormat); throw exception("Unsupported voice PCM format: {}", pcmFormat);
} }
if (sampleRate != skyline::audio::constant::SampleRate) if (sampleRate != constant::SampleRate)
sampleBuffer = resampler.ResampleBuffer(sampleBuffer, static_cast<double>(sampleRate) / skyline::audio::constant::SampleRate, channelCount); sampleBuffer = resampler.ResampleBuffer(sampleBuffer, static_cast<double>(sampleRate) / constant::SampleRate, channelCount);
if (channelCount == 1 && skyline::audio::constant::ChannelCount != channelCount) { if (channelCount == 1 && constant::ChannelCount != channelCount) {
size_t originalSize = sampleBuffer.size(); size_t originalSize = sampleBuffer.size();
sampleBuffer.resize((originalSize / channelCount) * skyline::audio::constant::ChannelCount); sampleBuffer.resize((originalSize / channelCount) * constant::ChannelCount);
for (size_t monoIndex = originalSize - 1, targetIndex = sampleBuffer.size(); monoIndex > 0; monoIndex--) for (size_t monoIndex = originalSize - 1, targetIndex = sampleBuffer.size(); monoIndex > 0; monoIndex--)
for (uint i = 0; i < skyline::audio::constant::ChannelCount; i++) for (uint i = 0; i < constant::ChannelCount; i++)
sampleBuffer[--targetIndex] = sampleBuffer[monoIndex]; sampleBuffer[--targetIndex] = sampleBuffer[monoIndex];
} }
} }
@ -82,9 +82,9 @@ namespace skyline::service::audio::IAudioRenderer {
} }
outOffset = sampleOffset; outOffset = sampleOffset;
outSize = std::min(maxSamples * skyline::audio::constant::ChannelCount, static_cast<int>(sampleBuffer.size() - sampleOffset)); outSize = std::min(maxSamples * constant::ChannelCount, static_cast<int>(sampleBuffer.size() - sampleOffset));
output.playedSamplesCount += outSize / skyline::audio::constant::ChannelCount; output.playedSamplesCount += outSize / constant::ChannelCount;
sampleOffset += outSize; sampleOffset += outSize;
if (sampleOffset == sampleBuffer.size()) { if (sampleOffset == sampleBuffer.size()) {

View File

@ -91,7 +91,7 @@ namespace skyline::service::audio::IAudioRenderer {
bool acquired{false}; //!< If the voice is in use bool acquired{false}; //!< If the voice is in use
bool bufferReload{true}; //!< If the buffer needs to be updated bool bufferReload{true}; //!< If the buffer needs to be updated
int bufferIndex{}; //!< The index of the wave buffer currently in use uint bufferIndex{}; //!< The index of the wave buffer currently in use
int sampleOffset{}; //!< The offset in the sample data of the current wave buffer int sampleOffset{}; //!< The offset in the sample data of the current wave buffer
int sampleRate{}; //!< The sample rate of the sample data int sampleRate{}; //!< The sample rate of the sample data
int channelCount{}; //!< The amount of channels in the sample data int channelCount{}; //!< The amount of channels in the sample data

View File

@ -25,25 +25,25 @@ namespace skyline::service::audio {
u32 totalMixCount = params.subMixCount + 1; u32 totalMixCount = params.subMixCount + 1;
i64 size = utils::AlignUp(params.mixBufferCount * 4, IAudioRenderer::constant::BufferAlignment) + i64 size = utils::AlignUp(params.mixBufferCount * 4, constant::BufferAlignment) +
params.subMixCount * 0x400 + params.subMixCount * 0x400 +
totalMixCount * 0x940 + totalMixCount * 0x940 +
params.voiceCount * 0x3F0 + params.voiceCount * 0x3F0 +
utils::AlignUp(totalMixCount * 8, 16) + utils::AlignUp(totalMixCount * 8, 16) +
utils::AlignUp(params.voiceCount * 8, 16) + utils::AlignUp(params.voiceCount * 8, 16) +
utils::AlignUp(((params.sinkCount + params.subMixCount) * 0x3C0 + params.sampleCount * 4) * (params.mixBufferCount + 6), IAudioRenderer::constant::BufferAlignment) + utils::AlignUp(((params.sinkCount + params.subMixCount) * 0x3C0 + params.sampleCount * 4) * (params.mixBufferCount + 6), constant::BufferAlignment) +
(params.sinkCount + params.subMixCount) * 0x2C0 + (params.sinkCount + params.subMixCount) * 0x2C0 +
(params.effectCount + params.voiceCount * 4) * 0x30 + (params.effectCount + params.voiceCount * 4) * 0x30 +
0x50; 0x50;
if (revisionInfo.SplitterSupported()) { if (revisionInfo.SplitterSupported()) {
i32 nodeStateWorkSize = utils::AlignUp(totalMixCount, IAudioRenderer::constant::BufferAlignment); i32 nodeStateWorkSize = utils::AlignUp(totalMixCount, constant::BufferAlignment);
if (nodeStateWorkSize < 0) if (nodeStateWorkSize < 0)
nodeStateWorkSize |= 7; nodeStateWorkSize |= 7;
nodeStateWorkSize = 4 * (totalMixCount * totalMixCount) + 12 * totalMixCount + 2 * (nodeStateWorkSize / 8); nodeStateWorkSize = 4 * (totalMixCount * totalMixCount) + 12 * totalMixCount + 2 * (nodeStateWorkSize / 8);
i32 edgeMatrixWorkSize = utils::AlignUp(totalMixCount * totalMixCount, IAudioRenderer::constant::BufferAlignment); i32 edgeMatrixWorkSize = utils::AlignUp(totalMixCount * totalMixCount, constant::BufferAlignment);
if (edgeMatrixWorkSize < 0) if (edgeMatrixWorkSize < 0)
edgeMatrixWorkSize |= 7; edgeMatrixWorkSize |= 7;

View File

@ -3,14 +3,19 @@
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
namespace skyline::service { namespace skyline::service {
Parcel::Parcel(kernel::ipc::InputBuffer &buffer, const DeviceState &state) : Parcel(buffer.address, buffer.size, state) {} Parcel::Parcel(kernel::ipc::InputBuffer &buffer, const DeviceState &state, bool hasToken) : Parcel(buffer.address, buffer.size, state, hasToken) {}
Parcel::Parcel(u64 address, u64 size, const DeviceState &state) : state(state) { Parcel::Parcel(u64 address, u64 size, const DeviceState &state, bool hasToken) : state(state) {
state.process->ReadMemory(&header, address, sizeof(ParcelHeader)); state.process->ReadMemory(&header, address, sizeof(ParcelHeader));
if (size < (sizeof(ParcelHeader) + header.dataSize + header.objectsSize)) if (size < (sizeof(ParcelHeader) + header.dataSize + header.objectsSize))
throw exception("The size of the parcel according to the header exceeds the specified size"); throw exception("The size of the parcel according to the header exceeds the specified size");
data.resize(header.dataSize);
state.process->ReadMemory(data.data(), address + header.dataOffset, header.dataSize); constexpr auto tokenLength = 0x50; // The length of the token on BufferQueue parcels
data.resize(header.dataSize - (hasToken ? tokenLength : 0));
state.process->ReadMemory(data.data(), address + header.dataOffset + (hasToken ? tokenLength : 0), header.dataSize - (hasToken ? tokenLength : 0));
objects.resize(header.objectsSize); objects.resize(header.objectsSize);
state.process->ReadMemory(objects.data(), address + header.objectsOffset, header.objectsSize); state.process->ReadMemory(objects.data(), address + header.objectsOffset, header.objectsSize);
} }
@ -24,14 +29,19 @@ namespace skyline::service {
u64 Parcel::WriteParcel(u64 address, u64 maxSize) { u64 Parcel::WriteParcel(u64 address, u64 maxSize) {
header.dataSize = static_cast<u32>(data.size()); header.dataSize = static_cast<u32>(data.size());
header.dataOffset = sizeof(ParcelHeader); header.dataOffset = sizeof(ParcelHeader);
header.objectsSize = static_cast<u32>(objects.size()); header.objectsSize = static_cast<u32>(objects.size());
header.objectsOffset = sizeof(ParcelHeader) + data.size(); header.objectsOffset = sizeof(ParcelHeader) + data.size();
u64 totalSize = sizeof(ParcelHeader) + header.dataSize + header.objectsSize;
auto totalSize = sizeof(ParcelHeader) + header.dataSize + header.objectsSize;
if (maxSize < totalSize) if (maxSize < totalSize)
throw exception("The size of the parcel exceeds maxSize"); throw exception("The size of the parcel exceeds maxSize");
state.process->WriteMemory(header, address); state.process->WriteMemory(header, address);
state.process->WriteMemory(data.data(), address + header.dataOffset, data.size()); state.process->WriteMemory(data.data(), address + header.dataOffset, data.size());
state.process->WriteMemory(objects.data(), address + header.objectsOffset, objects.size()); state.process->WriteMemory(objects.data(), address + header.objectsOffset, objects.size());
return totalSize; return totalSize;
} }
} }

View File

@ -10,7 +10,7 @@ namespace skyline::service {
class Parcel { class Parcel {
private: private:
/** /**
* @brief This holds the header of a parcel * @brief The header of an Android Parcel structure
*/ */
struct ParcelHeader { struct ParcelHeader {
u32 dataSize; u32 dataSize;
@ -30,16 +30,18 @@ namespace skyline::service {
* @brief This constructor fills in the Parcel object with data from a IPC buffer * @brief This constructor fills in the Parcel object with data from a IPC buffer
* @param buffer The buffer that contains the parcel * @param buffer The buffer that contains the parcel
* @param state The state of the device * @param state The state of the device
* @param hasToken If the parcel starts with a token, it is skipped if this flag is true
*/ */
Parcel(kernel::ipc::InputBuffer &buffer, const DeviceState &state); Parcel(kernel::ipc::InputBuffer &buffer, const DeviceState &state, bool hasToken = false);
/** /**
* @brief This constructor fills in the Parcel object with data from a Parcel on a remote process * @brief This constructor fills in the Parcel object with data from a Parcel on a remote process
* @param address The remote address of the parcel * @param address The remote address of the parcel
* @param size The size of the parcel * @param size The size of the parcel
* @param state The state of the device * @param state The state of the device
* @param hasToken If the parcel starts with a token, it is skipped if this flag is true
*/ */
Parcel(u64 address, u64 size, const DeviceState &state); Parcel(u64 address, u64 size, const DeviceState &state, bool hasToken = false);
/** /**
* @brief This constructor is used to create an empty parcel then write to a process * @brief This constructor is used to create an empty parcel then write to a process

View File

@ -6,7 +6,7 @@ namespace skyline::service::hid {
}) {} }) {}
void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
hidSharedMemory = std::make_shared<kernel::type::KSharedMemory>(state, NULL, constant::hidSharedMemSize, memory::Permission{true, false, false}); hidSharedMemory = std::make_shared<kernel::type::KSharedMemory>(state, NULL, constant::HidSharedMemSize, memory::Permission{true, false, false});
auto handle = state.process->InsertItem<type::KSharedMemory>(hidSharedMemory); auto handle = state.process->InsertItem<type::KSharedMemory>(hidSharedMemory);
state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle); state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);

View File

@ -3,24 +3,25 @@
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <services/base_service.h> #include <services/base_service.h>
#include <services/serviceman.h> #include <services/serviceman.h>
namespace skyline {
namespace skyline::service::hid {
namespace constant { namespace constant {
constexpr size_t hidSharedMemSize = 0x40000; //!< The size of HID Shared Memory (https://switchbrew.org/wiki/HID_Shared_Memory) constexpr auto HidSharedMemSize = 0x40000; //!< The size of HID Shared Memory (https://switchbrew.org/wiki/HID_Shared_Memory)
} }
/** namespace service::hid {
* @brief IAppletResource is used to get a handle to the HID shared memory (https://switchbrew.org/wiki/HID_services#IAppletResource)
*/
class IAppletResource : public BaseService {
public:
std::shared_ptr <type::KSharedMemory> hidSharedMemory; //!< A pointer to HID shared memory
IAppletResource(const DeviceState &state, ServiceManager &manager);
/** /**
* @brief This opens a handle to HID shared memory (https://switchbrew.org/wiki/HID_services#GetSharedMemoryHandle) * @brief IAppletResource is used to get a handle to the HID shared memory (https://switchbrew.org/wiki/HID_services#IAppletResource)
*/ */
void GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); class IAppletResource : public BaseService {
}; public:
std::shared_ptr<type::KSharedMemory> hidSharedMemory; //!< A pointer to HID shared memory
IAppletResource(const DeviceState &state, ServiceManager &manager);
/**
* @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);
};
}
} }

View File

@ -14,7 +14,7 @@ namespace skyline::service::hosbinder {
}) {} }) {}
void IHOSBinderDriver::RequestBuffer(Parcel &in, Parcel &out) { void IHOSBinderDriver::RequestBuffer(Parcel &in, Parcel &out) {
u32 slot = *reinterpret_cast<u32 *>(in.data.data() + constant::TokenLength); u32 slot = *reinterpret_cast<u32 *>(in.data.data());
out.WriteData<u32>(1); out.WriteData<u32>(1);
out.WriteData<u32>(sizeof(GbpBuffer)); out.WriteData<u32>(sizeof(GbpBuffer));
out.WriteData<u32>(0); out.WriteData<u32>(0);
@ -29,7 +29,7 @@ namespace skyline::service::hosbinder {
u32 height; u32 height;
u32 timestamps; u32 timestamps;
u32 usage; u32 usage;
} *data = reinterpret_cast<Data *>(in.data.data() + constant::TokenLength); } *data = reinterpret_cast<Data *>(in.data.data());
i64 slot{-1}; i64 slot{-1};
while (slot == -1) { while (slot == -1) {
@ -66,7 +66,7 @@ namespace skyline::service::hosbinder {
u64 _unk0_; u64 _unk0_;
u32 swapInterval; u32 swapInterval;
Fence fence[4]; Fence fence[4];
} *data = reinterpret_cast<Data *>(in.data.data() + constant::TokenLength); } *data = reinterpret_cast<Data *>(in.data.data());
auto buffer = queue.at(data->slot); auto buffer = queue.at(data->slot);
buffer->status = BufferStatus::Queued; buffer->status = BufferStatus::Queued;
@ -100,13 +100,13 @@ namespace skyline::service::hosbinder {
struct Data { struct Data {
u32 slot; u32 slot;
Fence fence[4]; Fence fence[4];
} *data = reinterpret_cast<Data *>(parcel.data.data() + constant::TokenLength); } *data = reinterpret_cast<Data *>(parcel.data.data());
FreeBuffer(data->slot); FreeBuffer(data->slot);
state.logger->Debug("CancelBuffer: Slot: {}", data->slot); state.logger->Debug("CancelBuffer: Slot: {}", data->slot);
} }
void IHOSBinderDriver::SetPreallocatedBuffer(Parcel &parcel) { void IHOSBinderDriver::SetPreallocatedBuffer(Parcel &parcel) {
auto pointer = parcel.data.data() + constant::TokenLength; auto pointer = parcel.data.data();
struct Data { struct Data {
u32 slot; u32 slot;
u32 _unk0_; u32 _unk0_;
@ -159,7 +159,7 @@ namespace skyline::service::hosbinder {
auto layerId = request.Pop<u32>(); auto layerId = request.Pop<u32>();
auto code = request.Pop<TransactionCode>(); auto code = request.Pop<TransactionCode>();
Parcel in(request.inputBuf.at(0), state); Parcel in(request.inputBuf.at(0), state, true);
Parcel out(state); Parcel out(state);
state.logger->Debug("TransactParcel: Layer ID: {}, Code: {}", layerId, code); state.logger->Debug("TransactParcel: Layer ID: {}, Code: {}", layerId, code);
@ -211,7 +211,7 @@ namespace skyline::service::hosbinder {
} }
void IHOSBinderDriver::GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IHOSBinderDriver::GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
handle_t handle = state.process->InsertItem(state.gpu->bufferEvent); KHandle handle = state.process->InsertItem(state.gpu->bufferEvent);
state.logger->Debug("Display Buffer Event Handle: 0x{:X}", handle); state.logger->Debug("Display Buffer Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);
response.Push<u32>(constant::status::Success); response.Push<u32>(constant::status::Success);
@ -219,7 +219,7 @@ namespace skyline::service::hosbinder {
void IHOSBinderDriver::SetDisplay(const std::string &name) { void IHOSBinderDriver::SetDisplay(const std::string &name) {
try { try {
const auto type = displayTypeMap.at(name); const auto type = DisplayTypeMap.at(name);
if (displayId == DisplayId::Null) if (displayId == DisplayId::Null)
displayId = type; displayId = type;
else else

View File

@ -17,7 +17,7 @@ namespace skyline::service::hosbinder {
/** /**
* @brief A mapping from a display's name to it's displayType entry * @brief A mapping from a display's name to it's displayType entry
*/ */
static const std::unordered_map <std::string, DisplayId> displayTypeMap{ static const std::unordered_map <std::string, DisplayId> DisplayTypeMap{
{"Default", DisplayId::Default}, {"Default", DisplayId::Default},
{"External", DisplayId::External}, {"External", DisplayId::External},
{"Edid", DisplayId::Edid}, {"Edid", DisplayId::Edid},

View File

@ -32,8 +32,8 @@ namespace skyline::service::nvdrv::device {
NvMapObject(u32 id, u32 size); NvMapObject(u32 id, u32 size);
}; };
std::unordered_map<handle_t, std::shared_ptr<NvMapObject>> handleTable; //!< A mapping from a handle to it's corresponding NvMapObject std::unordered_map<KHandle, std::shared_ptr<NvMapObject>> handleTable; //!< A mapping from a handle to it's corresponding NvMapObject
handle_t handleIndex{1}; //!< This is used to keep track of the next handle to allocate KHandle handleIndex{1}; //!< This is used to keep track of the next handle to allocate
u32 idIndex{1}; //!< This is used to keep track of the next ID to allocate u32 idIndex{1}; //!< This is used to keep track of the next ID to allocate
NvMap(const DeviceState &state); NvMap(const DeviceState &state);

View File

@ -70,7 +70,7 @@ namespace skyline::service {
return serviceObj; return serviceObj;
} }
handle_t ServiceManager::NewSession(const Service serviceType) { KHandle ServiceManager::NewSession(const Service serviceType) {
std::lock_guard serviceGuard(mutex); std::lock_guard serviceGuard(mutex);
return state.process->NewHandle<type::KSession>(CreateService(serviceType)).handle; return state.process->NewHandle<type::KSession>(CreateService(serviceType)).handle;
} }
@ -78,7 +78,7 @@ namespace skyline::service {
std::shared_ptr<BaseService> ServiceManager::NewService(const std::string &serviceName, type::KSession &session, ipc::IpcResponse &response) { std::shared_ptr<BaseService> ServiceManager::NewService(const std::string &serviceName, type::KSession &session, ipc::IpcResponse &response) {
std::lock_guard serviceGuard(mutex); std::lock_guard serviceGuard(mutex);
auto serviceObject = CreateService(ServiceString.at(serviceName)); auto serviceObject = CreateService(ServiceString.at(serviceName));
handle_t handle{}; KHandle handle{};
if (response.isDomain) { if (response.isDomain) {
session.domainTable[++session.handleIndex] = serviceObject; session.domainTable[++session.handleIndex] = serviceObject;
response.domainObjects.push_back(session.handleIndex); response.domainObjects.push_back(session.handleIndex);
@ -93,7 +93,7 @@ namespace skyline::service {
void ServiceManager::RegisterService(std::shared_ptr<BaseService> serviceObject, type::KSession &session, ipc::IpcResponse &response, bool submodule) { // NOLINT(performance-unnecessary-value-param) void ServiceManager::RegisterService(std::shared_ptr<BaseService> serviceObject, type::KSession &session, ipc::IpcResponse &response, bool submodule) { // NOLINT(performance-unnecessary-value-param)
std::lock_guard serviceGuard(mutex); std::lock_guard serviceGuard(mutex);
handle_t handle{}; KHandle handle{};
if (response.isDomain) { if (response.isDomain) {
session.domainTable[session.handleIndex] = serviceObject; session.domainTable[session.handleIndex] = serviceObject;
response.domainObjects.push_back(session.handleIndex); response.domainObjects.push_back(session.handleIndex);
@ -107,7 +107,7 @@ namespace skyline::service {
state.logger->Debug("Service has been registered: \"{}\" (0x{:X})", serviceObject->serviceName, handle); state.logger->Debug("Service has been registered: \"{}\" (0x{:X})", serviceObject->serviceName, handle);
} }
void ServiceManager::CloseSession(const handle_t handle) { void ServiceManager::CloseSession(const KHandle handle) {
std::lock_guard serviceGuard(mutex); std::lock_guard serviceGuard(mutex);
auto session = state.process->GetHandle<type::KSession>(handle); auto session = state.process->GetHandle<type::KSession>(handle);
if (session->serviceStatus == type::KSession::ServiceStatus::Open) { if (session->serviceStatus == type::KSession::ServiceStatus::Open) {
@ -121,7 +121,7 @@ namespace skyline::service {
} }
}; };
void ServiceManager::SyncRequestHandler(const handle_t handle) { void ServiceManager::SyncRequestHandler(const KHandle handle) {
auto session = state.process->GetHandle<type::KSession>(handle); auto session = state.process->GetHandle<type::KSession>(handle);
state.logger->Debug("----Start----"); state.logger->Debug("----Start----");
state.logger->Debug("Handle is 0x{:X}", handle); state.logger->Debug("Handle is 0x{:X}", handle);

View File

@ -32,7 +32,7 @@ namespace skyline::service {
* @param serviceType The type of the service * @param serviceType The type of the service
* @return Handle to KService object of the service * @return Handle to KService object of the service
*/ */
handle_t NewSession(const Service serviceType); KHandle NewSession(const Service serviceType);
/** /**
* @brief Creates a new service using it's type enum and writes it's handle or virtual handle (If it's a domain request) to IpcResponse * @brief Creates a new service using it's type enum and writes it's handle or virtual handle (If it's a domain request) to IpcResponse
@ -66,12 +66,12 @@ namespace skyline::service {
* @brief Closes an existing session to a service * @brief Closes an existing session to a service
* @param service The handle of the KService object * @param service The handle of the KService object
*/ */
void CloseSession(const handle_t handle); void CloseSession(const KHandle handle);
/** /**
* @brief Handles a Synchronous IPC Request * @brief Handles a Synchronous IPC Request
* @param handle The handle of the object * @param handle The handle of the object
*/ */
void SyncRequestHandler(const handle_t handle); void SyncRequestHandler(const KHandle handle);
}; };
} }

View File

@ -17,11 +17,11 @@ namespace skyline::service::timesrv {
}; };
response.Push(calendarTime); response.Push(calendarTime);
CalendarAdditionalInfo calendarInfo{ CalendarAdditionalInfo calendarInfo{
.day_week = static_cast<u32>(calender.tm_wday), .dayWeek = static_cast<u32>(calender.tm_wday),
.day_month = static_cast<u32>(calender.tm_mday), .dayMonth = static_cast<u32>(calender.tm_mday),
.name = *reinterpret_cast<const u64 *>(calender.tm_zone), .name = *reinterpret_cast<const u64 *>(calender.tm_zone),
.dst = static_cast<i32>(calender.tm_isdst), .dst = static_cast<i32>(calender.tm_isdst),
.utc_rel = static_cast<u32>(calender.tm_gmtoff) .utcRel = static_cast<u32>(calender.tm_gmtoff)
}; };
response.Push(calendarInfo); response.Push(calendarInfo);
} }

View File

@ -27,11 +27,11 @@ namespace skyline::service::timesrv {
* @brief This is passed in addition to CalendarTime * @brief This is passed in addition to CalendarTime
*/ */
struct CalendarAdditionalInfo { struct CalendarAdditionalInfo {
u32 day_week; u32 dayWeek;
u32 day_month; u32 dayMonth;
u64 name; u64 name;
i32 dst; i32 dst;
u32 utc_rel; u32 utcRel;
}; };
static_assert(sizeof(CalendarAdditionalInfo) == 0x18); static_assert(sizeof(CalendarAdditionalInfo) == 0x18);

View File

@ -88,7 +88,7 @@ namespace skyline::service::visrv {
} }
void IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
handle_t handle = state.process->InsertItem(state.gpu->vsyncEvent); KHandle handle = state.process->InsertItem(state.gpu->vsyncEvent);
state.logger->Debug("VSync Event Handle: 0x{:X}", handle); state.logger->Debug("VSync Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);
} }