mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-28 07:45:37 +03:00
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:
parent
618353c1fa
commit
773ee25e5a
4
.idea/inspectionProfiles/Project_Default.xml
generated
4
.idea/inspectionProfiles/Project_Default.xml
generated
@ -1621,7 +1621,7 @@
|
||||
<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="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="NotifyCalledOnCondition" 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="OCDFA" 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="OCLoopDoesntUseConditionVariable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="OCNotLocalizedString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
|
@ -29,7 +29,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env,
|
||||
std::signal(SIGABRT, 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 settings = std::make_shared<skyline::Settings>(preferenceFd);
|
||||
|
@ -3,41 +3,43 @@
|
||||
#include <oboe/Oboe.h>
|
||||
#include <common.h>
|
||||
|
||||
namespace skyline::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
|
||||
};
|
||||
|
||||
namespace skyline {
|
||||
namespace constant {
|
||||
constexpr int 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 oboe::AudioFormat PcmFormat = oboe::AudioFormat::I16; //!< The pcm data format to use for the oboe audio output
|
||||
constexpr auto SampleRate = 48000; //!< The sampling rate to use for the oboe audio output
|
||||
constexpr auto ChannelCount = 2; //!< The amount of channels 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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ namespace skyline::audio {
|
||||
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},
|
||||
{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},
|
||||
@ -44,7 +44,7 @@ namespace skyline::audio {
|
||||
{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}}};
|
||||
|
||||
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},
|
||||
{-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},
|
||||
@ -78,7 +78,7 @@ namespace skyline::audio {
|
||||
{-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}}};
|
||||
|
||||
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},
|
||||
{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},
|
||||
@ -118,14 +118,13 @@ namespace skyline::audio {
|
||||
std::vector<i16> outputBuffer(static_cast<size_t>(outputSize));
|
||||
|
||||
const std::array<skyline::audio::LutEntry, 128> &lut = [step] {
|
||||
if (step > 0xaaaa) {
|
||||
return curveLut0;
|
||||
} else if (step <= 0x8000) {
|
||||
return curveLut1;
|
||||
} else {
|
||||
return curveLut2;
|
||||
}
|
||||
} ();
|
||||
if (step > 0xaaaa)
|
||||
return CurveLut0;
|
||||
else if (step <= 0x8000)
|
||||
return CurveLut1;
|
||||
else
|
||||
return CurveLut2;
|
||||
}();
|
||||
|
||||
for (uint outIndex = 0, inIndex = 0; outIndex < outputBuffer.size(); outIndex += channelCount) {
|
||||
uint lutIndex = (fraction >> 8) << 2;
|
||||
|
@ -11,6 +11,7 @@ namespace skyline {
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
if (!flag.test_and_set(std::memory_order_acquire))
|
||||
return;
|
||||
|
||||
asm volatile("yield");
|
||||
}
|
||||
sched_yield();
|
||||
@ -21,14 +22,17 @@ namespace skyline {
|
||||
auto none = Group::None;
|
||||
constexpr u64 timeout = 100; // The timeout in ns
|
||||
auto end = utils::GetTimeNs() + timeout;
|
||||
|
||||
while (true) {
|
||||
if (next == group) {
|
||||
if (flag == group) {
|
||||
std::lock_guard lock(mtx);
|
||||
|
||||
if (flag == group) {
|
||||
auto groupT = group;
|
||||
next.compare_exchange_strong(groupT, Group::None);
|
||||
num++;
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -36,6 +40,7 @@ namespace skyline {
|
||||
}
|
||||
} else if (flag == group && (next == Group::None || utils::GetTimeNs() >= end)) {
|
||||
std::lock_guard lock(mtx);
|
||||
|
||||
if (flag == group) {
|
||||
num++;
|
||||
return;
|
||||
@ -43,6 +48,7 @@ namespace skyline {
|
||||
} else {
|
||||
next.compare_exchange_weak(none, group);
|
||||
}
|
||||
|
||||
none = Group::None;
|
||||
asm volatile("yield");
|
||||
}
|
||||
@ -50,38 +56,44 @@ namespace skyline {
|
||||
|
||||
void GroupMutex::unlock() {
|
||||
std::lock_guard lock(mtx);
|
||||
|
||||
if (!--num)
|
||||
flag.exchange(next);
|
||||
}
|
||||
|
||||
Settings::Settings(const int preferenceFd) {
|
||||
tinyxml2::XMLDocument pref;
|
||||
|
||||
if (pref.LoadFile(fdopen(preferenceFd, "r")))
|
||||
throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
|
||||
|
||||
tinyxml2::XMLElement *elem = pref.LastChild()->FirstChild()->ToElement();
|
||||
|
||||
while (elem) {
|
||||
switch (elem->Value()[0]) {
|
||||
case 's':
|
||||
stringMap[elem->FindAttribute("name")->Value()] = elem->GetText();
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
boolMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute(
|
||||
"value")->BoolValue();
|
||||
boolMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->BoolValue();
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
intMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute(
|
||||
"value")->IntValue();
|
||||
intMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->IntValue();
|
||||
break;
|
||||
|
||||
default:
|
||||
syslog(LOG_ALERT, "Settings type is missing: %s for %s", elem->Value(),
|
||||
elem->FindAttribute("name")->Value());
|
||||
syslog(LOG_ALERT, "Settings type is missing: %s for %s", elem->Value(), elem->FindAttribute("name")->Value());
|
||||
break;
|
||||
};
|
||||
|
||||
if (elem->NextSibling())
|
||||
elem = elem->NextSibling()->ToElement();
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
pref.Clear();
|
||||
}
|
||||
|
||||
@ -100,6 +112,7 @@ namespace skyline {
|
||||
void Settings::List(const std::shared_ptr<Logger> &logger) {
|
||||
for (auto &iter : stringMap)
|
||||
logger->Info("Key: {}, Value: {}, Type: String", iter.first, GetString(iter.first));
|
||||
|
||||
for (auto &iter : boolMap)
|
||||
logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first));
|
||||
}
|
||||
@ -115,15 +128,18 @@ namespace skyline {
|
||||
|
||||
void Logger::WriteHeader(const std::string &str) {
|
||||
syslog(LOG_ALERT, "%s", str.c_str());
|
||||
|
||||
logFile << "0|" << str << "\n";
|
||||
logFile.flush();
|
||||
}
|
||||
|
||||
void Logger::Write(const LogLevel level, std::string str) {
|
||||
syslog(levelSyslog[static_cast<u8>(level)], "%s", str.c_str());
|
||||
|
||||
for (auto &character : str)
|
||||
if (character == '\n')
|
||||
character = '\\';
|
||||
|
||||
logFile << "1|" << levelStr[static_cast<u8>(level)] << "|" << str << "\n";
|
||||
logFile.flush();
|
||||
}
|
||||
|
@ -19,47 +19,20 @@
|
||||
#include "nce/guest_common.h"
|
||||
|
||||
namespace skyline {
|
||||
using handle_t = u32; //!< The type of a kernel handle
|
||||
using KHandle = u32; //!< The type of a kernel handle
|
||||
|
||||
namespace constant {
|
||||
// Memory
|
||||
constexpr u64 BaseAddress = 0x8000000; //!< The address space base
|
||||
constexpr u64 TotalPhyMem = 0xF8000000; // ~4 GB of RAM
|
||||
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
|
||||
constexpr auto BaseAddress = 0x8000000; //!< The address space base
|
||||
constexpr auto DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB
|
||||
// Kernel
|
||||
constexpr u64 MaxSyncHandles = 0x40; //!< The total amount of handles that can be passed to WaitSynchronization
|
||||
constexpr handle_t BaseHandleIndex = 0xD000; // The index of the base handle
|
||||
constexpr handle_t ThreadSelf = 0xFFFF8000; //!< This is the handle used by threads to refer to themselves
|
||||
constexpr u8 DefaultPriority = 44; //!< The default priority of a process
|
||||
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 std::pair<u8, u8> PriorityNin = {0, 63}; //!< The range of priority for the Nintendo Switch
|
||||
constexpr u32 MtxOwnerMask = 0xBFFFFFFF; //!< The mask of values which contain the owner of a mutex
|
||||
// IPC
|
||||
constexpr size_t TlsIpcSize = 0x100; //!< The size of the IPC command buffer in a TLS slot
|
||||
constexpr u8 PortSize = 0x8; //!< The size of a port name string
|
||||
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
|
||||
constexpr std::pair<int8_t, int8_t> AndroidPriority = {19, -8}; //!< The range of priority for Android
|
||||
constexpr std::pair<u8, u8> SwitchPriority = {0, 63}; //!< The range of priority for the Nintendo Switch
|
||||
// Display
|
||||
constexpr auto HandheldResolutionW = 1280; //!< The width component of the handheld resolution
|
||||
constexpr auto HandheldResolutionH = 720; //!< The height component of the handheld resolution
|
||||
constexpr auto DockedResolutionW = 1920; //!< The width component of the docked resolution
|
||||
constexpr auto DockedResolutionH = 1080; //!< The height component of the docked resolution
|
||||
// Status codes
|
||||
namespace status {
|
||||
constexpr u32 Success = 0x0; //!< "Success"
|
||||
@ -98,13 +71,13 @@ namespace skyline {
|
||||
* @return The current time in nanoseconds
|
||||
*/
|
||||
inline u64 GetTimeNs() {
|
||||
constexpr uint64_t NsInSecond = 1000000000;
|
||||
constexpr uint64_t nsInSecond = 1000000000;
|
||||
static u64 frequency{};
|
||||
if (!frequency)
|
||||
asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency));
|
||||
u64 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,12 +23,12 @@ namespace skyline::kernel::ipc {
|
||||
handleDesc = reinterpret_cast<HandleDescriptor *>(pointer);
|
||||
pointer += sizeof(HandleDescriptor) + (handleDesc->sendPid ? sizeof(u64) : 0);
|
||||
for (uint index = 0; handleDesc->copyCount > index; index++) {
|
||||
copyHandles.push_back(*reinterpret_cast<handle_t *>(pointer));
|
||||
pointer += sizeof(handle_t);
|
||||
copyHandles.push_back(*reinterpret_cast<KHandle *>(pointer));
|
||||
pointer += sizeof(KHandle);
|
||||
}
|
||||
for (uint index = 0; handleDesc->moveCount > index; index++) {
|
||||
moveHandles.push_back(*reinterpret_cast<handle_t *>(pointer));
|
||||
pointer += sizeof(handle_t);
|
||||
moveHandles.push_back(*reinterpret_cast<KHandle *>(pointer));
|
||||
pointer += sizeof(KHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +69,9 @@ namespace skyline::kernel::ipc {
|
||||
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;
|
||||
|
||||
if (isDomain && (header->type == CommandType::Request)) {
|
||||
@ -84,24 +86,26 @@ namespace skyline::kernel::ipc {
|
||||
pointer += domain->payloadSz;
|
||||
|
||||
for (uint index = 0; domain->inputCount > index; index++) {
|
||||
domainObjects.push_back(*reinterpret_cast<handle_t *>(pointer));
|
||||
pointer += sizeof(handle_t);
|
||||
domainObjects.push_back(*reinterpret_cast<KHandle *>(pointer));
|
||||
pointer += sizeof(KHandle);
|
||||
}
|
||||
} else {
|
||||
payload = reinterpret_cast<PayloadHeader *>(pointer);
|
||||
pointer += sizeof(PayloadHeader);
|
||||
|
||||
cmdArg = pointer;
|
||||
cmdArgSz = (header->rawSize * sizeof(u32)) - (constant::IpcPaddingSum + sizeof(PayloadHeader));
|
||||
cmdArgSz = (header->rawSize * sizeof(u32)) - (ipcPaddingSum + sizeof(PayloadHeader));
|
||||
pointer += cmdArgSz;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
pointer += constant::IpcPaddingSum - padding;
|
||||
pointer += ipcPaddingSum - padding;
|
||||
|
||||
if (header->cFlag == BufferCFlag::SingleDescriptor) {
|
||||
auto bufC = reinterpret_cast<BufferDescriptorC *>(pointer);
|
||||
@ -135,10 +139,14 @@ namespace skyline::kernel::ipc {
|
||||
void IpcResponse::WriteResponse() {
|
||||
auto tls = state.process->GetPointer<u8>(state.thread->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);
|
||||
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());
|
||||
pointer += sizeof(CommandHeader);
|
||||
|
||||
@ -149,17 +157,17 @@ namespace skyline::kernel::ipc {
|
||||
pointer += sizeof(HandleDescriptor);
|
||||
|
||||
for (unsigned int copyHandle : copyHandles) {
|
||||
*reinterpret_cast<handle_t *>(pointer) = copyHandle;
|
||||
pointer += sizeof(handle_t);
|
||||
*reinterpret_cast<KHandle *>(pointer) = copyHandle;
|
||||
pointer += sizeof(KHandle);
|
||||
}
|
||||
|
||||
for (unsigned int moveHandle : moveHandles) {
|
||||
*reinterpret_cast<handle_t *>(pointer) = moveHandle;
|
||||
pointer += sizeof(handle_t);
|
||||
*reinterpret_cast<KHandle *>(pointer) = moveHandle;
|
||||
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;
|
||||
|
||||
if (isDomain) {
|
||||
@ -168,19 +176,22 @@ namespace skyline::kernel::ipc {
|
||||
pointer += sizeof(DomainHeaderResponse);
|
||||
}
|
||||
|
||||
constexpr auto sfcoMagic = 0x4F434653; // SFCO in reverse, IPC Response Magic
|
||||
|
||||
auto payload = reinterpret_cast<PayloadHeader *>(pointer);
|
||||
payload->magic = constant::SfcoMagic;
|
||||
payload->magic = sfcoMagic;
|
||||
payload->version = 1;
|
||||
payload->value = errorCode;
|
||||
pointer += sizeof(PayloadHeader);
|
||||
|
||||
if (!argVec.empty())
|
||||
memcpy(pointer, argVec.data(), argVec.size());
|
||||
pointer += argVec.size();
|
||||
|
||||
if (isDomain) {
|
||||
for (auto &domainObject : domainObjects) {
|
||||
*reinterpret_cast<handle_t *>(pointer) = domainObject;
|
||||
pointer += sizeof(handle_t);
|
||||
*reinterpret_cast<KHandle *>(pointer) = domainObject;
|
||||
pointer += sizeof(KHandle);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,7 +242,6 @@ namespace skyline::kernel::ipc {
|
||||
u8 *payloadOffset; //!< This is the offset of the data read from the payload
|
||||
|
||||
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
|
||||
HandleDescriptor *handleDesc{}; //!< The handle descriptor in case CommandHeader::handle_desc is true in the header
|
||||
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
|
||||
u8 *cmdArg{}; //!< This is a pointer to the data payload (End of PayloadHeader)
|
||||
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<handle_t> 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> 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> moveHandles; //!< A vector of handles that should be moved from the server to the client process rather than copied
|
||||
std::vector<KHandle> domainObjects; //!< A vector of all input domain objects
|
||||
std::vector<InputBuffer> inputBuf; //!< This is a vector of input 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 isDomain{}; //!< If this is a domain request
|
||||
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<handle_t> moveHandles; //!< A vector of handles to move
|
||||
std::vector<handle_t> domainObjects; //!< A vector of domain objects to write
|
||||
std::vector<KHandle> copyHandles; //!< A vector of handles to copy
|
||||
std::vector<KHandle> moveHandles; //!< A vector of handles to move
|
||||
std::vector<KHandle> domainObjects; //!< A vector of domain objects to write
|
||||
|
||||
/**
|
||||
* @param isDomain If the following request is a domain request
|
||||
|
@ -117,24 +117,24 @@ namespace skyline {
|
||||
|
||||
struct {
|
||||
MemoryType type; //!< The MemoryType of this memory 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 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 NonSecureIpcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=1
|
||||
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 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 nonSecureIpcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=1
|
||||
bool _pad0_ : 1;
|
||||
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 UnmapProcessCodeMemoryAllowed : 1; //!< If the application can use svcUnmapProcessCodeMemory 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 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 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 MapProcessAllowed : 1; //!< If the application can use svcMapProcessMemory 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 processPermissionChangeAllowed : 1; //!< If the application can use svcSetProcessMemoryPermission 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 transferMemoryAllowed : 1; //!< If the application can use svcCreateTransferMemory 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 mapDeviceAlignedAllowed : 1; //!< If the application can use svcMapDeviceAddressSpaceAligned on this block
|
||||
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 mapProcessAllowed : 1; //!< If the application can use svcMapProcessMemory 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
|
||||
};
|
||||
u32 value;
|
||||
};
|
||||
@ -143,7 +143,7 @@ namespace skyline {
|
||||
/**
|
||||
* @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 Io = 0x00002001;
|
||||
constexpr MemoryState CodeStatic = 0x00DC7E03;
|
||||
|
@ -3,17 +3,23 @@
|
||||
|
||||
namespace skyline::kernel::svc {
|
||||
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;
|
||||
if (size % constant::HeapSizeDiv != 0) {
|
||||
|
||||
if (size % heapSizeAlign != 0) {
|
||||
state.ctx->registers.w0 = constant::status::InvSize;
|
||||
state.ctx->registers.x1 = 0;
|
||||
|
||||
state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size);
|
||||
return;
|
||||
}
|
||||
|
||||
auto &heap = state.process->heap;
|
||||
heap->Resize(size);
|
||||
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
state.ctx->registers.x1 = heap->address;
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
if (!chunk->state.AttributeChangeAllowed) {
|
||||
if (!chunk->state.attributeChangeAllowed) {
|
||||
state.ctx->registers.w0 = constant::status::InvState;
|
||||
state.logger->Warn("svcSetMemoryAttribute: Attribute change not allowed for chunk: 0x{:X}", address);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
if (!descriptor->chunk.state.MapAllowed) {
|
||||
if (!descriptor->chunk.state.mapAllowed) {
|
||||
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);
|
||||
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);
|
||||
auto object = state.process->GetMemoryObject(source);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
if (!destDesc->chunk.state.MapAllowed) {
|
||||
if (!destDesc->chunk.state.mapAllowed) {
|
||||
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);
|
||||
return;
|
||||
@ -181,7 +187,7 @@ namespace skyline::kernel::svc {
|
||||
u64 entryArgument = state.ctx->registers.x2;
|
||||
u64 stackTop = state.ctx->registers.x3;
|
||||
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.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
|
||||
return;
|
||||
@ -312,7 +318,7 @@ namespace skyline::kernel::svc {
|
||||
}
|
||||
|
||||
void CloseHandle(DeviceState &state) {
|
||||
auto handle = static_cast<handle_t>(state.ctx->registers.w0);
|
||||
auto handle = static_cast<KHandle>(state.ctx->registers.w0);
|
||||
try {
|
||||
state.process->handles.erase(handle);
|
||||
state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle);
|
||||
@ -350,40 +356,52 @@ namespace skyline::kernel::svc {
|
||||
}
|
||||
|
||||
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;
|
||||
if (numHandles > constant::MaxSyncHandles) {
|
||||
if (numHandles > maxSyncHandles) {
|
||||
state.ctx->registers.w0 = constant::status::MaxHandles;
|
||||
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::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) {
|
||||
handleStr += fmt::format("* 0x{:X}\n", handle);
|
||||
|
||||
auto object = state.process->handles.at(handle);
|
||||
|
||||
switch (object->objectType) {
|
||||
case type::KType::KProcess:
|
||||
case type::KType::KThread:
|
||||
case type::KType::KEvent:
|
||||
case type::KType::KSession:
|
||||
break;
|
||||
|
||||
default: {
|
||||
state.ctx->registers.w0 = constant::status::InvHandle;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
objectTable.push_back(std::static_pointer_cast<type::KSyncObject>(object));
|
||||
}
|
||||
|
||||
auto timeout = state.ctx->registers.x3;
|
||||
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
|
||||
|
||||
auto start = utils::GetTimeNs();
|
||||
|
||||
while (true) {
|
||||
if (state.thread->cancelSync) {
|
||||
state.thread->cancelSync = false;
|
||||
state.ctx->registers.w0 = constant::status::Interrupted;
|
||||
break;
|
||||
}
|
||||
|
||||
uint index{};
|
||||
for (const auto &object : objectTable) {
|
||||
if (object->signalled) {
|
||||
@ -394,6 +412,7 @@ namespace skyline::kernel::svc {
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if ((utils::GetTimeNs() - start) >= timeout) {
|
||||
state.logger->Debug("svcWaitSynchronization: Wait has timed out");
|
||||
state.ctx->registers.w0 = constant::status::Timeout;
|
||||
@ -496,34 +515,41 @@ namespace skyline::kernel::svc {
|
||||
}
|
||||
|
||||
void ConnectToNamedPort(DeviceState &state) {
|
||||
char port[constant::PortSize + 1]{0};
|
||||
state.process->ReadMemory(port, state.ctx->registers.x1, constant::PortSize);
|
||||
handle_t handle{};
|
||||
if (std::strcmp(port, "sm:") == 0) {
|
||||
constexpr auto portSize = 0x8; //!< The size of a port name string
|
||||
std::string_view port(state.process->GetPointer<char>(state.ctx->registers.x1), portSize);
|
||||
|
||||
KHandle handle{};
|
||||
if (port.compare("sm:") >= 0) {
|
||||
handle = state.os->serviceManager.NewSession(service::Service::sm_IUserInterface);
|
||||
} else {
|
||||
state.logger->Warn("svcConnectToNamedPort: Connecting to invalid port: '{}'", port);
|
||||
state.ctx->registers.w0 = constant::status::NotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
state.logger->Debug("svcConnectToNamedPort: Connecting to port '{}' at 0x{:X}", port, handle);
|
||||
|
||||
state.ctx->registers.w1 = handle;
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
if (handle != constant::ThreadSelf)
|
||||
pid_t pid{};
|
||||
|
||||
if (handle != threadSelf)
|
||||
pid = state.process->GetHandle<type::KThread>(handle)->pid;
|
||||
else
|
||||
pid = state.thread->pid;
|
||||
|
||||
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid);
|
||||
|
||||
state.ctx->registers.x1 = static_cast<u64>(pid);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
@ -540,7 +566,11 @@ namespace skyline::kernel::svc {
|
||||
auto id0 = state.ctx->registers.w1;
|
||||
auto handle = state.ctx->registers.w2;
|
||||
auto id1 = state.ctx->registers.x3;
|
||||
|
||||
constexpr auto totalPhysicalMemory = 0xF8000000; // ~4 GB of RAM
|
||||
|
||||
u64 out{};
|
||||
|
||||
switch (id0) {
|
||||
case constant::infoState::AllowedCpuIdBitmask:
|
||||
case constant::infoState::AllowedThreadPriorityMask:
|
||||
@ -548,57 +578,75 @@ namespace skyline::kernel::svc {
|
||||
case constant::infoState::TitleId:
|
||||
case constant::infoState::PrivilegedProcessId:
|
||||
break;
|
||||
|
||||
case constant::infoState::AliasRegionBaseAddr:
|
||||
out = state.os->memory.GetRegion(memory::Regions::Alias).address;
|
||||
break;
|
||||
|
||||
case constant::infoState::AliasRegionSize:
|
||||
out = state.os->memory.GetRegion(memory::Regions::Alias).size;
|
||||
break;
|
||||
|
||||
case constant::infoState::HeapRegionBaseAddr:
|
||||
out = state.os->memory.GetRegion(memory::Regions::Heap).address;
|
||||
break;
|
||||
|
||||
case constant::infoState::HeapRegionSize:
|
||||
out = state.os->memory.GetRegion(memory::Regions::Heap).size;
|
||||
break;
|
||||
|
||||
case constant::infoState::TotalMemoryAvailable:
|
||||
out = constant::TotalPhyMem;
|
||||
out = totalPhysicalMemory;
|
||||
break;
|
||||
|
||||
case constant::infoState::TotalMemoryUsage:
|
||||
out = state.process->heap->address + constant::DefStackSize + state.os->memory.GetProgramSize();
|
||||
break;
|
||||
|
||||
case constant::infoState::AddressSpaceBaseAddr:
|
||||
out = state.os->memory.GetRegion(memory::Regions::Base).address;
|
||||
break;
|
||||
|
||||
case constant::infoState::AddressSpaceSize:
|
||||
out = state.os->memory.GetRegion(memory::Regions::Base).size;
|
||||
break;
|
||||
|
||||
case constant::infoState::StackRegionBaseAddr:
|
||||
out = state.os->memory.GetRegion(memory::Regions::Stack).address;
|
||||
break;
|
||||
|
||||
case constant::infoState::StackRegionSize:
|
||||
out = state.os->memory.GetRegion(memory::Regions::Stack).size;
|
||||
break;
|
||||
|
||||
case constant::infoState::PersonalMmHeapSize:
|
||||
out = constant::TotalPhyMem;
|
||||
out = totalPhysicalMemory;
|
||||
break;
|
||||
|
||||
case constant::infoState::PersonalMmHeapUsage:
|
||||
out = state.process->heap->address + constant::DefStackSize;
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
case constant::infoState::TotalMemoryUsedWithoutMmHeap:
|
||||
out = state.process->heap->size + constant::DefStackSize; // TODO: Same as above
|
||||
break;
|
||||
|
||||
case constant::infoState::UserExceptionContextAddr:
|
||||
out = state.process->tlsPages[0]->Get(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", id0, id1);
|
||||
state.ctx->registers.w0 = constant::status::Unimpl;
|
||||
return;
|
||||
}
|
||||
|
||||
state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", id0, id1, out);
|
||||
|
||||
state.ctx->registers.x1 = out;
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
@ -34,7 +34,8 @@ namespace skyline {
|
||||
// 6.0.0+
|
||||
constexpr u8 TotalMemoryAvailableWithoutMmHeap = 0x15;
|
||||
constexpr u8 TotalMemoryUsedWithoutMmHeap = 0x16;
|
||||
};
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -38,23 +38,29 @@ namespace skyline::kernel::type {
|
||||
} else {
|
||||
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));
|
||||
|
||||
auto &tlsPage = tlsPages.back();
|
||||
if (tlsPages.empty())
|
||||
tlsPage->ReserveSlot(); // User-mode exception handling
|
||||
|
||||
return tlsPage->ReserveSlot();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
state.nce->WaitThreadInit(thread);
|
||||
|
||||
memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC);
|
||||
if (memFd == -1)
|
||||
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) {
|
||||
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{};
|
||||
fregs.x0 = CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO;
|
||||
fregs.x1 = stackTop;
|
||||
@ -166,7 +172,7 @@ namespace skyline::kernel::type {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool KProcess::MutexLock(u64 address, handle_t owner) {
|
||||
bool KProcess::MutexLock(u64 address, KHandle owner) {
|
||||
std::unique_lock lock(mutexLock);
|
||||
auto mtx = GetPointer<u32>(address);
|
||||
auto &mtxWaiters = mutexes[address];
|
||||
|
@ -9,372 +9,381 @@
|
||||
#include <kernel/memory.h>
|
||||
#include <list>
|
||||
|
||||
namespace skyline::kernel::type {
|
||||
/**
|
||||
* @brief The KProcess class is responsible for holding the state of a process
|
||||
*/
|
||||
class KProcess : public KSyncObject {
|
||||
private:
|
||||
namespace skyline {
|
||||
namespace constant {
|
||||
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
|
||||
constexpr KHandle BaseHandleIndex = 0xD000; // The index of the base handle
|
||||
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
|
||||
* @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.
|
||||
* The first slot of the first page is reserved for user-mode exception handling.
|
||||
* 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
|
||||
* @brief The KProcess class is responsible for holding the state of a process
|
||||
*/
|
||||
class KProcess : public KSyncObject {
|
||||
private:
|
||||
/**
|
||||
* @brief This class holds a single TLS page's status
|
||||
* @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.
|
||||
* The first slot of the first page is reserved for user-mode exception handling.
|
||||
* 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
|
||||
*/
|
||||
TlsPage(u64 address);
|
||||
* @brief Returns a TLS slot from an arbitrary TLS page
|
||||
* @return The address of a free TLS slot
|
||||
*/
|
||||
u64 GetTlsSlot();
|
||||
|
||||
/**
|
||||
* @brief Reserves a single 0x200 byte TLS slot
|
||||
* @return The address of the reserved slot
|
||||
*/
|
||||
u64 ReserveSlot();
|
||||
* @brief This initializes heap and the initial TLS page
|
||||
*/
|
||||
void InitializeMemory();
|
||||
|
||||
public:
|
||||
friend OS;
|
||||
|
||||
/**
|
||||
* @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 This is used as the output for functions that return created kernel objects
|
||||
* @tparam objectClass The class of the kernel object
|
||||
*/
|
||||
template<typename objectClass>
|
||||
struct HandleOut {
|
||||
std::shared_ptr<objectClass> item; //!< 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
|
||||
* @return If the whole page is full or not
|
||||
*/
|
||||
bool Full();
|
||||
};
|
||||
* @brief This enum is used to describe the current status of the process
|
||||
*/
|
||||
enum class Status {
|
||||
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
|
||||
* @return The address of a free TLS slot
|
||||
*/
|
||||
u64 GetTlsSlot();
|
||||
/**
|
||||
* @brief This is used to hold information about a single waiting thread for mutexes and conditional variables
|
||||
*/
|
||||
struct WaitStatus {
|
||||
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
|
||||
|
||||
/**
|
||||
* @brief This initializes heap and the initial TLS page
|
||||
*/
|
||||
void InitializeMemory();
|
||||
WaitStatus(u8 priority, KHandle handle) : priority(priority), handle(handle) {}
|
||||
|
||||
public:
|
||||
friend OS;
|
||||
WaitStatus(u8 priority, KHandle handle, u64 mutexAddress) : priority(priority), handle(handle), mutexAddress(mutexAddress) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is used as the output for functions that return created kernel objects
|
||||
* @tparam objectClass The class of the kernel object
|
||||
*/
|
||||
template<typename objectClass>
|
||||
struct HandleOut {
|
||||
std::shared_ptr<objectClass> item; //!< A shared pointer to the object
|
||||
handle_t handle; //!< The handle of the object in the process
|
||||
};
|
||||
KHandle 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
|
||||
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
|
||||
std::unordered_map<pid_t, std::shared_ptr<KThread>> threads; //!< A mapping from a PID to it's corresponding KThread object
|
||||
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
|
||||
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
|
||||
*/
|
||||
enum class Status {
|
||||
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 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);
|
||||
|
||||
/**
|
||||
* @brief This is used to hold information about a single waiting thread for mutexes and conditional variables
|
||||
*/
|
||||
struct WaitStatus {
|
||||
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
|
||||
/**
|
||||
* Close the file descriptor to the process's memory
|
||||
*/
|
||||
~KProcess();
|
||||
|
||||
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
|
||||
int memFd; //!< The file descriptor to the memory of the process
|
||||
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
|
||||
std::unordered_map<pid_t, std::shared_ptr<KThread>> threads; //!< A mapping from a PID to it's corresponding KThread object
|
||||
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
|
||||
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 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;
|
||||
/**
|
||||
* @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 string from guest memory
|
||||
* @param address The address of the object
|
||||
* @param maxSize The maximum size of the string
|
||||
* @return A copy of a string in guest memory
|
||||
*/
|
||||
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 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);
|
||||
/**
|
||||
* @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("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);
|
||||
throw exception("Cannot retrieve reference to object not in shared guest memory");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 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 This deletes a certain handle from the handle table
|
||||
* @param handle The handle to delete
|
||||
*/
|
||||
inline void DeleteHandle(handle_t handle) {
|
||||
handles.erase(handle);
|
||||
}
|
||||
/**
|
||||
* @brief Returns a string from guest memory
|
||||
* @param address The address of the object
|
||||
* @param maxSize The maximum size of the string
|
||||
* @return A copy of a string in guest memory
|
||||
*/
|
||||
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
|
||||
* @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, handle_t owner);
|
||||
/**
|
||||
* @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 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);
|
||||
/**
|
||||
* @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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 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 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 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 This resets the object to an unsignalled state
|
||||
*/
|
||||
inline void ResetSignal() {
|
||||
signalled = false;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @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>
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ namespace skyline::kernel::type {
|
||||
class KSession : public KSyncObject {
|
||||
public:
|
||||
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
|
||||
handle_t handleIndex = constant::BaseVirtualHandleIndex;
|
||||
std::unordered_map<KHandle, std::shared_ptr<service::BaseService>> domainTable; //!< This maps from a virtual handle to it's service
|
||||
KHandle handleIndex{0x1}; //!< The currently allocated handle index
|
||||
const service::Service serviceType; //!< The type of the service
|
||||
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
|
||||
@ -27,7 +27,7 @@ namespace skyline::kernel::type {
|
||||
* 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
|
||||
*/
|
||||
handle_t ConvertDomain() {
|
||||
KHandle ConvertDomain() {
|
||||
isDomain = true;
|
||||
domainTable[handleIndex] = serviceObject;
|
||||
return handleIndex++;
|
||||
|
@ -50,7 +50,7 @@ namespace skyline::kernel::type {
|
||||
}
|
||||
|
||||
void KSharedMemory::Resize(size_t size) {
|
||||
if (guest.valid() && kernel.valid()) {
|
||||
if (guest.Valid() && kernel.Valid()) {
|
||||
if (close(fd) < 0)
|
||||
throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno));
|
||||
fd = ASharedMemory_create("KSharedMemory", size);
|
||||
@ -98,7 +98,7 @@ namespace skyline::kernel::type {
|
||||
throw exception("An occurred while mapping shared memory: {}", strerror(errno));
|
||||
guest.size = size;
|
||||
MemoryManager::ResizeChunk(chunk, size);
|
||||
} else if (kernel.valid()) {
|
||||
} else if (kernel.Valid()) {
|
||||
if (close(fd) < 0)
|
||||
throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno));
|
||||
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) {
|
||||
if (guest.valid() && !host) {
|
||||
if (guest.Valid() && !host) {
|
||||
Registers fregs{
|
||||
.x0 = address,
|
||||
.x1 = size,
|
||||
@ -137,7 +137,7 @@ namespace skyline::kernel::type {
|
||||
};
|
||||
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))
|
||||
throw exception("An error occurred while remapping shared memory: {}", strerror(errno));
|
||||
kernel.permission = permission;
|
||||
@ -146,7 +146,7 @@ namespace skyline::kernel::type {
|
||||
|
||||
KSharedMemory::~KSharedMemory() {
|
||||
try {
|
||||
if (guest.valid() && state.process) {
|
||||
if (guest.Valid() && state.process) {
|
||||
Registers fregs{
|
||||
.x0 = guest.address,
|
||||
.x1 = guest.size,
|
||||
@ -156,7 +156,7 @@ namespace skyline::kernel::type {
|
||||
}
|
||||
} catch (const std::exception &) {
|
||||
}
|
||||
if (kernel.valid())
|
||||
if (kernel.Valid())
|
||||
munmap(reinterpret_cast<void *>(kernel.address), kernel.size);
|
||||
state.os->memory.DeleteChunk(guest.address);
|
||||
close(fd);
|
||||
|
@ -24,7 +24,7 @@ namespace skyline::kernel::type {
|
||||
* @brief Returns if the 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;
|
||||
|
||||
/**
|
||||
@ -35,7 +35,7 @@ namespace skyline::kernel::type {
|
||||
* @param memState The MemoryState of the chunk of memory
|
||||
* @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
|
||||
|
@ -4,8 +4,7 @@
|
||||
#include <nce.h>
|
||||
|
||||
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,
|
||||
KType::KThread) {
|
||||
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) {
|
||||
UpdatePriority(priority);
|
||||
}
|
||||
|
||||
@ -31,8 +30,9 @@ namespace skyline::kernel::type {
|
||||
|
||||
void KThread::UpdatePriority(u8 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)
|
||||
if (setpriority(PRIO_PROCESS, static_cast<id_t>(pid), liPriority) == -1)
|
||||
throw exception("Couldn't set process priority to {} for PID: {}", liPriority, pid);
|
||||
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), linuxPriority) == -1)
|
||||
throw exception("Couldn't set process priority to {} for PID: {}", linuxPriority, pid);
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace skyline::kernel::type {
|
||||
} 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::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])
|
||||
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
|
||||
@ -30,7 +30,7 @@ namespace skyline::kernel::type {
|
||||
/**
|
||||
* @param state The state of the device
|
||||
* @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 entryArg An argument to pass to the process on entry
|
||||
* @param stackTop The top of the stack
|
||||
@ -39,7 +39,7 @@ namespace skyline::kernel::type {
|
||||
* @param parent The parent process of this thread
|
||||
* @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.
|
||||
|
@ -23,7 +23,7 @@ namespace skyline::kernel::type {
|
||||
* @param type The type of the 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
|
||||
|
@ -5,7 +5,10 @@
|
||||
namespace skyline::loader {
|
||||
NroLoader::NroLoader(const int romFd) : Loader(romFd) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -26,19 +29,19 @@ namespace skyline::loader {
|
||||
u64 patchSize = patch.size() * sizeof(u32);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
process->WriteMemory(text.data(), constant::BaseAddress, textSize);
|
||||
|
@ -189,7 +189,9 @@ namespace skyline {
|
||||
if (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";
|
||||
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) {
|
||||
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 *end = start + (code.size() / sizeof(u32));
|
||||
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);
|
||||
offset += guest::saveCtxSize;
|
||||
std::memcpy(patch.data(), reinterpret_cast<void *>(&guest::SaveCtx), guest::SaveCtxSize);
|
||||
offset += guest::SaveCtxSize;
|
||||
|
||||
std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::saveCtxSize, reinterpret_cast<void *>(&guest::LoadCtx), guest::loadCtxSize);
|
||||
offset += guest::loadCtxSize;
|
||||
std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::SaveCtxSize, reinterpret_cast<void *>(&guest::LoadCtx), guest::LoadCtxSize);
|
||||
offset += guest::LoadCtxSize;
|
||||
|
||||
std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::saveCtxSize + guest::loadCtxSize, reinterpret_cast<void *>(&guest::SvcHandler), guest::svcHandlerSize);
|
||||
offset += guest::svcHandlerSize;
|
||||
std::memcpy(reinterpret_cast<u8 *>(patch.data()) + guest::SaveCtxSize + guest::LoadCtxSize, reinterpret_cast<void *>(&guest::SvcHandler), guest::SvcHandlerSize);
|
||||
offset += guest::SvcHandlerSize;
|
||||
|
||||
static u64 frequency{};
|
||||
if (!frequency)
|
||||
@ -238,10 +246,10 @@ namespace skyline {
|
||||
offset += sizeof(u32) * movPc.size();
|
||||
instr::Movz movCmd(regs::W1, static_cast<u16>(instrSvc->value));
|
||||
offset += sizeof(movCmd);
|
||||
instr::BL bSvcHandler((patchOffset + guest::saveCtxSize + guest::loadCtxSize) - offset);
|
||||
instr::BL bSvcHandler((patchOffset + guest::SaveCtxSize + guest::LoadCtxSize) - offset);
|
||||
offset += sizeof(bSvcHandler);
|
||||
|
||||
instr::BL bLdCtx((patchOffset + guest::saveCtxSize) - offset);
|
||||
instr::BL bLdCtx((patchOffset + guest::SaveCtxSize) - offset);
|
||||
offset += sizeof(bLdCtx);
|
||||
constexpr u32 ldrLr = 0xF84107FE; // LDR LR, [SP], #16
|
||||
offset += sizeof(ldrLr);
|
||||
@ -259,7 +267,7 @@ namespace skyline {
|
||||
patch.push_back(ldrLr);
|
||||
patch.push_back(bret.raw);
|
||||
} else if (instrMrs->Verify()) {
|
||||
if (instrMrs->srcReg == constant::TpidrroEl0) {
|
||||
if (instrMrs->srcReg == TpidrroEl0) {
|
||||
instr::B bJunc(offset);
|
||||
u32 strX0{};
|
||||
if (instrMrs->destReg != regs::X0) {
|
||||
@ -291,10 +299,10 @@ namespace skyline {
|
||||
if (ldrX0)
|
||||
patch.push_back(ldrX0);
|
||||
patch.push_back(bret.raw);
|
||||
} else if (frequency != constant::TegraX1Freq) {
|
||||
if (instrMrs->srcReg == constant::CntpctEl0) {
|
||||
} else if (frequency != TegraX1Freq) {
|
||||
if (instrMrs->srcReg == CntpctEl0) {
|
||||
instr::B bJunc(offset);
|
||||
offset += guest::rescaleClockSize;
|
||||
offset += guest::RescaleClockSize;
|
||||
instr::Ldr ldr(0xF94003E0); // LDR XOUT, [SP]
|
||||
ldr.destReg = instrMrs->destReg;
|
||||
offset += sizeof(ldr);
|
||||
@ -305,14 +313,14 @@ namespace skyline {
|
||||
|
||||
*address = bJunc.raw;
|
||||
auto size = patch.size();
|
||||
patch.resize(size + (guest::rescaleClockSize / sizeof(u32)));
|
||||
std::memcpy(patch.data() + size, reinterpret_cast<void *>(&guest::RescaleClock), guest::rescaleClockSize);
|
||||
patch.resize(size + (guest::RescaleClockSize / sizeof(u32)));
|
||||
std::memcpy(patch.data() + size, reinterpret_cast<void *>(&guest::RescaleClock), guest::RescaleClockSize);
|
||||
patch.push_back(ldr.raw);
|
||||
patch.push_back(addSp);
|
||||
patch.push_back(bret.raw);
|
||||
} else if (instrMrs->srcReg == constant::CntfrqEl0) {
|
||||
} else if (instrMrs->srcReg == CntfrqEl0) {
|
||||
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();
|
||||
instr::B bret(-offset + sizeof(u32));
|
||||
offset += sizeof(bret);
|
||||
@ -323,8 +331,8 @@ namespace skyline {
|
||||
patch.push_back(bret.raw);
|
||||
}
|
||||
} else {
|
||||
if (instrMrs->srcReg == constant::CntpctEl0) {
|
||||
instr::Mrs mrs(constant::CntvctEl0, regs::X(instrMrs->destReg));
|
||||
if (instrMrs->srcReg == CntpctEl0) {
|
||||
instr::Mrs mrs(CntvctEl0, regs::X(instrMrs->destReg));
|
||||
*address = mrs.raw;
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
namespace skyline {
|
||||
namespace guest {
|
||||
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 rescaleClockSize = 16 * sizeof(u32); //!< The size of the RescaleClock 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 RescaleClockSize = 16 * sizeof(u32); //!< The size of the RescaleClock function in 32-bit ARMv8 instructions
|
||||
#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
|
||||
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
|
||||
|
||||
/**
|
||||
|
@ -19,11 +19,11 @@ namespace skyline::kernel {
|
||||
}
|
||||
|
||||
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;
|
||||
if (mprotect(reinterpret_cast<void *>(stack->guest.address), PAGE_SIZE, PROT_NONE))
|
||||
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;
|
||||
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)
|
||||
|
@ -11,7 +11,7 @@ namespace skyline::service::audio {
|
||||
{0x5, SFUNC(IAudioOut::GetReleasedAudioOutBuffer)},
|
||||
{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() {
|
||||
@ -46,7 +46,7 @@ namespace skyline::service::audio {
|
||||
|
||||
tmpSampleBuffer.resize(data.sampleSize / sizeof(i16));
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,8 @@ namespace skyline::service::audio {
|
||||
|
||||
state.logger->Debug("audoutU: Opening an IAudioOut with sample rate: {}, channel count: {}", sampleRate, channelCount);
|
||||
|
||||
sampleRate = sampleRate ? sampleRate : skyline::audio::constant::SampleRate;
|
||||
channelCount = channelCount ? channelCount : static_cast<u16>(skyline::audio::constant::ChannelCount);
|
||||
sampleRate = sampleRate ? sampleRate : constant::SampleRate;
|
||||
channelCount = channelCount ? channelCount : static_cast<u16>(constant::ChannelCount);
|
||||
manager.RegisterService(std::make_shared<IAudioOut>(state, manager, channelCount, sampleRate), session, response);
|
||||
|
||||
response.Push<u32>(sampleRate);
|
||||
|
@ -3,27 +3,28 @@
|
||||
#include <kernel/types/KEvent.h>
|
||||
#include <services/base_service.h>
|
||||
#include <services/serviceman.h>
|
||||
|
||||
namespace skyline::service::audio {
|
||||
namespace skyline {
|
||||
namespace constant {
|
||||
constexpr std::string_view DefaultAudioOutName = "DeviceOut"; //!< The default audio output device name
|
||||
};
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
namespace service::audio {
|
||||
/**
|
||||
* @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)
|
||||
*/
|
||||
void OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
};
|
||||
/**
|
||||
* @brief Returns a list of all available audio outputs (https://switchbrew.org/wiki/Audio_services#ListAudioOuts)
|
||||
*/
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace skyline::service::audio::IAudioRenderer {
|
||||
{0x6, SFUNC(IAudioRenderer::Stop)},
|
||||
{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();
|
||||
|
||||
memoryPools.resize(memoryPoolCount);
|
||||
@ -132,7 +132,7 @@ namespace skyline::service::audio::IAudioRenderer {
|
||||
|
||||
void IAudioRenderer::MixFinalBuffer() {
|
||||
int setIndex = 0;
|
||||
sampleBuffer.resize(samplesPerBuffer * skyline::audio::constant::ChannelCount);
|
||||
sampleBuffer.resize(static_cast<size_t>(samplesPerBuffer * constant::ChannelCount));
|
||||
|
||||
for (auto &voice : voices) {
|
||||
if (!voice.Playable())
|
||||
@ -149,7 +149,7 @@ namespace skyline::service::audio::IAudioRenderer {
|
||||
if (voiceBufferSize == 0)
|
||||
break;
|
||||
|
||||
pendingSamples -= voiceBufferSize / skyline::audio::constant::ChannelCount;
|
||||
pendingSamples -= voiceBufferSize / constant::ChannelCount;
|
||||
|
||||
for (int i = voiceBufferOffset; i < voiceBufferOffset + voiceBufferSize; i++) {
|
||||
if (setIndex == bufferOffset) {
|
||||
|
@ -9,127 +9,130 @@
|
||||
#include "voice.h"
|
||||
#include "revisionInfo.h"
|
||||
|
||||
namespace skyline::service::audio::IAudioRenderer {
|
||||
namespace skyline {
|
||||
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();
|
||||
|
||||
public:
|
||||
/**
|
||||
* @param params The parameters to use for rendering
|
||||
*/
|
||||
IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParams ¶ms);
|
||||
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 Closes the audio track
|
||||
*/
|
||||
~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)
|
||||
* @brief IAudioRenderer is used to control an audio renderer output (https://switchbrew.org/wiki/Audio_services#IAudioRenderer)
|
||||
*/
|
||||
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
|
||||
|
||||
/**
|
||||
* @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);
|
||||
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
|
||||
|
||||
/**
|
||||
* @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 Obtains new sample data from voices and mixes it together into the sample buffer
|
||||
*/
|
||||
void MixFinalBuffer();
|
||||
|
||||
/**
|
||||
* @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 Appends all released buffers with new mixed sample data
|
||||
*/
|
||||
void UpdateAudio();
|
||||
|
||||
/**
|
||||
* @brief Start the audio stream from the renderer
|
||||
*/
|
||||
void Start(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
public:
|
||||
/**
|
||||
* @param params The parameters to use for rendering
|
||||
*/
|
||||
IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParams ¶ms);
|
||||
|
||||
/**
|
||||
* @brief Stop the audio stream from the renderer
|
||||
*/
|
||||
void Stop(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
/**
|
||||
* @brief Closes the audio track
|
||||
*/
|
||||
~IAudioRenderer();
|
||||
|
||||
/**
|
||||
* @brief Returns a handle to the sample release KEvent
|
||||
*/
|
||||
void QuerySystemEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
};
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
|
||||
namespace skyline::service::audio::IAudioRenderer {
|
||||
namespace skyline {
|
||||
namespace constant {
|
||||
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
|
||||
@ -17,67 +16,69 @@ namespace skyline::service::audio::IAudioRenderer {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extracts the audren version from a revision magic
|
||||
* @param revision The revision magic
|
||||
* @return The revision number from the magic
|
||||
*/
|
||||
inline u32 ExtractVersionFromRevision(u32 revision) {
|
||||
return (revision - constant::Rev0Magic) >> 24;
|
||||
namespace service::audio::IAudioRenderer {
|
||||
/**
|
||||
* @brief Extracts the audren version from a revision magic
|
||||
* @param revision The revision magic
|
||||
* @return The revision number from the magic
|
||||
*/
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -55,15 +55,15 @@ namespace skyline::service::audio::IAudioRenderer {
|
||||
throw exception("Unsupported voice PCM format: {}", pcmFormat);
|
||||
}
|
||||
|
||||
if (sampleRate != skyline::audio::constant::SampleRate)
|
||||
sampleBuffer = resampler.ResampleBuffer(sampleBuffer, static_cast<double>(sampleRate) / skyline::audio::constant::SampleRate, channelCount);
|
||||
if (sampleRate != constant::SampleRate)
|
||||
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();
|
||||
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 (uint i = 0; i < skyline::audio::constant::ChannelCount; i++)
|
||||
for (uint i = 0; i < constant::ChannelCount; i++)
|
||||
sampleBuffer[--targetIndex] = sampleBuffer[monoIndex];
|
||||
}
|
||||
}
|
||||
@ -82,9 +82,9 @@ namespace skyline::service::audio::IAudioRenderer {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (sampleOffset == sampleBuffer.size()) {
|
||||
|
@ -91,7 +91,7 @@ namespace skyline::service::audio::IAudioRenderer {
|
||||
|
||||
bool acquired{false}; //!< If the voice is in use
|
||||
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 sampleRate{}; //!< The sample rate of the sample data
|
||||
int channelCount{}; //!< The amount of channels in the sample data
|
||||
|
@ -25,25 +25,25 @@ namespace skyline::service::audio {
|
||||
|
||||
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 +
|
||||
totalMixCount * 0x940 +
|
||||
params.voiceCount * 0x3F0 +
|
||||
utils::AlignUp(totalMixCount * 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.effectCount + params.voiceCount * 4) * 0x30 +
|
||||
0x50;
|
||||
|
||||
if (revisionInfo.SplitterSupported()) {
|
||||
i32 nodeStateWorkSize = utils::AlignUp(totalMixCount, IAudioRenderer::constant::BufferAlignment);
|
||||
i32 nodeStateWorkSize = utils::AlignUp(totalMixCount, constant::BufferAlignment);
|
||||
if (nodeStateWorkSize < 0)
|
||||
nodeStateWorkSize |= 7;
|
||||
|
||||
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)
|
||||
edgeMatrixWorkSize |= 7;
|
||||
|
||||
|
@ -3,14 +3,19 @@
|
||||
#include <kernel/types/KProcess.h>
|
||||
|
||||
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));
|
||||
|
||||
if (size < (sizeof(ParcelHeader) + header.dataSize + header.objectsSize))
|
||||
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);
|
||||
state.process->ReadMemory(objects.data(), address + header.objectsOffset, header.objectsSize);
|
||||
}
|
||||
@ -24,14 +29,19 @@ namespace skyline::service {
|
||||
u64 Parcel::WriteParcel(u64 address, u64 maxSize) {
|
||||
header.dataSize = static_cast<u32>(data.size());
|
||||
header.dataOffset = sizeof(ParcelHeader);
|
||||
|
||||
header.objectsSize = static_cast<u32>(objects.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)
|
||||
throw exception("The size of the parcel exceeds maxSize");
|
||||
|
||||
state.process->WriteMemory(header, address);
|
||||
state.process->WriteMemory(data.data(), address + header.dataOffset, data.size());
|
||||
state.process->WriteMemory(objects.data(), address + header.objectsOffset, objects.size());
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ namespace skyline::service {
|
||||
class Parcel {
|
||||
private:
|
||||
/**
|
||||
* @brief This holds the header of a parcel
|
||||
* @brief The header of an Android Parcel structure
|
||||
*/
|
||||
struct ParcelHeader {
|
||||
u32 dataSize;
|
||||
@ -30,16 +30,18 @@ namespace skyline::service {
|
||||
* @brief This constructor fills in the Parcel object with data from a IPC buffer
|
||||
* @param buffer The buffer that contains the parcel
|
||||
* @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
|
||||
* @param address The remote address of the parcel
|
||||
* @param size The size of the parcel
|
||||
* @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
|
||||
|
@ -6,7 +6,7 @@ namespace skyline::service::hid {
|
||||
}) {}
|
||||
|
||||
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);
|
||||
state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle);
|
||||
response.copyHandles.push_back(handle);
|
||||
|
@ -3,24 +3,25 @@
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <services/base_service.h>
|
||||
#include <services/serviceman.h>
|
||||
|
||||
namespace skyline::service::hid {
|
||||
namespace skyline {
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
namespace service::hid {
|
||||
/**
|
||||
* @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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ namespace skyline::service::hosbinder {
|
||||
}) {}
|
||||
|
||||
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>(sizeof(GbpBuffer));
|
||||
out.WriteData<u32>(0);
|
||||
@ -29,7 +29,7 @@ namespace skyline::service::hosbinder {
|
||||
u32 height;
|
||||
u32 timestamps;
|
||||
u32 usage;
|
||||
} *data = reinterpret_cast<Data *>(in.data.data() + constant::TokenLength);
|
||||
} *data = reinterpret_cast<Data *>(in.data.data());
|
||||
|
||||
i64 slot{-1};
|
||||
while (slot == -1) {
|
||||
@ -66,7 +66,7 @@ namespace skyline::service::hosbinder {
|
||||
u64 _unk0_;
|
||||
u32 swapInterval;
|
||||
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);
|
||||
buffer->status = BufferStatus::Queued;
|
||||
@ -100,13 +100,13 @@ namespace skyline::service::hosbinder {
|
||||
struct Data {
|
||||
u32 slot;
|
||||
Fence fence[4];
|
||||
} *data = reinterpret_cast<Data *>(parcel.data.data() + constant::TokenLength);
|
||||
} *data = reinterpret_cast<Data *>(parcel.data.data());
|
||||
FreeBuffer(data->slot);
|
||||
state.logger->Debug("CancelBuffer: Slot: {}", data->slot);
|
||||
}
|
||||
|
||||
void IHOSBinderDriver::SetPreallocatedBuffer(Parcel &parcel) {
|
||||
auto pointer = parcel.data.data() + constant::TokenLength;
|
||||
auto pointer = parcel.data.data();
|
||||
struct Data {
|
||||
u32 slot;
|
||||
u32 _unk0_;
|
||||
@ -159,7 +159,7 @@ namespace skyline::service::hosbinder {
|
||||
auto layerId = request.Pop<u32>();
|
||||
auto code = request.Pop<TransactionCode>();
|
||||
|
||||
Parcel in(request.inputBuf.at(0), state);
|
||||
Parcel in(request.inputBuf.at(0), state, true);
|
||||
Parcel out(state);
|
||||
|
||||
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) {
|
||||
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);
|
||||
response.copyHandles.push_back(handle);
|
||||
response.Push<u32>(constant::status::Success);
|
||||
@ -219,7 +219,7 @@ namespace skyline::service::hosbinder {
|
||||
|
||||
void IHOSBinderDriver::SetDisplay(const std::string &name) {
|
||||
try {
|
||||
const auto type = displayTypeMap.at(name);
|
||||
const auto type = DisplayTypeMap.at(name);
|
||||
if (displayId == DisplayId::Null)
|
||||
displayId = type;
|
||||
else
|
||||
|
@ -17,7 +17,7 @@ namespace skyline::service::hosbinder {
|
||||
/**
|
||||
* @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},
|
||||
{"External", DisplayId::External},
|
||||
{"Edid", DisplayId::Edid},
|
||||
|
@ -32,8 +32,8 @@ namespace skyline::service::nvdrv::device {
|
||||
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
|
||||
handle_t handleIndex{1}; //!< This is used to keep track of the next handle to allocate
|
||||
std::unordered_map<KHandle, std::shared_ptr<NvMapObject>> handleTable; //!< A mapping from a handle to it's corresponding NvMapObject
|
||||
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
|
||||
|
||||
NvMap(const DeviceState &state);
|
||||
|
@ -70,7 +70,7 @@ namespace skyline::service {
|
||||
return serviceObj;
|
||||
}
|
||||
|
||||
handle_t ServiceManager::NewSession(const Service serviceType) {
|
||||
KHandle ServiceManager::NewSession(const Service serviceType) {
|
||||
std::lock_guard serviceGuard(mutex);
|
||||
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::lock_guard serviceGuard(mutex);
|
||||
auto serviceObject = CreateService(ServiceString.at(serviceName));
|
||||
handle_t handle{};
|
||||
KHandle handle{};
|
||||
if (response.isDomain) {
|
||||
session.domainTable[++session.handleIndex] = serviceObject;
|
||||
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)
|
||||
std::lock_guard serviceGuard(mutex);
|
||||
handle_t handle{};
|
||||
KHandle handle{};
|
||||
if (response.isDomain) {
|
||||
session.domainTable[session.handleIndex] = serviceObject;
|
||||
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);
|
||||
}
|
||||
|
||||
void ServiceManager::CloseSession(const handle_t handle) {
|
||||
void ServiceManager::CloseSession(const KHandle handle) {
|
||||
std::lock_guard serviceGuard(mutex);
|
||||
auto session = state.process->GetHandle<type::KSession>(handle);
|
||||
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);
|
||||
state.logger->Debug("----Start----");
|
||||
state.logger->Debug("Handle is 0x{:X}", handle);
|
||||
|
@ -32,7 +32,7 @@ namespace skyline::service {
|
||||
* @param serviceType The type 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
|
||||
@ -66,12 +66,12 @@ namespace skyline::service {
|
||||
* @brief Closes an existing session to a service
|
||||
* @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
|
||||
* @param handle The handle of the object
|
||||
*/
|
||||
void SyncRequestHandler(const handle_t handle);
|
||||
void SyncRequestHandler(const KHandle handle);
|
||||
};
|
||||
}
|
||||
|
@ -17,11 +17,11 @@ namespace skyline::service::timesrv {
|
||||
};
|
||||
response.Push(calendarTime);
|
||||
CalendarAdditionalInfo calendarInfo{
|
||||
.day_week = static_cast<u32>(calender.tm_wday),
|
||||
.day_month = static_cast<u32>(calender.tm_mday),
|
||||
.dayWeek = static_cast<u32>(calender.tm_wday),
|
||||
.dayMonth = static_cast<u32>(calender.tm_mday),
|
||||
.name = *reinterpret_cast<const u64 *>(calender.tm_zone),
|
||||
.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);
|
||||
}
|
||||
|
@ -27,11 +27,11 @@ namespace skyline::service::timesrv {
|
||||
* @brief This is passed in addition to CalendarTime
|
||||
*/
|
||||
struct CalendarAdditionalInfo {
|
||||
u32 day_week;
|
||||
u32 day_month;
|
||||
u32 dayWeek;
|
||||
u32 dayMonth;
|
||||
u64 name;
|
||||
i32 dst;
|
||||
u32 utc_rel;
|
||||
u32 utcRel;
|
||||
};
|
||||
static_assert(sizeof(CalendarAdditionalInfo) == 0x18);
|
||||
|
||||
|
@ -88,7 +88,7 @@ namespace skyline::service::visrv {
|
||||
}
|
||||
|
||||
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);
|
||||
response.copyHandles.push_back(handle);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user