Improve NCE and Finish Up Refactor

This commit mainly finishes up refactor by fixing everything brought up in the CR + Improving NCE somewhat and actually killing the child processes properly now.
This commit is contained in:
◱ PixelyIon 2020-04-22 22:32:27 +05:30 committed by ◱ PixelyIon
parent c76ef3730b
commit 05f3e3c3ac
68 changed files with 529 additions and 429 deletions

View File

@ -2,16 +2,21 @@
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="400" /> <option name="RIGHT_MARGIN" value="400" />
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" /> <option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
<option name="FORMATTER_TAGS_ENABLED" value="true" />
<option name="SOFT_MARGINS" value="80,140" /> <option name="SOFT_MARGINS" value="80,140" />
<JetCodeStyleSettings>
<option name="SPACE_BEFORE_TYPE_COLON" value="true" />
</JetCodeStyleSettings>
<Objective-C> <Objective-C>
<option name="INDENT_VISIBILITY_KEYWORDS" value="2" /> <option name="INDENT_VISIBILITY_KEYWORDS" value="2" />
<option name="INDENT_PREPROCESSOR_DIRECTIVE" value="4" /> <option name="INDENT_PREPROCESSOR_DIRECTIVE" value="4" />
<option name="INDENT_DIRECTIVE_AS_CODE" value="true" /> <option name="INDENT_DIRECTIVE_AS_CODE" value="true" />
<option name="KEEP_STRUCTURES_IN_ONE_LINE" value="true" /> <option name="KEEP_STRUCTURES_IN_ONE_LINE" value="true" />
<option name="KEEP_NESTED_NAMESPACES_IN_ONE_LINE" value="true" />
<option name="FUNCTION_NON_TOP_AFTER_RETURN_TYPE_WRAP" value="0" /> <option name="FUNCTION_NON_TOP_AFTER_RETURN_TYPE_WRAP" value="0" />
<option name="FUNCTION_TOP_AFTER_RETURN_TYPE_WRAP" value="0" /> <option name="FUNCTION_TOP_AFTER_RETURN_TYPE_WRAP" value="0" />
<option name="FUNCTION_PARAMETERS_WRAP" value="0" /> <option name="FUNCTION_PARAMETERS_WRAP" value="0" />
<option name="FUNCTION_CALL_ARGUMENTS_WRAP" value="5" /> <option name="FUNCTION_CALL_ARGUMENTS_WRAP" value="0" />
<option name="SHIFT_OPERATION_WRAP" value="0" /> <option name="SHIFT_OPERATION_WRAP" value="0" />
<option name="TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE" value="true" /> <option name="TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE" value="true" />
<option name="CLASS_CONSTRUCTOR_INIT_LIST_WRAP" value="0" /> <option name="CLASS_CONSTRUCTOR_INIT_LIST_WRAP" value="0" />
@ -82,6 +87,7 @@
<option name="WRAP_LONG_LINES" value="true" /> <option name="WRAP_LONG_LINES" value="true" />
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="ObjectiveC"> <codeStyleSettings language="ObjectiveC">
<option name="RIGHT_MARGIN" value="999" />
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" /> <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" /> <option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" /> <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
@ -92,7 +98,6 @@
<option name="TERNARY_OPERATION_WRAP" value="0" /> <option name="TERNARY_OPERATION_WRAP" value="0" />
<option name="FOR_STATEMENT_WRAP" value="1" /> <option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="5" /> <option name="ARRAY_INITIALIZER_WRAP" value="5" />
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
<option name="ASSIGNMENT_WRAP" value="1" /> <option name="ASSIGNMENT_WRAP" value="1" />
<option name="SOFT_MARGINS" value="140" /> <option name="SOFT_MARGINS" value="140" />
<indentOptions> <indentOptions>
@ -209,5 +214,8 @@
</rules> </rules>
</arrangement> </arrangement>
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="RIGHT_MARGIN" value="999" />
</codeStyleSettings>
</code_scheme> </code_scheme>
</component> </component>

View File

@ -1,6 +1,6 @@
<component name="CopyrightManager"> <component name="CopyrightManager">
<copyright> <copyright>
<option name="notice" value="SPDX-License-Identifier: MPL-2.0&#10;Copyright © &amp;#36;today.year Skyline Team and Contributors (https://github.com/skyline-emu/)" /> <option name="notice" value="SPDX-License-Identifier: MPL-2.0&#10;Copyright © &amp;#36;today.year Skyline Team and Contributors (https://github.com/skyline-emu/)" />
<option name="myName" value="Skyline-LGPLv3-or-later" /> <option name="myName" value="Skyline-License" />
</copyright> </copyright>
</component> </component>

View File

@ -1,7 +1,7 @@
<component name="CopyrightManager"> <component name="CopyrightManager">
<settings> <settings>
<module2copyright> <module2copyright>
<element module="SkylineKotlin" copyright="Skyline-LGPLv3-or-later" /> <element module="SkylineKotlin" copyright="Skyline-License" />
</module2copyright> </module2copyright>
</settings> </settings>
</component> </component>

View File

@ -46,7 +46,8 @@ Use doxygen style comments for:
* Enumerations - Use a `/**` block comment with a brief for the enum itself and a `//!<` single-line comment for all the individual items * Enumerations - Use a `/**` block comment with a brief for the enum itself and a `//!<` single-line comment for all the individual items
Notes: Notes:
* The DeviceState object can be skipped from function argument documentation as well as class members in the constructor * The `DeviceState` object or other objects which are systematically used throughout multiple classes such as `ServiceManager` can be skipped from function argument documentation as well as class members in the constructor
* The `KSession`, `IpcRequest` and `IpcResponse` objects in IPC command function arguments and other such objects (Such as `IoctlData`) can be skipped from function argument documentation if used in those contexts, they will need to be documented if they're used as a class member or something on those lines
* Any class members don't need to be redundantly documented in the constructor * Any class members don't need to be redundantly documented in the constructor
### Spacing ### Spacing
@ -190,17 +191,23 @@ void ClassConstructor(const Class& class) : class(class) {} // Make a copy direc
Use C++ range-based iterators for any C++ container iteration unless it can be performed better with functional programming (After C++20 when they are merged with the container). In addition, stick to using references/const references using them Use C++ range-based iterators for any C++ container iteration unless it can be performed better with functional programming (After C++20 when they are merged with the container). In addition, stick to using references/const references using them
### Usage of auto ### Usage of auto
Use `auto` unless a variable needs to be a specific type that isn't automatically deducible. Use `auto` to assign a variable the type of the value it's being assigned to, but not where a different type is desired. So, as a rule of thumb always specify the type when setting something from a number rather than depending on `auto`. In addition, prefer not to use `auto` in cases where it's hard to determine the return type due to assigned value being complex.
```cpp ```cpp
u16 a = 20; // Only if `a` is required to specifically be 16-bit, otherwise integers should be auto u8 a = 20; // `20` won't be stored in a `u8` but rather in a `int` (i32, generally) if `auto` is used
Handle b = 0x10; // Handle is a specific type that won't be automatically assigned auto b = std::make_shared<Something>(); // In this case `auto` is used to avoid typing out `std::shared_ptr<Something>`
``` ```
### Primitive Types
We generally use `in` and `un` where `n = {8, 16, 32, 64}` for our integer primitives in which `i` represents signed integers and `u` represents unsigned integers. In addition, we have some other types such as `KHandle` that are used to make certain operations more clear, use these depending on the context.
### Constants ### Constants
If a variable is constant at compile time use `constexpr`, if it's only used in a local function then place it in the function but if it's used throughout a class then in the corresponding header add the variable to the `skyline::constant` namespace. If a constant is used throughout the codebase, add it to `common.h`. If a variable is constant at compile time use `constexpr`, if it's only used in a local function then place it in the function but if it's used throughout a class then in the corresponding header add the variable to the `skyline::constant` namespace. If a constant is used throughout the codebase, add it to `common.h`.
In addition, try to `constexpr` as much as possible including constructors and functions so that they may be initialized at compile-time and have lesser runtime overhead during usage and certain values can be pre-calculated in advance. In addition, try to `constexpr` as much as possible including constructors and functions so that they may be initialized at compile-time and have lesser runtime overhead during usage and certain values can be pre-calculated in advance.
We should also mention that this isn't promoting the usage of `const`, it's use is actually discouraged out of references, in which case it is extremely encouraged. In addition, pointers are a general exception to this, using `const` with them isn't encouraged nor discouraged. Another exception are class functions, they can be made `const` if used from a `const` reference/pointer and don't
modify any members but do not do this preemptively.
## Kotlin ## Kotlin
### Naming rules ### Naming rules
* Enumerator: `PascalCase` **(1)** * Enumerator: `PascalCase` **(1)**

View File

@ -34,7 +34,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
std::signal(SIGABRT, signalHandler); std::signal(SIGABRT, signalHandler);
std::signal(SIGFPE, signalHandler); std::signal(SIGFPE, signalHandler);
setpriority(PRIO_PROCESS, static_cast<id_t>(getpid()), skyline::constant::AndroidPriority.second); setpriority(PRIO_PROCESS, static_cast<id_t>(gettid()), -8); // Set the priority of this process to the highest value
auto jvmManager = std::make_shared<skyline::JvmManager>(env, instance); auto jvmManager = std::make_shared<skyline::JvmManager>(env, instance);
auto settings = std::make_shared<skyline::Settings>(preferenceFd); auto settings = std::make_shared<skyline::Settings>(preferenceFd);
@ -45,7 +45,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
try { try {
skyline::kernel::OS os(jvmManager, logger, settings); skyline::kernel::OS os(jvmManager, logger, settings);
const char *romUri = env->GetStringUTFChars(romUriJstring, nullptr); auto romUri = env->GetStringUTFChars(romUriJstring, nullptr);
logger->Info("Launching ROM {}", romUri); logger->Info("Launching ROM {}", romUri);
env->ReleaseStringUTFChars(romUriJstring, romUri); env->ReleaseStringUTFChars(romUriJstring, romUri);
os.Execute(romFd, static_cast<skyline::TitleFormat>(romType)); os.Execute(romFd, static_cast<skyline::TitleFormat>(romType));

View File

@ -16,7 +16,7 @@ namespace skyline::audio {
outputStream->requestStart(); outputStream->requestStart();
} }
std::shared_ptr<AudioTrack> Audio::OpenTrack(const int channelCount, const int sampleRate, const std::function<void()> &releaseCallback) { std::shared_ptr<AudioTrack> Audio::OpenTrack(u8 channelCount, u32 sampleRate, const std::function<void()> &releaseCallback) {
std::lock_guard trackGuard(trackLock); std::lock_guard trackGuard(trackLock);
auto track = std::make_shared<AudioTrack>(channelCount, sampleRate, releaseCallback); auto track = std::make_shared<AudioTrack>(channelCount, sampleRate, releaseCallback);
@ -33,8 +33,8 @@ namespace skyline::audio {
} }
oboe::DataCallbackResult Audio::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) { oboe::DataCallbackResult Audio::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) {
i16 *destBuffer = static_cast<i16 *>(audioData); auto destBuffer = static_cast<i16 *>(audioData);
size_t streamSamples = static_cast<size_t>(numFrames) * audioStream->getChannelCount(); auto streamSamples = static_cast<size_t>(numFrames) * audioStream->getChannelCount();
size_t writtenSamples = 0; size_t writtenSamples = 0;
std::unique_lock trackGuard(trackLock); std::unique_lock trackGuard(trackLock);

View File

@ -32,7 +32,7 @@ namespace skyline::audio {
* @param releaseCallback The callback to call when a buffer has been released * @param releaseCallback The callback to call when a buffer has been released
* @return A shared pointer to a new AudioTrack object * @return A shared pointer to a new AudioTrack object
*/ */
std::shared_ptr<AudioTrack> OpenTrack(const int channelCount, const int sampleRate, const std::function<void()> &releaseCallback); std::shared_ptr<AudioTrack> OpenTrack(u8 channelCount, u32 sampleRate, const std::function<void()> &releaseCallback);
/** /**
* @brief Closes a track and frees its data * @brief Closes a track and frees its data

View File

@ -8,10 +8,10 @@
namespace skyline { namespace skyline {
namespace constant { namespace constant {
constexpr auto SampleRate = 48000; //!< The common sampling rate to use for audio output constexpr u16 SampleRate = 48000; //!< The common sampling rate to use for audio output
constexpr auto ChannelCount = 2; //!< The common amount of channels to use for audio output constexpr u8 ChannelCount = 2; //!< The common amount of channels to use for audio output
constexpr u16 MixBufferSize = 960; //!< The size of the mix buffer by default
constexpr auto PcmFormat = oboe::AudioFormat::I16; //!< The common PCM data format to use for audio output constexpr auto PcmFormat = oboe::AudioFormat::I16; //!< The common PCM data format to use for audio output
constexpr size_t MixBufferSize = 960; //!< The size of the mix buffer by default
}; };
namespace audio { namespace audio {

View File

@ -6,13 +6,17 @@
#include "resampler.h" #include "resampler.h"
namespace skyline::audio { namespace skyline::audio {
/**
* @brief This holds the coefficients of a single output frame
*/
struct LutEntry { struct LutEntry {
i32 a; i32 a; //!< The coefficient for the first index
i32 b; i32 b; //!< The coefficient for the second index
i32 c; i32 c; //!< The coefficient for the third index
i32 d; i32 d; //!< The coefficient for the fourth index
}; };
// @formatter:off
constexpr std::array<LutEntry, 128> CurveLut0 = {{ constexpr std::array<LutEntry, 128> CurveLut0 = {{
{6600, 19426, 6722, 3}, {6479, 19424, 6845, 9}, {6359, 19419, 6968, 15}, {6239, 19412, 7093, 22}, {6600, 19426, 6722, 3}, {6479, 19424, 6845, 9}, {6359, 19419, 6968, 15}, {6239, 19412, 7093, 22},
{6121, 19403, 7219, 28}, {6004, 19391, 7345, 34}, {5888, 19377, 7472, 41}, {5773, 19361, 7600, 48}, {6121, 19403, 7219, 28}, {6004, 19391, 7345, 34}, {5888, 19377, 7472, 41}, {5773, 19361, 7600, 48},
@ -114,9 +118,10 @@ namespace skyline::audio {
{-76, 5004, 25919, 1912}, {-72, 4837, 25980, 2015}, {-67, 4673, 26035, 2120}, {-63, 4512, 26085, 2227}, {-76, 5004, 25919, 1912}, {-72, 4837, 25980, 2015}, {-67, 4673, 26035, 2120}, {-63, 4512, 26085, 2227},
{-58, 4354, 26130, 2338}, {-54, 4199, 26169, 2451}, {-50, 4046, 26202, 2568}, {-46, 3897, 26230, 2688}, {-58, 4354, 26130, 2338}, {-54, 4199, 26169, 2451}, {-50, 4046, 26202, 2568}, {-46, 3897, 26230, 2688},
{-42, 3751, 26253, 2811}, {-38, 3608, 26270, 2936}, {-34, 3467, 26281, 3064}, {-32, 3329, 26287, 3195}}}; {-42, 3751, 26253, 2811}, {-38, 3608, 26270, 2936}, {-34, 3467, 26281, 3064}, {-32, 3329, 26287, 3195}}};
// @formatter:on
std::vector<i16> Resampler::ResampleBuffer(const std::vector<i16> &inputBuffer, double ratio, u8 channelCount) { std::vector<i16> Resampler::ResampleBuffer(const std::vector<i16> &inputBuffer, double ratio, u8 channelCount) {
auto step = static_cast<uint>(ratio * 0x8000); auto step = static_cast<u32>(ratio * 0x8000);
auto outputSize = static_cast<size_t>(inputBuffer.size() / ratio); auto outputSize = static_cast<size_t>(inputBuffer.size() / ratio);
std::vector<i16> outputBuffer(outputSize); std::vector<i16> outputBuffer(outputSize);
@ -129,7 +134,7 @@ namespace skyline::audio {
return CurveLut2; return CurveLut2;
}(); }();
for (auto outIndex = 0, inIndex = 0; outIndex < outputBuffer.size(); outIndex += channelCount) { for (size_t outIndex = 0, inIndex = 0; outIndex < outputSize; outIndex += channelCount) {
auto lutIndex = (fraction >> 8) << 2; auto lutIndex = (fraction >> 8) << 2;
for (u8 channel = 0; channel < channelCount; channel++) { for (u8 channel = 0; channel < channelCount; channel++) {

View File

@ -4,7 +4,7 @@
#include "track.h" #include "track.h"
namespace skyline::audio { namespace skyline::audio {
AudioTrack::AudioTrack(const u8 channelCount, const u32 sampleRate, const std::function<void()> &releaseCallback) : channelCount(channelCount), sampleRate(sampleRate), releaseCallback(releaseCallback) { AudioTrack::AudioTrack(u8 channelCount, u32 sampleRate, const std::function<void()> &releaseCallback) : channelCount(channelCount), sampleRate(sampleRate), releaseCallback(releaseCallback) {
if (sampleRate != constant::SampleRate) if (sampleRate != constant::SampleRate)
throw exception("Unsupported audio sample rate: {}", sampleRate); throw exception("Unsupported audio sample rate: {}", sampleRate);
@ -33,7 +33,7 @@ namespace skyline::audio {
std::vector<u64> AudioTrack::GetReleasedBuffers(u32 max) { std::vector<u64> AudioTrack::GetReleasedBuffers(u32 max) {
std::vector<u64> bufferIds; std::vector<u64> bufferIds;
for (auto index = 0; index < max; index++) { for (u32 index = 0; index < max; index++) {
if (!identifiers.back().released) if (!identifiers.back().released)
break; break;
bufferIds.push_back(identifiers.back().tag); bufferIds.push_back(identifiers.back().tag);

View File

@ -15,11 +15,11 @@ namespace skyline::audio {
*/ */
class AudioTrack { class AudioTrack {
private: private:
const std::function<void()> releaseCallback; //!< Callback called when a buffer has been played std::function<void()> releaseCallback; //!< Callback called when a buffer has been played
std::deque<BufferIdentifier> identifiers; //!< Queue of all appended buffer identifiers std::deque<BufferIdentifier> identifiers; //!< Queue of all appended buffer identifiers
const u8 channelCount; //!< The amount channels present in the track u8 channelCount; //!< The amount channels present in the track
const u32 sampleRate; //!< The sample rate of the track u32 sampleRate; //!< The sample rate of the track
public: public:
CircularBuffer<i16, constant::SampleRate * constant::ChannelCount * 10> samples; //!< A vector of all appended audio samples CircularBuffer<i16, constant::SampleRate * constant::ChannelCount * 10> samples; //!< A vector of all appended audio samples
@ -33,7 +33,7 @@ namespace skyline::audio {
* @param sampleRate The sample rate to use for the track * @param sampleRate The sample rate to use for the track
* @param releaseCallback A callback to call when a buffer has been played * @param releaseCallback A callback to call when a buffer has been played
*/ */
AudioTrack(const u8 channelCount, const u32 sampleRate, const std::function<void()> &releaseCallback); AudioTrack(u8 channelCount, u32 sampleRate, const std::function<void()> &releaseCallback);
/** /**
* @brief Starts audio playback using data from appended buffers * @brief Starts audio playback using data from appended buffers

View File

@ -64,10 +64,10 @@ namespace skyline {
flag.exchange(next); flag.exchange(next);
} }
Settings::Settings(const int preferenceFd) { Settings::Settings(int fd) {
tinyxml2::XMLDocument pref; tinyxml2::XMLDocument pref;
if (pref.LoadFile(fdopen(preferenceFd, "r"))) if (pref.LoadFile(fdopen(fd, "r")))
throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr())); throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
tinyxml2::XMLElement *elem = pref.LastChild()->FirstChild()->ToElement(); tinyxml2::XMLElement *elem = pref.LastChild()->FirstChild()->ToElement();
@ -120,8 +120,8 @@ namespace skyline {
logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first)); logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first));
} }
Logger::Logger(const int logFd, LogLevel configLevel) : configLevel(configLevel) { Logger::Logger(int fd, LogLevel configLevel) : configLevel(configLevel) {
logFile.__open(logFd, std::ios::app); logFile.__open(fd, std::ios::app);
WriteHeader("Logging started"); WriteHeader("Logging started");
} }
@ -137,7 +137,7 @@ namespace skyline {
logFile << "0|" << str << "\n"; logFile << "0|" << str << "\n";
} }
void Logger::Write(const LogLevel level, std::string str) { void Logger::Write(LogLevel level, std::string str) {
syslog(levelSyslog[static_cast<u8>(level)], "%s", str.c_str()); syslog(levelSyslog[static_cast<u8>(level)], "%s", str.c_str());
for (auto &character : str) for (auto &character : str)

View File

@ -26,16 +26,15 @@ namespace skyline {
namespace constant { namespace constant {
// Memory // Memory
constexpr auto BaseAddress = 0x8000000; //!< The address space base constexpr u64 BaseAddress = 0x8000000; //!< The address space base
constexpr auto DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB constexpr u64 DefStackSize = 0x1E8480; //!< The default amount of stack: 2 MB
// Kernel
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 // Display
constexpr auto HandheldResolutionW = 1280; //!< The width component of the handheld resolution constexpr u16 HandheldResolutionW = 1280; //!< The width component of the handheld resolution
constexpr auto HandheldResolutionH = 720; //!< The height component of the handheld resolution constexpr u16 HandheldResolutionH = 720; //!< The height component of the handheld resolution
constexpr auto DockedResolutionW = 1920; //!< The width component of the docked resolution constexpr u16 DockedResolutionW = 1920; //!< The width component of the docked resolution
constexpr auto DockedResolutionH = 1080; //!< The height component of the docked resolution constexpr u16 DockedResolutionH = 1080; //!< The height component of the docked resolution
// Time
constexpr u64 NsInSecond = 1000000000; //!< This is the amount of nanoseconds in a second
// Status codes // Status codes
namespace status { namespace status {
constexpr u32 Success = 0x0; //!< "Success" constexpr u32 Success = 0x0; //!< "Success"
@ -74,13 +73,12 @@ namespace skyline {
* @return The current time in nanoseconds * @return The current time in nanoseconds
*/ */
inline u64 GetTimeNs() { inline u64 GetTimeNs() {
constexpr uint64_t nsInSecond = 1000000000;
static u64 frequency{}; static u64 frequency{};
if (!frequency) if (!frequency)
asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency)); asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency));
u64 ticks; u64 ticks;
asm("MRS %0, CNTVCT_EL0" : "=r"(ticks)); asm("MRS %0, CNTVCT_EL0" : "=r"(ticks));
return ((ticks / frequency) * nsInSecond) + (((ticks % frequency) * nsInSecond + (frequency / 2)) / frequency); return ((ticks / frequency) * constant::NsInSecond) + (((ticks % frequency) * constant::NsInSecond + (frequency / 2)) / frequency);
} }
/** /**
@ -114,19 +112,28 @@ namespace skyline {
} }
/** /**
* @param address The address to check for alignment * @param value The value to check for alignment
* @return If the address is page aligned * @param multiple The multiple to check alignment with
* @return If the address is aligned with the multiple
*/ */
constexpr inline bool PageAligned(u64 address) { constexpr inline bool IsAligned(u64 value, u64 multiple) {
return !(address & (PAGE_SIZE - 1U)); return !(value & (multiple - 1U));
} }
/** /**
* @param address The address to check for alignment * @param value The value to check for alignment
* @return If the address is word aligned * @return If the value is page aligned
*/ */
constexpr inline bool WordAligned(u64 address) { constexpr inline bool PageAligned(u64 value) {
return !(address & 3U); return IsAligned(value, PAGE_SIZE);
}
/**
* @param value The value to check for alignment
* @return If the value is word aligned
*/
constexpr inline bool WordAligned(u64 value) {
return IsAligned(value, WORD_BIT / 8);
} }
/** /**
@ -222,10 +229,10 @@ namespace skyline {
LogLevel configLevel; //!< The level of logs to write LogLevel configLevel; //!< The level of logs to write
/** /**
* @param logFd A FD to the log file * @param fd A FD to the log file
* @param configLevel The minimum level of logs to write * @param configLevel The minimum level of logs to write
*/ */
Logger(const int logFd, LogLevel configLevel); Logger(int fd, LogLevel configLevel);
/** /**
* @brief Writes the termination message to the log file * @brief Writes the termination message to the log file
@ -243,7 +250,7 @@ namespace skyline {
* @param level The level of the log * @param level The level of the log
* @param str The value to be written * @param str The value to be written
*/ */
void Write(const LogLevel level, std::string str); void Write(LogLevel level, std::string str);
/** /**
* @brief Write an error log with libfmt formatting * @brief Write an error log with libfmt formatting
@ -305,9 +312,9 @@ namespace skyline {
public: public:
/** /**
* @param preferenceFd An FD to the preference XML file * @param fd An FD to the preference XML file
*/ */
Settings(const int preferenceFd); Settings(int fd);
/** /**
* @brief Retrieves a particular setting as a string * @brief Retrieves a particular setting as a string

View File

@ -62,8 +62,8 @@ namespace skyline::gpu {
if (frameTimestamp) { if (frameTimestamp) {
auto now = util::GetTimeNs(); auto now = util::GetTimeNs();
frametime = static_cast<u32>((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals frametime = static_cast<u32>((now - frameTimestamp) / (constant::NsInSecond / 100)); // frametime / 100 is the real ms value, this is to retain the first two decimals
fps = static_cast<u16>(1000000000 / (now - frameTimestamp)); fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp));
frameTimestamp = now; frameTimestamp = now;
} else { } else {

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include "texture.h"
namespace skyline::gpu::format {
using Format = gpu::texture::Format;
constexpr Format RGBA8888Unorm{sizeof(u8) * 4, 1, 1, vk::Format::eR8G8B8A8Unorm}; //!< 8-bits per channel 4-channel pixels
constexpr Format RGB565Unorm{sizeof(u8) * 2, 1, 1, vk::Format::eR5G6B5UnormPack16}; //!< Red channel: 5-bit, Green channel: 6-bit, Blue channel: 5-bit
}

View File

@ -20,10 +20,10 @@ namespace skyline::gpu {
if (guest->tileMode == texture::TileMode::Block) { if (guest->tileMode == texture::TileMode::Block) {
// Reference on Block-linear tiling: https://gist.github.com/PixelyIon/d9c35050af0ef5690566ca9f0965bc32 // Reference on Block-linear tiling: https://gist.github.com/PixelyIon/d9c35050af0ef5690566ca9f0965bc32
constexpr auto sectorWidth = 16; // The width of a sector in bytes constexpr u8 sectorWidth = 16; // The width of a sector in bytes
constexpr auto sectorHeight = 2; // The height of a sector in lines constexpr u8 sectorHeight = 2; // The height of a sector in lines
constexpr auto gobWidth = 64; // The width of a GOB in bytes constexpr u8 gobWidth = 64; // The width of a GOB in bytes
constexpr auto gobHeight = 8; // The height of a GOB in lines constexpr u8 gobHeight = 8; // The height of a GOB in lines
auto robHeight = gobHeight * guest->tileConfig.blockHeight; // The height of a single ROB (Row of Blocks) in lines auto robHeight = gobHeight * guest->tileConfig.blockHeight; // The height of a single ROB (Row of Blocks) in lines
auto surfaceHeightRobs = util::AlignUp(dimensions.height / format.blockHeight, robHeight) / robHeight; // The height of the surface in ROBs (Row Of Blocks) auto surfaceHeightRobs = util::AlignUp(dimensions.height / format.blockHeight, robHeight) / robHeight; // The height of the surface in ROBs (Row Of Blocks)
@ -41,8 +41,8 @@ namespace skyline::gpu {
auto outputGob = outputBlock; // We iterate through a GOB independently of the block auto outputGob = outputBlock; // We iterate through a GOB independently of the block
for (u32 gobY = 0; gobY < guest->tileConfig.blockHeight; gobY++) { // Every Block contains `blockHeight` Y-axis GOBs for (u32 gobY = 0; gobY < guest->tileConfig.blockHeight; gobY++) { // Every Block contains `blockHeight` Y-axis GOBs
for (u32 index = 0; index < sectorWidth * sectorHeight; index++) { // Every Y-axis GOB contains `sectorWidth * sectorHeight` sectors for (u32 index = 0; index < sectorWidth * sectorHeight; index++) { // Every Y-axis GOB contains `sectorWidth * sectorHeight` sectors
const u32 xT = ((index << 3) & 0b10000) | ((index << 1) & 0b100000); // Morton-Swizzle on the X-axis u32 xT = ((index << 3) & 0b10000) | ((index << 1) & 0b100000); // Morton-Swizzle on the X-axis
const u32 yT = ((index >> 1) & 0b110) | (index & 0b1); // Morton-Swizzle on the Y-axis u32 yT = ((index >> 1) & 0b110) | (index & 0b1); // Morton-Swizzle on the Y-axis
std::memcpy(outputGob + (yT * robWidthBytes) + xT, inputSector, sectorWidth); std::memcpy(outputGob + (yT * robWidthBytes) + xT, inputSector, sectorWidth);
inputSector += sectorWidth; // `sectorWidth` bytes are of sequential image data inputSector += sectorWidth; // `sectorWidth` bytes are of sequential image data
} }
@ -59,7 +59,7 @@ namespace skyline::gpu {
auto inputLine = texture; // The address of the input line auto inputLine = texture; // The address of the input line
auto outputLine = output; // The address of the output line auto outputLine = output; // The address of the output line
for (auto line = 0; line < dimensions.height; line++) { for (u32 line = 0; line < dimensions.height; line++) {
std::memcpy(outputLine, inputLine, sizeLine); std::memcpy(outputLine, inputLine, sizeLine);
inputLine += sizeStride; inputLine += sizeStride;
outputLine += sizeLine; outputLine += sizeLine;

View File

@ -97,11 +97,6 @@ namespace skyline {
} }
}; };
namespace format {
constexpr Format RGBA8888Unorm{sizeof(u8) * 4, 1, 1, vk::Format::eR8G8B8A8Unorm}; //!< 8-bits per channel 4-channel pixels
constexpr Format RGB565Unorm{sizeof(u8) * 2, 1, 1, vk::Format::eR5G6B5UnormPack16}; //!< Red channel: 5-bit, Green channel: 6-bit, Blue channel: 5-bit
}
/** /**
* @brief This describes the linearity of a texture. Refer to Chapter 20.1 of the Tegra X1 TRM for information. * @brief This describes the linearity of a texture. Refer to Chapter 20.1 of the Tegra X1 TRM for information.
*/ */

View File

@ -125,14 +125,14 @@ namespace skyline {
/** /**
* @return The address of the buffer * @return The address of the buffer
*/ */
inline u64 Address() const { inline u64 Address() {
return static_cast<u64>(address0_31) | static_cast<u64>(address32_35) << 32 | static_cast<u64>(address36_38) << 36; return static_cast<u64>(address0_31) | static_cast<u64>(address32_35) << 32 | static_cast<u64>(address36_38) << 36;
} }
/** /**
* @return The buffer counter * @return The counter index of the buffer
*/ */
inline u16 Counter() const { inline u16 Counter() {
return static_cast<u16>(counter0_5) | static_cast<u16>(counter9_11) << 9; return static_cast<u16>(counter0_5) | static_cast<u16>(counter9_11) << 9;
} }
}; };
@ -161,14 +161,14 @@ namespace skyline {
/** /**
* @return The address of the buffer * @return The address of the buffer
*/ */
inline u64 Address() const { inline u64 Address() {
return static_cast<u64>(address0_31) | static_cast<u64>(address32_35) << 32 | static_cast<u64>(address36_38) << 36; return static_cast<u64>(address0_31) | static_cast<u64>(address32_35) << 32 | static_cast<u64>(address36_38) << 36;
} }
/** /**
* @return The size of the buffer * @return The size of the buffer
*/ */
inline u64 Size() const { inline u64 Size() {
return static_cast<u64>(size0_31) | static_cast<u64>(size32_35) << 32; return static_cast<u64>(size0_31) | static_cast<u64>(size32_35) << 32;
} }
}; };

View File

@ -92,7 +92,7 @@ namespace skyline::kernel {
chunk->size = size; chunk->size = size;
} }
void MemoryManager::InsertBlock(ChunkDescriptor *chunk, const BlockDescriptor block) { void MemoryManager::InsertBlock(ChunkDescriptor *chunk, BlockDescriptor block) {
if (chunk->address + chunk->size < block.address + block.size) if (chunk->address + chunk->size < block.address + block.size)
throw exception("InsertBlock: Inserting block past chunk end is not allowed"); throw exception("InsertBlock: Inserting block past chunk end is not allowed");
@ -118,7 +118,7 @@ namespace skyline::kernel {
throw exception("InsertBlock: Block offset not present within current block list"); throw exception("InsertBlock: Block offset not present within current block list");
} }
void MemoryManager::InitializeRegions(u64 address, u64 size, const memory::AddressSpaceType type) { void MemoryManager::InitializeRegions(u64 address, u64 size, memory::AddressSpaceType type) {
switch (type) { switch (type) {
case memory::AddressSpaceType::AddressSpace32Bit: case memory::AddressSpaceType::AddressSpace32Bit:
throw exception("32-bit address spaces are not supported"); throw exception("32-bit address spaces are not supported");

View File

@ -16,29 +16,29 @@ namespace skyline {
/** /**
* @brief This constructor initializes all permissions to false * @brief This constructor initializes all permissions to false
*/ */
Permission() : r(), w(), x() {}; constexpr Permission() : r(), w(), x() {}
/** /**
* @param read If memory has read permission * @param read If memory has read permission
* @param write If memory has write permission * @param write If memory has write permission
* @param execute If memory has execute permission * @param execute If memory has execute permission
*/ */
Permission(bool read, bool write, bool execute) : r(read), w(write), x(execute) {}; constexpr Permission(bool read, bool write, bool execute) : r(read), w(write), x(execute) {}
/** /**
* @brief Equality operator between two Permission objects * @brief Equality operator between two Permission objects
*/ */
inline bool operator==(const Permission &rhs) const { return (this->r == rhs.r && this->w == rhs.w && this->x == rhs.x); }; inline bool operator==(const Permission &rhs) const { return (this->r == rhs.r && this->w == rhs.w && this->x == rhs.x); }
/** /**
* @brief Inequality operator between two Permission objects * @brief Inequality operator between two Permission objects
*/ */
inline bool operator!=(const Permission &rhs) const { return !operator==(rhs); }; inline bool operator!=(const Permission &rhs) const { return !operator==(rhs); }
/** /**
* @return The value of the permission struct in Linux format * @return The value of the permission struct in Linux format
*/ */
int Get() const { constexpr int Get() const {
int perm = 0; int perm = 0;
if (r) if (r)
perm |= PROT_READ; perm |= PROT_READ;
@ -47,7 +47,7 @@ namespace skyline {
if (x) if (x)
perm |= PROT_EXEC; perm |= PROT_EXEC;
return perm; return perm;
}; }
bool r; //!< The permission to read bool r; //!< The permission to read
bool w; //!< The permission to write bool w; //!< The permission to write
@ -114,9 +114,9 @@ namespace skyline {
* @brief This structure is used to hold the state of a certain block of memory (https://switchbrew.org/wiki/SVC#MemoryState) * @brief This structure is used to hold the state of a certain block of memory (https://switchbrew.org/wiki/SVC#MemoryState)
*/ */
union MemoryState { union MemoryState {
constexpr MemoryState(const u32 value) : value(value) {}; constexpr MemoryState(const u32 value) : value(value) {}
constexpr MemoryState() : value(0) {}; constexpr MemoryState() : value(0) {}
struct { struct {
MemoryType type; //!< The MemoryType of this memory block MemoryType type; //!< The MemoryType of this memory block
@ -304,9 +304,9 @@ namespace skyline {
/** /**
* @brief Insert a block into a chunk * @brief Insert a block into a chunk
* @param chunk The chunk to insert the block into * @param chunk The chunk to insert the block into
* @param block The block to insert * @param block The block to insert into the chunk
*/ */
static void InsertBlock(ChunkDescriptor *chunk, const BlockDescriptor block); static void InsertBlock(ChunkDescriptor *chunk, BlockDescriptor block);
/** /**
* @brief This initializes all of the regions in the address space * @brief This initializes all of the regions in the address space
@ -314,7 +314,7 @@ namespace skyline {
* @param size The size of the code region * @param size The size of the code region
* @param type The type of the address space * @param type The type of the address space
*/ */
void InitializeRegions(u64 address, u64 size, const memory::AddressSpaceType type); void InitializeRegions(u64 address, u64 size, memory::AddressSpaceType type);
public: public:
friend class type::KPrivateMemory; friend class type::KPrivateMemory;

View File

@ -6,10 +6,9 @@
namespace skyline::kernel::svc { namespace skyline::kernel::svc {
void SetHeapSize(DeviceState &state) { void SetHeapSize(DeviceState &state) {
constexpr auto heapSizeAlign = 0x200000; // The heap size has to be a multiple of this value
auto size = state.ctx->registers.w1; auto size = state.ctx->registers.w1;
if (size % heapSizeAlign != 0) { if (!util::IsAligned(size, 0x200000)) {
state.ctx->registers.w0 = constant::status::InvSize; state.ctx->registers.w0 = constant::status::InvSize;
state.ctx->registers.x1 = 0; state.ctx->registers.x1 = 0;
@ -219,19 +218,19 @@ namespace skyline::kernel::svc {
} }
void CreateThread(DeviceState &state) { void CreateThread(DeviceState &state) {
u64 entryAddress = state.ctx->registers.x1; auto entryAddress = state.ctx->registers.x1;
u64 entryArgument = state.ctx->registers.x2; auto entryArgument = state.ctx->registers.x2;
u64 stackTop = state.ctx->registers.x3; auto stackTop = state.ctx->registers.x3;
u8 priority = static_cast<u8>(state.ctx->registers.w4); auto priority = static_cast<i8>(state.ctx->registers.w4);
if ((priority < constant::SwitchPriority.first) || (priority > constant::SwitchPriority.second)) { if (!state.thread->switchPriority.Valid(priority)) {
state.ctx->registers.w0 = constant::status::InvAddress; state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority); state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
return; return;
} }
auto thread = state.process->CreateThread(entryAddress, entryArgument, stackTop, priority); auto thread = state.process->CreateThread(entryAddress, entryArgument, stackTop, priority);
state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, PID: {})", thread->handle, entryAddress, entryArgument, stackTop, priority, thread->tid); state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, TID: {})", thread->handle, entryAddress, entryArgument, stackTop, priority, thread->tid);
state.ctx->registers.w1 = thread->handle; state.ctx->registers.w1 = thread->handle;
state.ctx->registers.w0 = constant::status::Success; state.ctx->registers.w0 = constant::status::Success;
@ -305,7 +304,7 @@ namespace skyline::kernel::svc {
void MapSharedMemory(DeviceState &state) { void MapSharedMemory(DeviceState &state) {
try { try {
auto object = state.process->GetHandle<type::KSharedMemory>(state.ctx->registers.w0); auto object = state.process->GetHandle<type::KSharedMemory>(state.ctx->registers.w0);
u64 address = state.ctx->registers.x1; auto address = state.ctx->registers.x1;
if (!util::PageAligned(address)) { if (!util::PageAligned(address)) {
state.ctx->registers.w0 = constant::status::InvAddress; state.ctx->registers.w0 = constant::status::InvAddress;
@ -320,8 +319,7 @@ namespace skyline::kernel::svc {
return; return;
} }
u32 perm = state.ctx->registers.w3; memory::Permission permission = *reinterpret_cast<memory::Permission *>(&state.ctx->registers.w3);
memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm);
if ((permission.w && !permission.r) || (permission.x && !permission.r)) { if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
state.logger->Warn("svcMapSharedMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); state.logger->Warn("svcMapSharedMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
state.ctx->registers.w0 = constant::status::InvPermission; state.ctx->registers.w0 = constant::status::InvPermission;
@ -340,22 +338,21 @@ namespace skyline::kernel::svc {
} }
void CreateTransferMemory(DeviceState &state) { void CreateTransferMemory(DeviceState &state) {
u64 address = state.ctx->registers.x1; auto address = state.ctx->registers.x1;
if (!util::PageAligned(address)) { if (!util::PageAligned(address)) {
state.ctx->registers.w0 = constant::status::InvAddress; state.ctx->registers.w0 = constant::status::InvAddress;
state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: 0x{:X}", address); state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: 0x{:X}", address);
return; return;
} }
u64 size = state.ctx->registers.x2; auto size = state.ctx->registers.x2;
if (!util::PageAligned(size)) { if (!util::PageAligned(size)) {
state.ctx->registers.w0 = constant::status::InvSize; state.ctx->registers.w0 = constant::status::InvSize;
state.logger->Warn("svcCreateTransferMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); state.logger->Warn("svcCreateTransferMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return; return;
} }
u32 perm = state.ctx->registers.w3; memory::Permission permission = *reinterpret_cast<memory::Permission *>(&state.ctx->registers.w3);
memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm);
if ((permission.w && !permission.r) || (permission.x && !permission.r)) { if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
state.logger->Warn("svcCreateTransferMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-"); state.logger->Warn("svcCreateTransferMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
state.ctx->registers.w0 = constant::status::InvPermission; state.ctx->registers.w0 = constant::status::InvPermission;
@ -387,11 +384,11 @@ namespace skyline::kernel::svc {
try { try {
auto &object = state.process->handles.at(handle); auto &object = state.process->handles.at(handle);
switch (object->objectType) { switch (object->objectType) {
case (type::KType::KEvent): case type::KType::KEvent:
std::static_pointer_cast<type::KEvent>(object)->ResetSignal(); std::static_pointer_cast<type::KEvent>(object)->ResetSignal();
break; break;
case (type::KType::KProcess): case type::KType::KProcess:
std::static_pointer_cast<type::KProcess>(object)->ResetSignal(); std::static_pointer_cast<type::KProcess>(object)->ResetSignal();
break; break;

View File

@ -27,7 +27,7 @@ namespace skyline::kernel::type {
* @param size The size of the partition to change the permissions of * @param size The size of the partition to change the permissions of
* @param permission The new permissions to be set for the memory * @param permission The new permissions to be set for the memory
*/ */
virtual void UpdatePermission(const u64 address, const u64 size, memory::Permission permission) = 0; virtual void UpdatePermission(u64 address, u64 size, memory::Permission permission) = 0;
/** /**
* @brief Updates the permissions of a chunk of mapped memory * @brief Updates the permissions of a chunk of mapped memory

View File

@ -9,7 +9,7 @@
#include "KProcess.h" #include "KProcess.h"
namespace skyline::kernel::type { namespace skyline::kernel::type {
KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState) : size(size), KMemory(state, KType::KPrivateMemory) { KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, memory::MemoryState memState) : size(size), KMemory(state, KType::KPrivateMemory) {
if (address && !util::PageAligned(address)) if (address && !util::PageAligned(address))
throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", address); throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", address);
@ -113,7 +113,7 @@ namespace skyline::kernel::type {
size = nSize; size = nSize;
} }
void KPrivateMemory::UpdatePermission(const u64 address, const u64 size, memory::Permission permission) { void KPrivateMemory::UpdatePermission(u64 address, u64 size, memory::Permission permission) {
Registers fregs{ Registers fregs{
.x0 = address, .x0 = address,
.x1 = size, .x1 = size,

View File

@ -24,7 +24,7 @@ namespace skyline::kernel::type {
* @param permission The permissions for the allocated memory * @param permission The permissions for the allocated memory
* @param memState The MemoryState of the chunk of memory * @param memState The MemoryState of the chunk of memory
*/ */
KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState); KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, memory::MemoryState memState);
/** /**
* @brief Remap a chunk of memory as to change the size occupied by it * @brief Remap a chunk of memory as to change the size occupied by it
@ -39,7 +39,7 @@ namespace skyline::kernel::type {
* @param size The size of the partition to change the permissions of * @param size The size of the partition to change the permissions of
* @param permission The new permissions to be set for the memory * @param permission The new permissions to be set for the memory
*/ */
virtual void UpdatePermission(const u64 address, const u64 size, memory::Permission permission); virtual void UpdatePermission(u64 address, u64 size, memory::Permission permission);
/** /**
* @brief Updates the permissions of a chunk of mapped memory * @brief Updates the permissions of a chunk of mapped memory

View File

@ -78,7 +78,7 @@ namespace skyline::kernel::type {
status = Status::Exiting; status = Status::Exiting;
} }
std::shared_ptr<KThread> KProcess::CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, u8 priority) { std::shared_ptr<KThread> KProcess::CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, i8 priority) {
auto size = (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); auto size = (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, size, memory::Permission{true, true, false}, memory::states::Reserved); auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, size, memory::Permission{true, true, false}, memory::states::Reserved);
@ -102,12 +102,12 @@ namespace skyline::kernel::type {
return process; return process;
} }
u64 KProcess::GetHostAddress(u64 address) const { u64 KProcess::GetHostAddress(u64 address) {
auto chunk = state.os->memory.GetChunk(address); auto chunk = state.os->memory.GetChunk(address);
return (chunk && chunk->host) ? chunk->host + (address - chunk->address) : 0; return (chunk && chunk->host) ? chunk->host + (address - chunk->address) : 0;
} }
void KProcess::ReadMemory(void *destination, const u64 offset, const size_t size, const bool forceGuest) const { void KProcess::ReadMemory(void *destination, u64 offset, size_t size, bool forceGuest) {
if (!forceGuest) { if (!forceGuest) {
auto source = GetHostAddress(offset); auto source = GetHostAddress(offset);
@ -131,7 +131,7 @@ namespace skyline::kernel::type {
pread64(memFd, destination, size, offset); pread64(memFd, destination, size, offset);
} }
void KProcess::WriteMemory(const void *source, const u64 offset, const size_t size, const bool forceGuest) const { void KProcess::WriteMemory(const void *source, u64 offset, size_t size, bool forceGuest) {
if (!forceGuest) { if (!forceGuest) {
auto destination = GetHostAddress(offset); auto destination = GetHostAddress(offset);
@ -155,7 +155,7 @@ namespace skyline::kernel::type {
pwrite64(memFd, source, size, offset); pwrite64(memFd, source, size, offset);
} }
void KProcess::CopyMemory(u64 source, u64 destination, size_t size) const { void KProcess::CopyMemory(u64 source, u64 destination, size_t size) {
auto sourceHost = GetHostAddress(source); auto sourceHost = GetHostAddress(source);
auto destinationHost = GetHostAddress(destination); auto destinationHost = GetHostAddress(destination);

View File

@ -146,14 +146,14 @@ namespace skyline {
* @param priority The priority of the thread * @param priority The priority of the thread
* @return An instance of KThread class for the corresponding thread * @return An instance of KThread class for the corresponding thread
*/ */
std::shared_ptr<KThread> CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, u8 priority); std::shared_ptr<KThread> CreateThread(u64 entryPoint, u64 entryArg, u64 stackTop, i8 priority);
/** /**
* @brief This returns the host address for a specific address in guest memory * @brief This returns the host address for a specific address in guest memory
* @param address The corresponding guest address * @param address The corresponding guest address
* @return The corresponding host address * @return The corresponding host address
*/ */
u64 GetHostAddress(const u64 address) const; u64 GetHostAddress(u64 address);
/** /**
* @tparam Type The type of the pointer to return * @tparam Type The type of the pointer to return
@ -162,7 +162,7 @@ namespace skyline {
* @note This can return a nullptr if the address is invalid * @note This can return a nullptr if the address is invalid
*/ */
template<typename Type> template<typename Type>
inline Type *GetPointer(const u64 address) const { inline Type *GetPointer(u64 address) {
return reinterpret_cast<Type *>(GetHostAddress(address)); return reinterpret_cast<Type *>(GetHostAddress(address));
} }
@ -173,7 +173,7 @@ namespace skyline {
* @return A reference to object with type T * @return A reference to object with type T
*/ */
template<typename Type> template<typename Type>
inline Type &GetReference(u64 address) const { inline Type &GetReference(u64 address) {
auto source = GetPointer<Type>(address); auto source = GetPointer<Type>(address);
if (source) if (source)
return *source; return *source;
@ -188,7 +188,7 @@ namespace skyline {
* @return A copy of the object from guest memory * @return A copy of the object from guest memory
*/ */
template<typename Type> template<typename Type>
inline Type GetObject(u64 address) const { inline Type GetObject(u64 address) {
auto source = GetPointer<Type>(address); auto source = GetPointer<Type>(address);
if (source) { if (source) {
return *source; return *source;
@ -205,7 +205,7 @@ namespace skyline {
* @param maxSize The maximum size of the string * @param maxSize The maximum size of the string
* @return A copy of a string in guest memory * @return A copy of a string in guest memory
*/ */
inline std::string GetString(u64 address, const size_t maxSize) const { inline std::string GetString(u64 address, size_t maxSize) {
auto source = GetPointer<char>(address); auto source = GetPointer<char>(address);
if (source) if (source)
return std::string(source, maxSize); return std::string(source, maxSize);
@ -221,7 +221,7 @@ namespace skyline {
* @param address The address of the object * @param address The address of the object
*/ */
template<typename Type> template<typename Type>
inline void WriteMemory(Type &item, u64 address) const { inline void WriteMemory(Type &item, u64 address) {
auto destination = GetPointer<Type>(address); auto destination = GetPointer<Type>(address);
if (destination) { if (destination) {
*destination = item; *destination = item;
@ -237,7 +237,7 @@ namespace skyline {
* @param address The address of the object * @param address The address of the object
*/ */
template<typename Type> template<typename Type>
inline void WriteMemory(const Type &item, u64 address) const { inline void WriteMemory(const Type &item, u64 address) {
auto destination = GetPointer<Type>(address); auto destination = GetPointer<Type>(address);
if (destination) { if (destination) {
*destination = item; *destination = item;
@ -253,7 +253,7 @@ namespace skyline {
* @param size The amount of memory to be read * @param size The amount of memory to be read
* @param forceGuest This flag forces the write to be performed in guest address space * @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; void ReadMemory(void *destination, u64 offset, size_t size, bool forceGuest = false);
/** /**
* @brief Write to the guest's memory * @brief Write to the guest's memory
@ -262,7 +262,7 @@ namespace skyline {
* @param size The amount of memory to be written * @param size The amount of memory to be written
* @param forceGuest This flag forces the write to be performed in guest address space * @param forceGuest This flag forces the write to be performed in guest address space
*/ */
void WriteMemory(const void *source, const u64 offset, const size_t size, const bool forceGuest = false) const; void WriteMemory(const void *source, u64 offset, size_t size, bool forceGuest = false);
/** /**
* @brief Copy one chunk to another in the guest's memory * @brief Copy one chunk to another in the guest's memory
@ -270,7 +270,7 @@ namespace skyline {
* @param destination The address to write the read data to * @param destination The address to write the read data to
* @param size The amount of memory to be copied * @param size The amount of memory to be copied
*/ */
void CopyMemory(const u64 source, const u64 destination, const size_t size) const; void CopyMemory(u64 source, u64 destination, size_t size);
/** /**
* @brief Creates a new handle to a KObject and adds it to the process handle_table * @brief Creates a new handle to a KObject and adds it to the process handle_table

View File

@ -13,10 +13,10 @@ namespace skyline::kernel::type {
*/ */
class KSession : public KSyncObject { class KSession : public KSyncObject {
public: public:
const std::shared_ptr<service::BaseService> serviceObject; //!< A shared pointer to the service class std::shared_ptr<service::BaseService> serviceObject; //!< A shared pointer to the service class
std::unordered_map<KHandle, std::shared_ptr<service::BaseService>> domainTable; //!< This maps from a virtual handle to it's service std::unordered_map<KHandle, std::shared_ptr<service::BaseService>> domainTable; //!< This maps from a virtual handle to it's service
KHandle handleIndex{0x1}; //!< The currently allocated handle index KHandle handleIndex{0x1}; //!< The currently allocated handle index
const service::Service serviceType; //!< The type of the service service::Service serviceType; //!< The type of the service
enum class ServiceStatus { Open, Closed } serviceStatus{ServiceStatus::Open}; //!< If the session is open or closed enum class ServiceStatus { Open, Closed } serviceStatus{ServiceStatus::Open}; //!< If the session is open or closed
bool isDomain{}; //!< Holds if this is a domain session or not bool isDomain{}; //!< Holds if this is a domain session or not

View File

@ -9,7 +9,7 @@
#include "KProcess.h" #include "KProcess.h"
namespace skyline::kernel::type { namespace skyline::kernel::type {
KSharedMemory::KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState, int mmapFlags) : initialState(memState), KMemory(state, KType::KSharedMemory) { KSharedMemory::KSharedMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, memory::MemoryState memState, int mmapFlags) : initialState(memState), KMemory(state, KType::KSharedMemory) {
if (address && !util::PageAligned(address)) if (address && !util::PageAligned(address))
throw exception("KSharedMemory was created with non-page-aligned address: 0x{:X}", address); throw exception("KSharedMemory was created with non-page-aligned address: 0x{:X}", address);

View File

@ -38,7 +38,7 @@ namespace skyline::kernel::type {
* @param memState The MemoryState of the chunk of memory * @param memState The MemoryState of the chunk of memory
* @param mmapFlags Additional flags to pass to mmap * @param mmapFlags Additional flags to pass to mmap
*/ */
KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState = memory::states::SharedMemory, int mmapFlags = 0); KSharedMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, memory::MemoryState memState = memory::states::SharedMemory, int mmapFlags = 0);
/** /**
* @brief Maps the shared memory in the guest * @brief Maps the shared memory in the guest
@ -47,7 +47,7 @@ namespace skyline::kernel::type {
* @param permission The permission of the kernel process * @param permission The permission of the kernel process
* @return The address of the allocation * @return The address of the allocation
*/ */
u64 Map(const u64 address, const u64 size, memory::Permission permission); u64 Map(u64 address, u64 size, memory::Permission permission);
/** /**
* @brief Resize a chunk of memory as to change the size occupied by it * @brief Resize a chunk of memory as to change the size occupied by it

View File

@ -7,8 +7,8 @@
#include "KProcess.h" #include "KProcess.h"
namespace skyline::kernel::type { namespace skyline::kernel::type {
KThread::KThread(const DeviceState &state, KHandle handle, pid_t selfTid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, u8 priority, KProcess *parent, std::shared_ptr<type::KSharedMemory> &tlsMemory) KThread::KThread(const DeviceState &state, KHandle handle, pid_t selfTid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, i8 priority, KProcess *parent, const std::shared_ptr<type::KSharedMemory> &tlsMemory) : handle(handle), tid(selfTid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), ctxMemory(tlsMemory), KSyncObject(state,
: handle(handle), tid(selfTid), entryPoint(entryPoint), entryArg(entryArg), stackTop(stackTop), tls(tls), priority(priority), parent(parent), ctxMemory(tlsMemory), KSyncObject(state, KType::KThread) { KType::KThread) {
UpdatePriority(priority); UpdatePriority(priority);
} }
@ -30,16 +30,16 @@ namespace skyline::kernel::type {
if (status != Status::Dead) { if (status != Status::Dead) {
status = Status::Dead; status = Status::Dead;
Signal(); Signal();
tgkill(parent->pid, tid, SIGKILL);
tgkill(parent->pid, tid, SIGTERM);
} }
} }
void KThread::UpdatePriority(u8 priority) { void KThread::UpdatePriority(i8 priority) {
this->priority = priority; this->priority = priority;
auto linuxPriority = auto priorityValue = androidPriority.Rescale(switchPriority, priority);
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>(tid), linuxPriority) == -1) if (setpriority(PRIO_PROCESS, static_cast<id_t>(tid), priorityValue) == -1)
throw exception("Couldn't set process priority to {} for PID: {}", linuxPriority, tid); throw exception("Couldn't set process priority to {} for PID: {}", priorityValue, tid);
} }
} }

View File

@ -16,6 +16,31 @@ namespace skyline::kernel::type {
u64 entryPoint; //!< The address to start execution at u64 entryPoint; //!< The address to start execution at
u64 entryArg; //!< An argument to pass to the process on entry u64 entryArg; //!< An argument to pass to the process on entry
/**
* @brief This holds a range of priorities for a corresponding system
*/
struct Priority {
i8 low; //!< The low range of priority
i8 high; //!< The high range of priority
/**
* @param priority The priority range of the value
* @param value The priority value to rescale
* @return The rescaled priority value according to this range
*/
constexpr inline i8 Rescale(const Priority &priority, i8 value) {
return static_cast<i8>(priority.low + ((static_cast<float>(priority.high - priority.low) / static_cast<float>(priority.low - priority.high)) * (static_cast<float>(value) - priority.low)));
}
/**
* @param value The priority value to check for validity
* @return If the supplied priority value is valid
*/
constexpr inline bool Valid(i8 value) {
return (value >= low) && (value <= high);
}
};
public: public:
enum class Status { enum class Status {
Created, //!< The thread has been created but has not been started yet Created, //!< The thread has been created but has not been started yet
@ -28,7 +53,10 @@ namespace skyline::kernel::type {
pid_t tid; //!< The TID of the current thread pid_t tid; //!< The TID of the current thread
u64 stackTop; //!< The top of the stack (Where it starts growing downwards from) u64 stackTop; //!< The top of the stack (Where it starts growing downwards from)
u64 tls; //!< The address of TLS (Thread Local Storage) slot assigned to the current thread u64 tls; //!< The address of TLS (Thread Local Storage) slot assigned to the current thread
u8 priority; //!< The priority of a thread in Nintendo format i8 priority; //!< The priority of a thread in Nintendo format
Priority androidPriority{19, -8}; //!< The range of priorities for Android
Priority switchPriority{0, 63}; //!< The range of priorities for the Nintendo Switch
/** /**
* @param state The state of the device * @param state The state of the device
@ -42,7 +70,7 @@ namespace skyline::kernel::type {
* @param parent The parent process of this thread * @param parent The parent process of this thread
* @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process * @param tlsMemory The KSharedMemory object for TLS memory allocated by the guest process
*/ */
KThread(const DeviceState &state, KHandle handle, pid_t selfTid, 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 selfTid, u64 entryPoint, u64 entryArg, u64 stackTop, u64 tls, i8 priority, KProcess *parent, const std::shared_ptr<type::KSharedMemory> &tlsMemory);
/** /**
* @brief Kills the thread and deallocates the memory allocated for stack. * @brief Kills the thread and deallocates the memory allocated for stack.
@ -64,6 +92,6 @@ namespace skyline::kernel::type {
* @details Set the priority of the current thread to `priority` using setpriority [https://linux.die.net/man/3/setpriority]. We rescale the priority from Nintendo scale to that of Android. * @details Set the priority of the current thread to `priority` using setpriority [https://linux.die.net/man/3/setpriority]. We rescale the priority from Nintendo scale to that of Android.
* @param priority The priority of the thread in Nintendo format * @param priority The priority of the thread in Nintendo format
*/ */
void UpdatePriority(u8 priority); void UpdatePriority(i8 priority);
}; };
} }

View File

@ -7,7 +7,7 @@
#include "KTransferMemory.h" #include "KTransferMemory.h"
namespace skyline::kernel::type { namespace skyline::kernel::type {
KTransferMemory::KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState) : host(host), size(size), KMemory(state, KType::KTransferMemory) { KTransferMemory::KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, memory::Permission permission, memory::MemoryState memState) : host(host), size(size), KMemory(state, KType::KTransferMemory) {
if (address && !util::PageAligned(address)) if (address && !util::PageAligned(address))
throw exception("KTransferMemory was created with non-page-aligned address: 0x{:X}", address); throw exception("KTransferMemory was created with non-page-aligned address: 0x{:X}", address);
@ -172,7 +172,7 @@ namespace skyline::kernel::type {
} }
} }
void KTransferMemory::UpdatePermission(const u64 address, const u64 size, memory::Permission permission) { void KTransferMemory::UpdatePermission(u64 address, u64 size, memory::Permission permission) {
BlockDescriptor block{ BlockDescriptor block{
.address = address, .address = address,
.size = size, .size = size,

View File

@ -26,7 +26,7 @@ namespace skyline::kernel::type {
* @param type The type of the memory * @param type The type of the memory
* @param memState The MemoryState of the chunk of memory * @param memState The MemoryState of the chunk of memory
*/ */
KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState = memory::states::TransferMemory); KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, memory::Permission permission, memory::MemoryState memState = memory::states::TransferMemory);
/** /**
* @brief Transfers this piece of memory to another process * @brief Transfers this piece of memory to another process
@ -50,7 +50,7 @@ namespace skyline::kernel::type {
* @param size The size of the partition to change the permissions of * @param size The size of the partition to change the permissions of
* @param permission The new permissions to be set for the memory * @param permission The new permissions to be set for the memory
*/ */
virtual void UpdatePermission(const u64 address, const u64 size, memory::Permission permission); virtual void UpdatePermission(u64 address, u64 size, memory::Permission permission);
/** /**
* @brief Updates the permissions of a chunk of mapped memory * @brief Updates the permissions of a chunk of mapped memory

View File

@ -10,7 +10,7 @@
namespace skyline::loader { namespace skyline::loader {
class Loader { class Loader {
protected: protected:
const int romFd; //!< An FD to the ROM file int fd; //!< An FD to the ROM file
/** /**
* @brief Read the file at a particular offset * @brief Read the file at a particular offset
@ -21,14 +21,14 @@ namespace skyline::loader {
*/ */
template<typename T> template<typename T>
inline void ReadOffset(T *output, u64 offset, size_t size) { inline void ReadOffset(T *output, u64 offset, size_t size) {
pread64(romFd, output, size, offset); pread64(fd, output, size, offset);
} }
public: public:
/** /**
* @param filePath The path to the ROM file * @param filePath The path to the ROM file
*/ */
Loader(const int romFd) : romFd(romFd) {} Loader(int fd) : fd(fd) {}
/** /**
* This loads in the data of the main process * This loads in the data of the main process

View File

@ -6,7 +6,7 @@
#include "nro.h" #include "nro.h"
namespace skyline::loader { namespace skyline::loader {
NroLoader::NroLoader(const int romFd) : Loader(romFd) { NroLoader::NroLoader(int fd) : Loader(fd) {
ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader)); ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader));
if (header.magic != util::MakeMagic<u32>("NRO0")) if (header.magic != util::MakeMagic<u32>("NRO0"))
@ -27,6 +27,10 @@ namespace skyline::loader {
u64 textSize = text.size(); u64 textSize = text.size();
u64 rodataSize = rodata.size(); u64 rodataSize = rodata.size();
u64 dataSize = data.size(); u64 dataSize = data.size();
if (!util::IsAligned(textSize, PAGE_SIZE) || !util::IsAligned(rodataSize, PAGE_SIZE) || !util::IsAligned(dataSize, PAGE_SIZE))
throw exception("LoadProcessData: Sections are not aligned with page size: 0x{:X}, 0x{:X}, 0x{:X}", textSize, rodataSize, dataSize);
u64 patchSize = patch.size() * sizeof(u32); u64 patchSize = patch.size() * sizeof(u32);
u64 padding = util::AlignUp(textSize + rodataSize + dataSize + header.bssSize + patchSize, PAGE_SIZE) - (textSize + rodataSize + dataSize + header.bssSize + patchSize); u64 padding = util::AlignUp(textSize + rodataSize + dataSize + header.bssSize + patchSize, PAGE_SIZE) - (textSize + rodataSize + dataSize + header.bssSize + patchSize);

View File

@ -46,12 +46,12 @@ namespace skyline::loader {
public: public:
/** /**
* @param filePath The path to the ROM file * @param fd A file descriptor to the ROM
*/ */
NroLoader(const int romFd); NroLoader(int fd);
/** /**
* This loads in the data of the main process * @brief This loads in the data of the main process
* @param process The process to load in the data * @param process The process to load in the data
* @param state The state of the device * @param state The state of the device
*/ */

View File

@ -36,7 +36,7 @@ namespace skyline {
if (__predict_false(!Surface)) if (__predict_false(!Surface))
continue; continue;
const u16 svc = static_cast<const u16>(state.ctx->commandId); auto svc = state.ctx->svc;
try { try {
if (kernel::svc::SvcTable[svc]) { if (kernel::svc::SvcTable[svc]) {
@ -51,7 +51,7 @@ namespace skyline {
state.ctx->state = ThreadState::WaitRun; state.ctx->state = ThreadState::WaitRun;
} else if (__predict_false(state.ctx->state == ThreadState::GuestCrash)) { } else if (__predict_false(state.ctx->state == ThreadState::GuestCrash)) {
state.logger->Warn("Thread with PID {} has crashed due to signal: {}", thread, strsignal(state.ctx->commandId)); state.logger->Warn("Thread with PID {} has crashed due to signal: {}", thread, strsignal(state.ctx->svc));
ThreadTrace(); ThreadTrace();
state.ctx->state = ThreadState::WaitRun; state.ctx->state = ThreadState::WaitRun;
@ -115,7 +115,7 @@ namespace skyline {
* So, we opted to use the hacky solution and disable optimizations for this single function. * So, we opted to use the hacky solution and disable optimizations for this single function.
*/ */
void ExecuteFunctionCtx(ThreadCall call, Registers &funcRegs, ThreadContext *ctx) __attribute__ ((optnone)) { void ExecuteFunctionCtx(ThreadCall call, Registers &funcRegs, ThreadContext *ctx) __attribute__ ((optnone)) {
ctx->commandId = static_cast<u32>(call); ctx->threadCall = call;
Registers registers = ctx->registers; Registers registers = ctx->registers;
while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel); while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel);
@ -192,9 +192,9 @@ namespace skyline {
if (ctx->sp) if (ctx->sp)
regStr += fmt::format("\nStack Pointer: 0x{:X}", ctx->sp); regStr += fmt::format("\nStack Pointer: 0x{:X}", ctx->sp);
constexpr auto numRegisters = 31; //!< The amount of general-purpose registers in ARMv8 constexpr u8 numRegisters = 31; //!< The amount of general-purpose registers in ARMv8
for (u16 index = 0; index < numRegisters - 2; index += 2) { for (u8 index = 0; index < numRegisters - 2; index += 2) {
auto xStr = index < 10 ? " X" : "X"; auto xStr = index < 10 ? " X" : "X";
regStr += fmt::format("\n{}{}: 0x{:<16X} {}{}: 0x{:X}", xStr, index, ctx->registers.regs[index], xStr, index + 1, ctx->registers.regs[index + 1]); regStr += fmt::format("\n{}{}: 0x{:<16X} {}{}: 0x{:X}", xStr, index, ctx->registers.regs[index], xStr, index + 1, ctx->registers.regs[index + 1]);
} }
@ -209,11 +209,11 @@ namespace skyline {
} }
std::vector<u32> NCE::PatchCode(std::vector<u8> &code, u64 baseAddress, i64 offset) { std::vector<u32> NCE::PatchCode(std::vector<u8> &code, u64 baseAddress, i64 offset) {
constexpr auto TpidrroEl0 = 0x5E83; // ID of TPIDRRO_EL0 in MRS constexpr u32 TpidrroEl0 = 0x5E83; // ID of TPIDRRO_EL0 in MRS
constexpr auto CntfrqEl0 = 0x5F00; // ID of CNTFRQ_EL0 in MRS constexpr u32 CntfrqEl0 = 0x5F00; // ID of CNTFRQ_EL0 in MRS
constexpr auto CntpctEl0 = 0x5F01; // ID of CNTPCT_EL0 in MRS constexpr u32 CntpctEl0 = 0x5F01; // ID of CNTPCT_EL0 in MRS
constexpr auto CntvctEl0 = 0x5F02; // ID of CNTVCT_EL0 in MRS constexpr u32 CntvctEl0 = 0x5F02; // ID of CNTVCT_EL0 in MRS
constexpr auto TegraX1Freq = 19200000; // The clock frequency of the Tegra X1 (19.2 MHz) constexpr u32 TegraX1Freq = 19200000; // The clock frequency of the Tegra X1 (19.2 MHz)
u32 *start = reinterpret_cast<u32 *>(code.data()); u32 *start = reinterpret_cast<u32 *>(code.data());
u32 *end = start + (code.size() / sizeof(u32)); u32 *end = start + (code.size() / sizeof(u32));
@ -239,23 +239,30 @@ namespace skyline {
auto instrMrs = reinterpret_cast<instr::Mrs *>(address); auto instrMrs = reinterpret_cast<instr::Mrs *>(address);
if (instrSvc->Verify()) { if (instrSvc->Verify()) {
// If this is an SVC we need to branch to saveCtx then to the SVC Handler after putting the PC + SVC into X0 and W1 and finally loadCtx before returning to where we were before
instr::B bJunc(offset); instr::B bJunc(offset);
constexpr u32 strLr = 0xF81F0FFE; // STR LR, [SP, #-16]! constexpr u32 strLr = 0xF81F0FFE; // STR LR, [SP, #-16]!
offset += sizeof(strLr); offset += sizeof(strLr);
instr::BL bSvCtx(patchOffset - offset); instr::BL bSvCtx(patchOffset - offset);
offset += sizeof(bSvCtx); offset += sizeof(bSvCtx);
auto movPc = instr::MoveU64Reg(regs::X0, baseAddress + (address - start)); auto movPc = instr::MoveRegister<u64>(regs::X0, baseAddress + (address - start));
offset += sizeof(u32) * movPc.size(); offset += sizeof(u32) * movPc.size();
instr::Movz movCmd(regs::W1, static_cast<u16>(instrSvc->value)); instr::Movz movCmd(regs::W1, static_cast<u16>(instrSvc->value));
offset += sizeof(movCmd); offset += sizeof(movCmd);
instr::BL bSvcHandler((patchOffset + guest::SaveCtxSize + guest::LoadCtxSize) - offset); instr::BL bSvcHandler((patchOffset + guest::SaveCtxSize + guest::LoadCtxSize) - offset);
offset += sizeof(bSvcHandler); offset += sizeof(bSvcHandler);
instr::BL bLdCtx((patchOffset + guest::SaveCtxSize) - offset); instr::BL bLdCtx((patchOffset + guest::SaveCtxSize) - offset);
offset += sizeof(bLdCtx); offset += sizeof(bLdCtx);
constexpr u32 ldrLr = 0xF84107FE; // LDR LR, [SP], #16 constexpr u32 ldrLr = 0xF84107FE; // LDR LR, [SP], #16
offset += sizeof(ldrLr); offset += sizeof(ldrLr);
instr::B bret(-offset + sizeof(u32)); instr::B bret(-offset + sizeof(u32));
offset += sizeof(bret); offset += sizeof(bret);
@ -271,24 +278,31 @@ namespace skyline {
patch.push_back(bret.raw); patch.push_back(bret.raw);
} else if (instrMrs->Verify()) { } else if (instrMrs->Verify()) {
if (instrMrs->srcReg == TpidrroEl0) { if (instrMrs->srcReg == TpidrroEl0) {
// If this moves TPIDRRO_EL0 into a register then we retrieve the value of our virtual TPIDRRO_EL0 from TLS and write it to the register
instr::B bJunc(offset); instr::B bJunc(offset);
u32 strX0{}; u32 strX0{};
if (instrMrs->destReg != regs::X0) { if (instrMrs->destReg != regs::X0) {
strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]! strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]!
offset += sizeof(strX0); offset += sizeof(strX0);
} }
const u32 mrsX0 = 0xD53BD040; // MRS X0, TPIDR_EL0
constexpr u32 mrsX0 = 0xD53BD040; // MRS X0, TPIDR_EL0
offset += sizeof(mrsX0); offset += sizeof(mrsX0);
const u32 ldrTls = 0xF9408000; // LDR X0, [X0, #256]
constexpr u32 ldrTls = 0xF9408000; // LDR X0, [X0, #256] (ThreadContext::tpidrroEl0)
offset += sizeof(ldrTls); offset += sizeof(ldrTls);
u32 movXn{}; u32 movXn{};
u32 ldrX0{}; u32 ldrX0{};
if (instrMrs->destReg != regs::X0) { if (instrMrs->destReg != regs::X0) {
movXn = instr::Mov(regs::X(instrMrs->destReg), regs::X0).raw; movXn = instr::Mov(regs::X(instrMrs->destReg), regs::X0).raw;
offset += sizeof(movXn); offset += sizeof(movXn);
ldrX0 = 0xF84107E0; // LDR X0, [SP], #16 ldrX0 = 0xF84107E0; // LDR X0, [SP], #16
offset += sizeof(ldrX0); offset += sizeof(ldrX0);
} }
instr::B bret(-offset + sizeof(u32)); instr::B bret(-offset + sizeof(u32));
offset += sizeof(bret); offset += sizeof(bret);
@ -303,14 +317,19 @@ namespace skyline {
patch.push_back(ldrX0); patch.push_back(ldrX0);
patch.push_back(bret.raw); patch.push_back(bret.raw);
} else if (frequency != TegraX1Freq) { } else if (frequency != TegraX1Freq) {
// These deal with changing the timer registers, we only do this if the clock frequency doesn't match the X1's clock frequency
if (instrMrs->srcReg == CntpctEl0) { if (instrMrs->srcReg == CntpctEl0) {
// If this moves CNTPCT_EL0 into a register then call RescaleClock to rescale the device's clock to the X1's clock frequency and write result to register
instr::B bJunc(offset); instr::B bJunc(offset);
offset += guest::RescaleClockSize; offset += guest::RescaleClockSize;
instr::Ldr ldr(0xF94003E0); // LDR XOUT, [SP] instr::Ldr ldr(0xF94003E0); // LDR XOUT, [SP]
ldr.destReg = instrMrs->destReg; ldr.destReg = instrMrs->destReg;
offset += sizeof(ldr); offset += sizeof(ldr);
const u32 addSp = 0x910083FF; // ADD SP, SP, #32
constexpr u32 addSp = 0x910083FF; // ADD SP, SP, #32
offset += sizeof(addSp); offset += sizeof(addSp);
instr::B bret(-offset + sizeof(u32)); instr::B bret(-offset + sizeof(u32));
offset += sizeof(bret); offset += sizeof(bret);
@ -322,9 +341,12 @@ namespace skyline {
patch.push_back(addSp); patch.push_back(addSp);
patch.push_back(bret.raw); patch.push_back(bret.raw);
} else if (instrMrs->srcReg == CntfrqEl0) { } else if (instrMrs->srcReg == CntfrqEl0) {
// If this moves CNTFRQ_EL0 into a register then move the Tegra X1's clock frequency into the register (Rather than the host clock frequency)
instr::B bJunc(offset); instr::B bJunc(offset);
auto movFreq = instr::MoveU32Reg(static_cast<regs::X>(instrMrs->destReg), TegraX1Freq);
auto movFreq = instr::MoveRegister<u32>(static_cast<regs::X>(instrMrs->destReg), TegraX1Freq);
offset += sizeof(u32) * movFreq.size(); offset += sizeof(u32) * movFreq.size();
instr::B bret(-offset + sizeof(u32)); instr::B bret(-offset + sizeof(u32));
offset += sizeof(bret); offset += sizeof(bret);
@ -334,9 +356,10 @@ namespace skyline {
patch.push_back(bret.raw); patch.push_back(bret.raw);
} }
} else { } else {
// If the host clock frequency is the same as the Tegra X1's clock frequency
if (instrMrs->srcReg == CntpctEl0) { if (instrMrs->srcReg == CntpctEl0) {
instr::Mrs mrs(CntvctEl0, regs::X(instrMrs->destReg)); // If this moves CNTPCT_EL0 into a register, change the instruction to move CNTVCT_EL0 instead as Linux or most other OSes don't allow access to CNTPCT_EL0 rather only CNTVCT_EL0 can be accessed from userspace
*address = mrs.raw; *address = instr::Mrs(CntvctEl0, regs::X(instrMrs->destReg)).raw;
} }
} }
} }

View File

@ -5,6 +5,8 @@
#include <cstdlib> #include <cstdlib>
#include <initializer_list> // This is used implicitly #include <initializer_list> // This is used implicitly
#include <asm/siginfo.h> #include <asm/siginfo.h>
#include <unistd.h>
#include <asm/unistd.h>
#include "guest_common.h" #include "guest_common.h"
#define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage) #define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage)
@ -98,54 +100,14 @@ namespace skyline::guest {
/** /**
* @note Do not use any functions that cannot be inlined from this, as this function is placed at an arbitrary address in the guest. In addition, do not use any static variables or globals as the .bss section is not copied into the guest. * @note Do not use any functions that cannot be inlined from this, as this function is placed at an arbitrary address in the guest. In addition, do not use any static variables or globals as the .bss section is not copied into the guest.
*/ */
void SvcHandler(u64 pc, u32 svc) { void SvcHandler(u64 pc, u16 svc) {
volatile ThreadContext *ctx; volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx)); asm("MRS %0, TPIDR_EL0":"=r"(ctx));
ctx->pc = pc; ctx->pc = pc;
ctx->commandId = svc; ctx->svc = svc;
if (svc == 0xB) { // svcSleepThread if (svc == 0x1E) { // svcGetSystemTick
switch (ctx->registers.x0) {
case 0:
case 1:
case 2: {
asm("MOV X0, XZR\n\t"
"MOV X1, XZR\n\t"
"MOV X2, XZR\n\t"
"MOV X3, XZR\n\t"
"MOV X4, XZR\n\t"
"MOV X5, XZR\n\t"
"MOV X8, #124\n\t" // __NR_sched_yield
"STR LR, [SP, #-16]!\n\t"
"MOV LR, SP\n\t"
"SVC #0\n\t"
"MOV SP, LR\n\t"
"LDR LR, [SP], #16":: : "x0", "x1", "x2", "x3", "x4", "x5", "x8");
break;
}
default: {
struct timespec spec = {
.tv_sec = static_cast<time_t>(ctx->registers.x0 / 1000000000),
.tv_nsec = static_cast<long>(ctx->registers.x0 % 1000000000)
};
asm("MOV X0, %0\n\t"
"MOV X1, XZR\n\t"
"MOV X2, XZR\n\t"
"MOV X3, XZR\n\t"
"MOV X4, XZR\n\t"
"MOV X5, XZR\n\t"
"MOV X8, #101\n\t" // __NR_nanosleep
"STR LR, [SP, #-16]!\n\t"
"MOV LR, SP\n\t"
"SVC #0\n\t"
"MOV SP, LR\n\t"
"LDR LR, [SP], #16"::"r"(&spec) : "x0", "x1", "x2", "x3", "x4", "x5", "x8");
}
}
return;
} else if (svc == 0x1E) { // svcGetSystemTick
asm("STP X1, X2, [SP, #-16]!\n\t" asm("STP X1, X2, [SP, #-16]!\n\t"
"STR Q0, [SP, #-16]!\n\t" "STR Q0, [SP, #-16]!\n\t"
"STR Q1, [SP, #-16]!\n\t" "STR Q1, [SP, #-16]!\n\t"
@ -175,7 +137,7 @@ namespace skyline::guest {
if (ctx->state == ThreadState::WaitRun) { if (ctx->state == ThreadState::WaitRun) {
break; break;
} else if (ctx->state == ThreadState::WaitFunc) { } else if (ctx->state == ThreadState::WaitFunc) {
if (ctx->commandId == static_cast<u32>(ThreadCall::Syscall)) { if (ctx->threadCall == ThreadCall::Syscall) {
SaveCtxStack(); SaveCtxStack();
LoadCtxTls(); LoadCtxTls();
@ -187,7 +149,7 @@ namespace skyline::guest {
SaveCtxTls(); SaveCtxTls();
LoadCtxStack(); LoadCtxStack();
} else if (ctx->commandId == static_cast<u32>(ThreadCall::Memcopy)) { } else if (ctx->threadCall == ThreadCall::Memcopy) {
auto src = reinterpret_cast<u8 *>(ctx->registers.x0); auto src = reinterpret_cast<u8 *>(ctx->registers.x0);
auto dest = reinterpret_cast<u8 *>(ctx->registers.x1); auto dest = reinterpret_cast<u8 *>(ctx->registers.x1);
auto size = ctx->registers.x2; auto size = ctx->registers.x2;
@ -195,7 +157,7 @@ namespace skyline::guest {
while (src < end) while (src < end)
*(src++) = *(dest++); *(src++) = *(dest++);
} else if (ctx->commandId == static_cast<u32>(ThreadCall::Clone)) { } else if (ctx->threadCall == ThreadCall::Clone) {
SaveCtxStack(); SaveCtxStack();
LoadCtxTls(); LoadCtxTls();
@ -248,7 +210,15 @@ namespace skyline::guest {
ctx->state = ThreadState::Running; ctx->state = ThreadState::Running;
} }
void SignalHandler(int signal, siginfo_t *info, ucontext_t *ucontext) { [[noreturn]] void Exit(int) {
if (gettid() == getpid())
syscall(__NR_exit_group, 0);
else
syscall(__NR_exit, 0);
__builtin_unreachable();
}
[[noreturn]] void SignalHandler(int signal, siginfo_t *info, ucontext_t *ucontext) {
volatile ThreadContext *ctx; volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx)); asm("MRS %0, TPIDR_EL0":"=r"(ctx));
@ -256,7 +226,7 @@ namespace skyline::guest {
ctx->registers.regs[index] = ucontext->uc_mcontext.regs[index]; ctx->registers.regs[index] = ucontext->uc_mcontext.regs[index];
ctx->pc = ucontext->uc_mcontext.pc; ctx->pc = ucontext->uc_mcontext.pc;
ctx->commandId = static_cast<u32>(signal); ctx->signal = static_cast<u32>(signal);
ctx->faultAddress = ucontext->uc_mcontext.fault_address; ctx->faultAddress = ucontext->uc_mcontext.fault_address;
ctx->sp = ucontext->uc_mcontext.sp; ctx->sp = ucontext->uc_mcontext.sp;
@ -264,7 +234,7 @@ namespace skyline::guest {
ctx->state = ThreadState::GuestCrash; ctx->state = ThreadState::GuestCrash;
if (ctx->state == ThreadState::WaitRun) if (ctx->state == ThreadState::WaitRun)
exit(0); Exit(0);
} }
} }
@ -279,7 +249,7 @@ namespace skyline::guest {
if (ctx->state == ThreadState::WaitRun) { if (ctx->state == ThreadState::WaitRun) {
break; break;
} else if (ctx->state == ThreadState::WaitFunc) { } else if (ctx->state == ThreadState::WaitFunc) {
if (ctx->commandId == static_cast<u32>(ThreadCall::Syscall)) { if (ctx->threadCall == ThreadCall::Syscall) {
SaveCtxStack(); SaveCtxStack();
LoadCtxTls(); LoadCtxTls();
@ -292,7 +262,7 @@ namespace skyline::guest {
SaveCtxTls(); SaveCtxTls();
LoadCtxStack(); LoadCtxStack();
} }
} else if (ctx->commandId == static_cast<u32>(ThreadCall::Memcopy)) { } else if (ctx->threadCall == ThreadCall::Memcopy) {
auto src = reinterpret_cast<u8 *>(ctx->registers.x0); auto src = reinterpret_cast<u8 *>(ctx->registers.x0);
auto dest = reinterpret_cast<u8 *>(ctx->registers.x1); auto dest = reinterpret_cast<u8 *>(ctx->registers.x1);
auto size = ctx->registers.x2; auto size = ctx->registers.x2;
@ -311,6 +281,12 @@ namespace skyline::guest {
for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}) for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
sigaction(signal, &sigact, nullptr); sigaction(signal, &sigact, nullptr);
sigact = {
.sa_handler = Exit,
};
sigaction(SIGTERM, &sigact, nullptr);
ctx->state = ThreadState::Running; ctx->state = ThreadState::Running;
asm("MOV LR, %0\n\t" asm("MOV LR, %0\n\t"

View File

@ -42,6 +42,6 @@ namespace skyline {
* @param pc The address of PC when the call was being done * @param pc The address of PC when the call was being done
* @param svc The SVC ID of the SVC being called * @param svc The SVC ID of the SVC being called
*/ */
void SvcHandler(u64 pc, u32 svc); void SvcHandler(u64 pc, u16 svc);
} }
} }

View File

@ -123,7 +123,7 @@ namespace skyline {
/** /**
* @brief This enumeration is used to convey the state of a thread to the kernel * @brief This enumeration is used to convey the state of a thread to the kernel
*/ */
enum class ThreadState : u32 { enum class ThreadState : u8 {
NotReady = 0, //!< The thread hasn't yet entered the entry handler NotReady = 0, //!< The thread hasn't yet entered the entry handler
Running = 1, //!< The thread is currently executing code Running = 1, //!< The thread is currently executing code
WaitKernel = 2, //!< The thread is currently waiting on the kernel WaitKernel = 2, //!< The thread is currently waiting on the kernel
@ -136,10 +136,10 @@ namespace skyline {
/** /**
* @brief This enumeration holds the functions that can be run on the guest process * @brief This enumeration holds the functions that can be run on the guest process
*/ */
enum class ThreadCall : u32 { enum class ThreadCall : u8 {
Syscall = 0x100, //!< A linux syscall needs to be called from the guest Syscall = 1, //!< A linux syscall needs to be called from the guest
Memcopy = 0x101, //!< To copy memory from one location to another Memcopy = 2, //!< To copy memory from one location to another
Clone = 0x102, //!< Use the clone syscall to create a new thread Clone = 3, //!< Use the clone syscall to create a new thread
}; };
/** /**
@ -147,7 +147,9 @@ namespace skyline {
*/ */
struct ThreadContext { struct ThreadContext {
ThreadState state; //!< The state of the guest ThreadState state; //!< The state of the guest
u32 commandId; //!< The command ID of the current kernel call/function call ThreadCall threadCall; //!< The function to run in the guest process
u16 svc; //!< The SVC ID of the current kernel call
u32 signal; //!< The signal caught by the guest process
u64 pc; //!< The program counter register on the guest u64 pc; //!< The program counter register on the guest
Registers registers; //!< The general purpose registers on the guest Registers registers; //!< The general purpose registers on the guest
u64 tpidrroEl0; //!< The value for TPIDRRO_EL0 for the current thread u64 tpidrroEl0; //!< The value for TPIDRRO_EL0 for the current thread

View File

@ -190,12 +190,12 @@ namespace skyline {
* @brief Creates a MOVZ instruction * @brief Creates a MOVZ instruction
* @param destReg The destination Xn register to store the value in * @param destReg The destination Xn register to store the value in
* @param imm16 The 16-bit value to store * @param imm16 The 16-bit value to store
* @param shift The offset (in bits and 16-bit aligned) in the register to store the value at * @param shift The offset (in units of 16-bits) in the register to store the value at
*/ */
inline constexpr Movz(regs::X destReg, u16 imm16, u8 shift = 0) { inline constexpr Movz(regs::X destReg, u16 imm16, u8 shift = 0) {
this->destReg = static_cast<u8>(destReg); this->destReg = static_cast<u8>(destReg);
this->imm16 = imm16; this->imm16 = imm16;
hw = static_cast<u8>(shift / 16); hw = shift;
sig = 0xA5; sig = 0xA5;
sf = 1; sf = 1;
} }
@ -204,22 +204,22 @@ namespace skyline {
* @brief Creates a MOVZ instruction * @brief Creates a MOVZ instruction
* @param destReg The destination Wn register to store the value in * @param destReg The destination Wn register to store the value in
* @param imm16 The 16-bit value to store * @param imm16 The 16-bit value to store
* @param shift The offset (in bits and 16-bit aligned) in the register to store the value at * @param shift The offset (in units of 16-bits) in the register to store the value at
*/ */
inline constexpr Movz(regs::W destReg, u16 imm16, u8 shift = 0) { inline constexpr Movz(regs::W destReg, u16 imm16, u8 shift = 0) {
this->destReg = static_cast<u8>(destReg); this->destReg = static_cast<u8>(destReg);
this->imm16 = imm16; this->imm16 = imm16;
hw = static_cast<u8>(shift / 16); hw = shift;
sig = 0xA5; sig = 0xA5;
sf = 0; sf = 0;
} }
/** /**
* @brief Returns the offset of the instruction * @brief Returns the offset of the instruction
* @return The offset encoded within the instruction * @return The offset encoded within the instruction (In Bytes)
*/ */
inline constexpr u8 Shift() { inline constexpr u8 Shift() {
return static_cast<u8>(hw * 16); return static_cast<u8>(hw * sizeof(u16));
} }
/** /**
@ -252,12 +252,12 @@ namespace skyline {
* @brief Creates a MOVK instruction * @brief Creates a MOVK instruction
* @param destReg The destination Xn register to store the value in * @param destReg The destination Xn register to store the value in
* @param imm16 The 16-bit value to store * @param imm16 The 16-bit value to store
* @param shift The offset (in bits and 16-bit aligned) in the register to store the value at * @param shift The offset (in units of 16-bits) in the register to store the value at
*/ */
inline constexpr Movk(regs::X destReg, u16 imm16, u8 shift = 0) { inline constexpr Movk(regs::X destReg, u16 imm16, u8 shift = 0) {
this->destReg = static_cast<u8>(destReg); this->destReg = static_cast<u8>(destReg);
this->imm16 = imm16; this->imm16 = imm16;
hw = static_cast<u8>(shift / 16); hw = shift;
sig = 0xE5; sig = 0xE5;
sf = 1; sf = 1;
} }
@ -266,22 +266,22 @@ namespace skyline {
* @brief Creates a MOVK instruction * @brief Creates a MOVK instruction
* @param destReg The destination Wn register to store the value in * @param destReg The destination Wn register to store the value in
* @param imm16 The 16-bit value to store * @param imm16 The 16-bit value to store
* @param shift The offset (in bits and 16-bit aligned) in the register to store the value at * @param shift The offset (in units of 16-bits) in the register to store the value at
*/ */
inline constexpr Movk(regs::W destReg, u16 imm16, u8 shift = 0) { inline constexpr Movk(regs::W destReg, u16 imm16, u8 shift = 0) {
this->destReg = static_cast<u8>(destReg); this->destReg = static_cast<u8>(destReg);
this->imm16 = imm16; this->imm16 = imm16;
hw = static_cast<u8>(shift / 16); hw = shift;
sig = 0xE5; sig = 0xE5;
sf = 0; sf = 0;
} }
/** /**
* @brief Returns the offset of the instruction * @brief Returns the offset of the instruction
* @return The offset encoded within the instruction * @return The offset encoded within the instruction (In Bytes)
*/ */
inline constexpr u8 Shift() { inline constexpr u8 Shift() {
return static_cast<u8>(hw * 16); return static_cast<u8>(hw * sizeof(u16));
} }
/** /**
@ -306,57 +306,27 @@ namespace skyline {
static_assert(sizeof(Movk) == sizeof(u32)); static_assert(sizeof(Movk) == sizeof(u32));
/** /**
* @param destReg The destination register of the operation * @param destination The destination register of the operation
* @param value The 64-bit value to insert into the register * @param value The value to insert into the register
* @return A vector with the instructions to insert the value * @return A array with the instructions to insert the value
*/ */
inline const std::vector<u32> MoveU64Reg(regs::X destReg, u64 value) { template<typename Type>
union { inline constexpr std::array<u32, sizeof(Type) / sizeof(u16)> MoveRegister(regs::X destination, Type value) {
u64 val; std::array<u32, sizeof(Type) / sizeof(u16)> instructions;
struct {
u16 v0; auto valuePointer = reinterpret_cast<u16 *>(&value);
u16 v16; u8 offset{};
u16 v32;
u16 v48; for (auto &instruction : instructions) {
}; if (offset)
} val; instruction = instr::Movk(destination, *(valuePointer + offset), offset).raw;
val.val = value; else
std::vector<u32> instr; instruction = instr::Movz(destination, *(valuePointer + offset), offset).raw;
instr::Movz mov0(destReg, val.v0, 0);
instr.push_back(mov0.raw); offset++;
instr::Movk mov16(destReg, val.v16, 16);
if (val.v16)
instr.push_back(mov16.raw);
instr::Movk mov32(destReg, val.v32, 32);
if (val.v32)
instr.push_back(mov32.raw);
instr::Movk mov48(destReg, val.v48, 48);
if (val.v48)
instr.push_back(mov48.raw);
return instr;
} }
/** return instructions;
* @param destReg The destination register of the operation
* @param value The 32-bit value to insert into the register
* @return A vector with the instructions to insert the value
*/
inline const std::vector<u32> MoveU32Reg(regs::X destReg, u32 value) {
union {
u32 val;
struct {
u16 v0;
u16 v16;
};
} val;
val.val = value;
std::vector<u32> instr;
instr::Movz mov0(destReg, val.v0, 0);
instr.push_back(mov0.raw);
instr::Movk mov16(destReg, val.v16, 16);
if (val.v16)
instr.push_back(mov16.raw);
return instr;
} }
/** /**

View File

@ -8,7 +8,7 @@
namespace skyline::kernel { namespace skyline::kernel {
OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : state(this, process, jvmManager, settings, logger), memory(state), serviceManager(state) {} OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : state(this, process, jvmManager, settings, logger), memory(state), serviceManager(state) {}
void OS::Execute(const int romFd, const TitleFormat romType) { void OS::Execute(int romFd, TitleFormat romType) {
std::shared_ptr<loader::Loader> loader; std::shared_ptr<loader::Loader> loader;
if (romType == TitleFormat::NRO) { if (romType == TitleFormat::NRO) {
@ -33,7 +33,7 @@ namespace skyline::kernel {
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); auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission{true, true, false}, memory::states::Reserved);
tlsMem->guest = tlsMem->kernel; tlsMem->guest = tlsMem->kernel;
pid_t pid = clone(reinterpret_cast<int (*)(void *)>(&guest::GuestEntry), reinterpret_cast<void *>(stack->guest.address + stackSize), CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, reinterpret_cast<void *>(tlsMem->guest.address)); auto pid = clone(reinterpret_cast<int (*)(void *)>(&guest::GuestEntry), reinterpret_cast<void *>(stack->guest.address + stackSize), CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, reinterpret_cast<void *>(tlsMem->guest.address));
if (pid == -1) if (pid == -1)
throw exception("Call to clone() has failed: {}", strerror(errno)); throw exception("Call to clone() has failed: {}", strerror(errno));

View File

@ -37,7 +37,7 @@ namespace skyline::kernel {
* @param romFd A FD to the ROM file to execute * @param romFd A FD to the ROM file to execute
* @param romType The type of the ROM file * @param romType The type of the ROM file
*/ */
void Execute(const int romFd, const TitleFormat romType); void Execute(int romFd, TitleFormat romType);
/** /**
* @brief Creates a new process * @brief Creates a new process

View File

@ -34,6 +34,7 @@ namespace skyline::service::am {
response.errorCode = constant::status::NoMessages; response.errorCode = constant::status::NoMessages;
return; return;
} }
response.Push(messageQueue.front()); response.Push(messageQueue.front());
messageQueue.pop(); messageQueue.pop();
} }

View File

@ -25,10 +25,12 @@ namespace skyline::service::am {
void ISelfController::CreateManagedDisplayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void ISelfController::CreateManagedDisplayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
state.logger->Debug("Creating Managed Layer on Default Display"); state.logger->Debug("Creating Managed Layer on Default Display");
auto hosBinder = state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(Service::hosbinder_IHOSBinderDriver); auto hosBinder = state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(Service::hosbinder_IHOSBinderDriver);
if (hosBinder->layerStatus != hosbinder::LayerStatus::Uninitialized) if (hosBinder->layerStatus != hosbinder::LayerStatus::Uninitialized)
throw exception("The application is creating more than one layer"); throw exception("The application is creating more than one layer");
hosBinder->layerStatus = hosbinder::LayerStatus::Managed; hosBinder->layerStatus = hosbinder::LayerStatus::Managed;
response.Push<u64>(0); response.Push<u64>(0);
} }
} }

View File

@ -5,7 +5,7 @@
#include "IAudioOut.h" #include "IAudioOut.h"
namespace skyline::service::audio { namespace skyline::service::audio {
IAudioOut::IAudioOut(const DeviceState &state, ServiceManager &manager, const u8 channelCount, const u32 sampleRate) : sampleRate(sampleRate), channelCount(channelCount), releaseEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager, Service::audio_IAudioOut, "audio:IAudioOut", { IAudioOut::IAudioOut(const DeviceState &state, ServiceManager &manager, u8 channelCount, u32 sampleRate) : sampleRate(sampleRate), channelCount(channelCount), releaseEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager, Service::audio_IAudioOut, "audio:IAudioOut", {
{0x0, SFUNC(IAudioOut::GetAudioOutState)}, {0x0, SFUNC(IAudioOut::GetAudioOutState)},
{0x1, SFUNC(IAudioOut::StartAudioOut)}, {0x1, SFUNC(IAudioOut::StartAudioOut)},
{0x2, SFUNC(IAudioOut::StopAudioOut)}, {0x2, SFUNC(IAudioOut::StopAudioOut)},

View File

@ -28,7 +28,7 @@ namespace skyline::service::audio {
* @param channelCount The channel count of the audio data the audio out will be fed * @param channelCount The channel count of the audio data the audio out will be fed
* @param sampleRate The sample rate of the audio data the audio out will be fed * @param sampleRate The sample rate of the audio data the audio out will be fed
*/ */
IAudioOut(const DeviceState &state, ServiceManager &manager, const u8 channelCount, const u32 sampleRate); IAudioOut(const DeviceState &state, ServiceManager &manager, u8 channelCount, u32 sampleRate);
/** /**
* @brief Closes the audio track * @brief Closes the audio track

View File

@ -91,12 +91,12 @@ namespace skyline::service {
class BaseService { class BaseService {
protected: protected:
const DeviceState &state; //!< The state of the device const DeviceState &state; //!< The state of the device
ServiceManager &manager; //!< A pointer to the service manager ServiceManager &manager; //!< A reference to the service manager
const std::unordered_map<u32, std::function<void(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>> vTable; //!< This holds the mapping from an object's CmdId to the actual function std::unordered_map<u32, std::function<void(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>> vTable; //!< This holds the mapping from a function's CmdId to the actual function
public: public:
const Service serviceType; //!< The type of the service this is Service serviceType; //!< The type of this service
const std::string serviceName; //!< The name of the service std::string serviceName; //!< The name of this service
/** /**
* @param state The state of the device * @param state The state of the device
@ -105,7 +105,7 @@ namespace skyline::service {
* @param serviceName The name of the service * @param serviceName The name of the service
* @param vTable The functions of the service * @param vTable The functions of the service
*/ */
BaseService(const DeviceState &state, ServiceManager &manager, const Service serviceType, const std::string &serviceName, const std::unordered_map<u32, std::function<void(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>> &vTable) : state(state), manager(manager), serviceType(serviceType), serviceName(serviceName), vTable(vTable) {} BaseService(const DeviceState &state, ServiceManager &manager, Service serviceType, const std::string &serviceName, const std::unordered_map<u32, std::function<void(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>> &vTable) : state(state), manager(manager), serviceType(serviceType), serviceName(serviceName), vTable(vTable) {}
/** /**
* @brief This handles all IPC commands with type request to a service * @brief This handles all IPC commands with type request to a service

View File

@ -11,6 +11,6 @@ namespace skyline::service::fatalsrv {
}) {} }) {}
void IService::ThrowFatal(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IService::ThrowFatal(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
throw exception("A fatal error with code: 0x{:X} has caused emulation to stop", *reinterpret_cast<u32 *>(request.cmdArg)); throw exception("A fatal error with code: 0x{:X} has caused emulation to stop", request.Pop<u32>());
} }
} }

View File

@ -4,5 +4,5 @@
#include "IFileSystem.h" #include "IFileSystem.h"
namespace skyline::service::fssrv { namespace skyline::service::fssrv {
IFileSystem::IFileSystem(const FsType type, const DeviceState &state, ServiceManager &manager) : type(type), BaseService(state, manager, Service::fssrv_IFileSystem, "fssrv:IFileSystem", {}) {} IFileSystem::IFileSystem(FsType type, const DeviceState &state, ServiceManager &manager) : type(type), BaseService(state, manager, Service::fssrv_IFileSystem, "fssrv:IFileSystem", {}) {}
} }

View File

@ -11,9 +11,9 @@ namespace skyline::service::fssrv {
* @brief These are the possible types of the filesystem * @brief These are the possible types of the filesystem
*/ */
enum class FsType { enum class FsType {
Nand, Nand, //!< The internal NAND storage
SdCard, SdCard, //!< The external SDCard storage
GameCard GameCard, //!< The Game-Card of the inserted game (https://switchbrew.org/wiki/Gamecard)
}; };
/** /**
@ -21,7 +21,7 @@ namespace skyline::service::fssrv {
*/ */
class IFileSystem : public BaseService { class IFileSystem : public BaseService {
public: public:
const FsType type; FsType type; //!< The type of filesystem this class represents
IFileSystem(FsType type, const DeviceState &state, ServiceManager &manager); IFileSystem(FsType type, const DeviceState &state, ServiceManager &manager);
}; };

View File

@ -10,7 +10,7 @@ namespace skyline::service::fssrv {
}) {} }) {}
void IFileSystemProxy::SetCurrentProcess(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IFileSystemProxy::SetCurrentProcess(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
process = *reinterpret_cast<pid_t *>(request.cmdArg); process = request.Pop<pid_t>();
} }
void IFileSystemProxy::OpenSdCardFileSystem(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IFileSystemProxy::OpenSdCardFileSystem(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {

View File

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

View File

@ -30,6 +30,7 @@ namespace skyline::service::hid {
const auto &buffer = request.inputBuf.at(0); const auto &buffer = request.inputBuf.at(0);
size_t numId = buffer.size / sizeof(NpadId); size_t numId = buffer.size / sizeof(NpadId);
u64 address = buffer.address; u64 address = buffer.address;
for (size_t i = 0; i < numId; i++) { for (size_t i = 0; i < numId; i++) {
auto id = state.process->GetObject<NpadId>(address); auto id = state.process->GetObject<NpadId>(address);
deviceMap[id] = JoyConDevice(id); deviceMap[id] = JoyConDevice(id);
@ -50,6 +51,7 @@ namespace skyline::service::hid {
void IHidServer::SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IHidServer::SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto controllerId = request.Pop<NpadId>(); auto controllerId = request.Pop<NpadId>();
auto appletUserId = request.Pop<u64>(); auto appletUserId = request.Pop<u64>();
deviceMap[controllerId].assignment = JoyConAssignment::Single; deviceMap[controllerId].assignment = JoyConAssignment::Single;
deviceMap[controllerId].side = request.Pop<JoyConSide>(); deviceMap[controllerId].side = request.Pop<JoyConSide>();
} }

View File

@ -5,6 +5,7 @@
#include <os.h> #include <os.h>
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <services/nvdrv/INvDrvServices.h> #include <services/nvdrv/INvDrvServices.h>
#include <gpu/format.h>
#include "IHOSBinderDriver.h" #include "IHOSBinderDriver.h"
#include "display.h" #include "display.h"
@ -123,9 +124,9 @@ namespace skyline::service::hosbinder {
std::shared_ptr<nvdrv::device::NvMap::NvMapObject> nvBuffer{}; std::shared_ptr<nvdrv::device::NvMap::NvMapObject> nvBuffer{};
auto nvmap = state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->GetDevice<nvdrv::device::NvMap>(nvdrv::device::NvDeviceType::nvmap); auto nvmap = state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->GetDevice<nvdrv::device::NvMap>(nvdrv::device::NvDeviceType::nvmap);
if (gbpBuffer->nvmapHandle) if (gbpBuffer->nvmapHandle) {
nvBuffer = nvmap->handleTable.at(gbpBuffer->nvmapHandle); nvBuffer = nvmap->handleTable.at(gbpBuffer->nvmapHandle);
else { } else {
for (const auto &object : nvmap->handleTable) { for (const auto &object : nvmap->handleTable) {
if (object.second->id == gbpBuffer->nvmapId) { if (object.second->id == gbpBuffer->nvmapId) {
nvBuffer = object.second; nvBuffer = object.second;
@ -140,10 +141,10 @@ namespace skyline::service::hosbinder {
switch (gbpBuffer->format) { switch (gbpBuffer->format) {
case WINDOW_FORMAT_RGBA_8888: case WINDOW_FORMAT_RGBA_8888:
case WINDOW_FORMAT_RGBX_8888: case WINDOW_FORMAT_RGBX_8888:
format = gpu::texture::format::RGBA8888Unorm; format = gpu::format::RGBA8888Unorm;
break; break;
case WINDOW_FORMAT_RGB_565: case WINDOW_FORMAT_RGB_565:
format = gpu::texture::format::RGB565Unorm; format = gpu::format::RGB565Unorm;
break; break;
default: default:
throw exception("Unknown pixel format used for FB"); throw exception("Unknown pixel format used for FB");

View File

@ -22,28 +22,35 @@ namespace skyline::service::nvdrv {
std::shared_ptr<device::NvDevice> object; std::shared_ptr<device::NvDevice> object;
switch (type) { switch (type) {
case (device::NvDeviceType::nvhost_ctrl): case device::NvDeviceType::nvhost_ctrl:
object = std::make_shared<device::NvHostCtrl>(state); object = std::make_shared<device::NvHostCtrl>(state);
break; break;
case (device::NvDeviceType::nvhost_gpu):
case (device::NvDeviceType::nvhost_vic): case device::NvDeviceType::nvhost_gpu:
case (device::NvDeviceType::nvhost_nvdec): case device::NvDeviceType::nvhost_vic:
case device::NvDeviceType::nvhost_nvdec:
object = std::make_shared<device::NvHostChannel>(state, type); object = std::make_shared<device::NvHostChannel>(state, type);
break; break;
case (device::NvDeviceType::nvhost_ctrl_gpu):
case device::NvDeviceType::nvhost_ctrl_gpu:
object = std::make_shared<device::NvHostCtrlGpu>(state); object = std::make_shared<device::NvHostCtrlGpu>(state);
break; break;
case (device::NvDeviceType::nvmap):
case device::NvDeviceType::nvmap:
object = std::make_shared<device::NvMap>(state); object = std::make_shared<device::NvMap>(state);
break; break;
case (device::NvDeviceType::nvhost_as_gpu):
case device::NvDeviceType::nvhost_as_gpu:
object = std::make_shared<device::NvHostAsGpu>(state); object = std::make_shared<device::NvHostAsGpu>(state);
break; break;
default: default:
throw exception("Cannot find NVDRV device"); throw exception("Cannot find NVDRV device");
} }
deviceMap[type] = object; deviceMap[type] = object;
fdMap[fdIndex] = object; fdMap[fdIndex] = object;
return fdIndex++; return fdIndex++;
} }
@ -59,6 +66,7 @@ namespace skyline::service::nvdrv {
void INvDrvServices::Open(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void INvDrvServices::Open(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto buffer = request.inputBuf.at(0); auto buffer = request.inputBuf.at(0);
auto path = state.process->GetString(buffer.address, buffer.size); auto path = state.process->GetString(buffer.address, buffer.size);
response.Push<u32>(OpenDevice(path)); response.Push<u32>(OpenDevice(path));
response.Push<u32>(constant::status::Success); response.Push<u32>(constant::status::Success);
} }
@ -66,20 +74,24 @@ namespace skyline::service::nvdrv {
void INvDrvServices::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void INvDrvServices::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto fd = request.Pop<u32>(); auto fd = request.Pop<u32>();
auto cmd = request.Pop<u32>(); auto cmd = request.Pop<u32>();
state.logger->Debug("IOCTL on device: 0x{:X}, cmd: 0x{:X}", fd, cmd); state.logger->Debug("IOCTL on device: 0x{:X}, cmd: 0x{:X}", fd, cmd);
try { try {
if (request.inputBuf.empty() || request.outputBuf.empty()) { if (request.inputBuf.empty() || request.outputBuf.empty()) {
if (request.inputBuf.empty()) { if (request.inputBuf.empty()) {
device::IoctlData data(request.outputBuf.at(0)); device::IoctlData data(request.outputBuf.at(0));
fdMap.at(fd)->HandleIoctl(cmd, data); fdMap.at(fd)->HandleIoctl(cmd, data);
response.Push<u32>(data.status); response.Push<u32>(data.status);
} else { } else {
device::IoctlData data(request.inputBuf.at(0)); device::IoctlData data(request.inputBuf.at(0));
fdMap.at(fd)->HandleIoctl(cmd, data); fdMap.at(fd)->HandleIoctl(cmd, data);
response.Push<u32>(data.status); response.Push<u32>(data.status);
} }
} else { } else {
device::IoctlData data(request.inputBuf.at(0), request.outputBuf.at(0)); device::IoctlData data(request.inputBuf.at(0), request.outputBuf.at(0));
fdMap.at(fd)->HandleIoctl(cmd, data); fdMap.at(fd)->HandleIoctl(cmd, data);
response.Push<u32>(data.status); response.Push<u32>(data.status);
} }
@ -89,16 +101,19 @@ namespace skyline::service::nvdrv {
} }
void INvDrvServices::Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void INvDrvServices::Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
u32 fd = *reinterpret_cast<u32 *>(request.cmdArg); auto fd = request.Pop<u32>();
state.logger->Debug("Closing NVDRV device ({})", fd); state.logger->Debug("Closing NVDRV device ({})", fd);
try { try {
auto device = fdMap.at(fd); auto device = fdMap.at(fd);
if (!--device->refCount) if (!--device->refCount)
deviceMap.erase(device->deviceType); deviceMap.erase(device->deviceType);
fdMap.erase(fd); fdMap.erase(fd);
} catch (const std::out_of_range &) { } catch (const std::out_of_range &) {
state.logger->Warn("Trying to close non-existent FD"); state.logger->Warn("Trying to close non-existent FD");
} }
response.Push<u32>(constant::status::Success); response.Push<u32>(constant::status::Success);
} }
@ -111,6 +126,7 @@ namespace skyline::service::nvdrv {
auto eventId = request.Pop<u32>(); auto eventId = request.Pop<u32>();
auto event = std::make_shared<type::KEvent>(state); auto event = std::make_shared<type::KEvent>(state);
auto handle = state.process->InsertItem<type::KEvent>(event); auto handle = state.process->InsertItem<type::KEvent>(event);
state.logger->Debug("QueryEvent: FD: {}, Event ID: {}, Handle: {}", fd, eventId, handle); state.logger->Debug("QueryEvent: FD: {}, Event ID: {}, Handle: {}", fd, eventId, handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);
} }

View File

@ -25,6 +25,7 @@ namespace skyline::service::nvdrv::device {
void NvHostChannel::SetPriority(IoctlData &buffer) { void NvHostChannel::SetPriority(IoctlData &buffer) {
auto priority = state.process->GetObject<NvChannelPriority>(buffer.input[0].address); auto priority = state.process->GetObject<NvChannelPriority>(buffer.input[0].address);
switch (priority) { switch (priority) {
case NvChannelPriority::Low: case NvChannelPriority::Low:
timeslice = 1300; timeslice = 1300;

View File

@ -31,6 +31,7 @@ namespace skyline::service::nvdrv::device {
u32 subregionHeightAlignPixels{0x40}; u32 subregionHeightAlignPixels{0x40};
u32 subregionCount{0x10}; u32 subregionCount{0x10};
} zCullInfo; } zCullInfo;
state.process->WriteMemory(zCullInfo, buffer.output[0].address); state.process->WriteMemory(zCullInfo, buffer.output[0].address);
} }
@ -72,11 +73,13 @@ namespace skyline::service::nvdrv::device {
u64 chipName; // 0x6230326D67 ("gm20b") u64 chipName; // 0x6230326D67 ("gm20b")
u64 grCompbitStoreBaseHw; // 0x0 (not supported) u64 grCompbitStoreBaseHw; // 0x0 (not supported)
}; };
struct Data { struct Data {
u64 gpuCharacteristicsBufSize; // InOut u64 gpuCharacteristicsBufSize; // InOut
u64 gpuCharacteristicsBufAddr; // In u64 gpuCharacteristicsBufAddr; // In
GpuCharacteristics gpuCharacteristics; // Out GpuCharacteristics gpuCharacteristics; // Out
} data = state.process->GetObject<Data>(buffer.input[0].address); } data = state.process->GetObject<Data>(buffer.input[0].address);
data.gpuCharacteristics = { data.gpuCharacteristics = {
.arch = 0x120, .arch = 0x120,
.impl = 0xB, .impl = 0xB,
@ -113,7 +116,9 @@ namespace skyline::service::nvdrv::device {
.chipName = 0x6230326D67, .chipName = 0x6230326D67,
.grCompbitStoreBaseHw = 0x0 .grCompbitStoreBaseHw = 0x0
}; };
data.gpuCharacteristicsBufSize = 0xA0; data.gpuCharacteristicsBufSize = 0xA0;
state.process->WriteMemory(data, buffer.output[0].address); state.process->WriteMemory(data, buffer.output[0].address);
} }
@ -123,8 +128,10 @@ namespace skyline::service::nvdrv::device {
u32 reserved[3]; // In u32 reserved[3]; // In
u64 maskBuf; // Out u64 maskBuf; // Out
} data = state.process->GetObject<Data>(buffer.input[0].address); } data = state.process->GetObject<Data>(buffer.input[0].address);
if (data.maskBufSize) if (data.maskBufSize)
data.maskBuf = 0x3; data.maskBuf = 0x3;
state.process->WriteMemory(data, buffer.output[0].address); state.process->WriteMemory(data, buffer.output[0].address);
} }
@ -136,6 +143,7 @@ namespace skyline::service::nvdrv::device {
.slot = 0x07, .slot = 0x07,
.mask = 0x01 .mask = 0x01
}; };
state.process->WriteMemory(data, buffer.output[0].address); state.process->WriteMemory(data, buffer.output[0].address);
} }
} }

View File

@ -21,8 +21,10 @@ namespace skyline::service::nvdrv::device {
u32 size; // In u32 size; // In
u32 handle; // Out u32 handle; // Out
} data = state.process->GetObject<Data>(buffer.input[0].address); } data = state.process->GetObject<Data>(buffer.input[0].address);
handleTable[handleIndex] = std::make_shared<NvMapObject>(idIndex++, data.size); handleTable[handleIndex] = std::make_shared<NvMapObject>(idIndex++, data.size);
data.handle = handleIndex++; data.handle = handleIndex++;
state.process->WriteMemory(data, buffer.output[0].address); state.process->WriteMemory(data, buffer.output[0].address);
state.logger->Debug("Create: Input: Size: 0x{:X}, Output: Handle: 0x{:X}, Status: {}", data.size, data.handle, buffer.status); state.logger->Debug("Create: Input: Size: 0x{:X}, Output: Handle: 0x{:X}, Status: {}", data.size, data.handle, buffer.status);
} }
@ -32,6 +34,7 @@ namespace skyline::service::nvdrv::device {
u32 id; // In u32 id; // In
u32 handle; // Out u32 handle; // Out
} data = state.process->GetObject<Data>(buffer.input[0].address); } data = state.process->GetObject<Data>(buffer.input[0].address);
bool found{}; bool found{};
for (const auto &object : handleTable) { for (const auto &object : handleTable) {
if (object.second->id == data.id) { if (object.second->id == data.id) {
@ -40,10 +43,12 @@ namespace skyline::service::nvdrv::device {
break; break;
} }
} }
if (found) if (found)
state.process->WriteMemory(data, buffer.output[0].address); state.process->WriteMemory(data, buffer.output[0].address);
else else
buffer.status = NvStatus::BadValue; buffer.status = NvStatus::BadValue;
state.logger->Debug("FromId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status); state.logger->Debug("FromId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
} }
@ -57,6 +62,7 @@ namespace skyline::service::nvdrv::device {
u8 _pad0_[7]; u8 _pad0_[7];
u64 address; // InOut u64 address; // InOut
} data = state.process->GetObject<Data>(buffer.input[0].address); } data = state.process->GetObject<Data>(buffer.input[0].address);
auto &object = handleTable.at(data.handle); auto &object = handleTable.at(data.handle);
object->heapMask = data.heapMask; object->heapMask = data.heapMask;
object->flags = data.flags; object->flags = data.flags;
@ -64,6 +70,7 @@ namespace skyline::service::nvdrv::device {
object->kind = data.kind; object->kind = data.kind;
object->address = data.address; object->address = data.address;
object->status = NvMapObject::Status::Allocated; object->status = NvMapObject::Status::Allocated;
state.logger->Debug("Alloc: Input: Handle: 0x{:X}, HeapMask: 0x{:X}, Flags: {}, Align: 0x{:X}, Kind: {}, Address: 0x{:X}, Output: Status: {}", data.handle, data.heapMask, data.flags, data.align, data.kind, data.address, buffer.status); state.logger->Debug("Alloc: Input: Handle: 0x{:X}, HeapMask: 0x{:X}, Flags: {}, Align: 0x{:X}, Kind: {}, Address: 0x{:X}, Output: Status: {}", data.handle, data.heapMask, data.flags, data.align, data.kind, data.address, buffer.status);
} }
@ -75,6 +82,7 @@ namespace skyline::service::nvdrv::device {
u32 size; // Out u32 size; // Out
u64 flags; // Out u64 flags; // Out
} data = state.process->GetObject<Data>(buffer.input[0].address); } data = state.process->GetObject<Data>(buffer.input[0].address);
const auto &object = handleTable.at(data.handle); const auto &object = handleTable.at(data.handle);
if (object.use_count() > 1) { if (object.use_count() > 1) {
data.address = static_cast<u32>(object->address); data.address = static_cast<u32>(object->address);
@ -83,8 +91,10 @@ namespace skyline::service::nvdrv::device {
data.address = 0x0; data.address = 0x0;
data.flags = 0x1; // Not free yet data.flags = 0x1; // Not free yet
} }
data.size = object->size; data.size = object->size;
handleTable.erase(data.handle); handleTable.erase(data.handle);
state.process->WriteMemory(data, buffer.output[0].address); state.process->WriteMemory(data, buffer.output[0].address);
} }
@ -95,11 +105,13 @@ namespace skyline::service::nvdrv::device {
Parameter parameter; // In Parameter parameter; // In
u32 result; // Out u32 result; // Out
} data = state.process->GetObject<Data>(buffer.input[0].address); } data = state.process->GetObject<Data>(buffer.input[0].address);
auto &object = handleTable.at(data.handle); auto &object = handleTable.at(data.handle);
switch (data.parameter) { switch (data.parameter) {
case Parameter::Size: case Parameter::Size:
data.result = object->size; data.result = object->size;
break; break;
case Parameter::Alignment: case Parameter::Alignment:
case Parameter::HeapMask: case Parameter::HeapMask:
case Parameter::Kind: { case Parameter::Kind: {
@ -120,13 +132,16 @@ namespace skyline::service::nvdrv::device {
} }
break; break;
} }
case Parameter::Base: case Parameter::Base:
buffer.status = NvStatus::NotImplemented; buffer.status = NvStatus::NotImplemented;
break; break;
case Parameter::Compr: case Parameter::Compr:
buffer.status = NvStatus::NotImplemented; buffer.status = NvStatus::NotImplemented;
break; break;
} }
state.process->WriteMemory(data, buffer.output[0].address); state.process->WriteMemory(data, buffer.output[0].address);
state.logger->Debug("Param: Input: Handle: 0x{:X}, Parameter: {}, Output: Result: 0x{:X}, Status: {}", data.handle, data.parameter, data.result, buffer.status); state.logger->Debug("Param: Input: Handle: 0x{:X}, Parameter: {}, Output: Result: 0x{:X}, Status: {}", data.handle, data.parameter, data.result, buffer.status);
} }
@ -136,7 +151,9 @@ namespace skyline::service::nvdrv::device {
u32 id; // Out u32 id; // Out
u32 handle; // In u32 handle; // In
} data = state.process->GetObject<Data>(buffer.input[0].address); } data = state.process->GetObject<Data>(buffer.input[0].address);
data.id = handleTable.at(data.handle)->id; data.id = handleTable.at(data.handle)->id;
state.process->WriteMemory(data, buffer.output[0].address); state.process->WriteMemory(data, buffer.output[0].address);
state.logger->Debug("GetId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status); state.logger->Debug("GetId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
} }

View File

@ -20,7 +20,7 @@
namespace skyline::service { namespace skyline::service {
ServiceManager::ServiceManager(const DeviceState &state) : state(state) {} ServiceManager::ServiceManager(const DeviceState &state) : state(state) {}
std::shared_ptr<BaseService> ServiceManager::CreateService(const Service serviceType) { std::shared_ptr<BaseService> ServiceManager::CreateService(Service serviceType) {
auto serviceIter = serviceMap.find(serviceType); auto serviceIter = serviceMap.find(serviceType);
if (serviceIter != serviceMap.end()) if (serviceIter != serviceMap.end())
return (*serviceIter).second; return (*serviceIter).second;
@ -73,7 +73,7 @@ namespace skyline::service {
return serviceObj; return serviceObj;
} }
KHandle ServiceManager::NewSession(const Service serviceType) { KHandle ServiceManager::NewSession(Service serviceType) {
std::lock_guard serviceGuard(mutex); std::lock_guard serviceGuard(mutex);
return state.process->NewHandle<type::KSession>(CreateService(serviceType)).handle; return state.process->NewHandle<type::KSession>(CreateService(serviceType)).handle;
} }
@ -110,7 +110,7 @@ namespace skyline::service {
state.logger->Debug("Service has been registered: \"{}\" (0x{:X})", serviceObject->serviceName, handle); state.logger->Debug("Service has been registered: \"{}\" (0x{:X})", serviceObject->serviceName, handle);
} }
void ServiceManager::CloseSession(const KHandle handle) { void ServiceManager::CloseSession(KHandle handle) {
std::lock_guard serviceGuard(mutex); std::lock_guard serviceGuard(mutex);
auto session = state.process->GetHandle<type::KSession>(handle); auto session = state.process->GetHandle<type::KSession>(handle);
if (session->serviceStatus == type::KSession::ServiceStatus::Open) { if (session->serviceStatus == type::KSession::ServiceStatus::Open) {
@ -124,7 +124,7 @@ namespace skyline::service {
} }
}; };
void ServiceManager::SyncRequestHandler(const KHandle handle) { void ServiceManager::SyncRequestHandler(KHandle handle) {
auto session = state.process->GetHandle<type::KSession>(handle); auto session = state.process->GetHandle<type::KSession>(handle);
state.logger->Debug("----Start----"); state.logger->Debug("----Start----");
state.logger->Debug("Handle is 0x{:X}", handle); state.logger->Debug("Handle is 0x{:X}", handle);

View File

@ -22,7 +22,7 @@ namespace skyline::service {
* @param serviceType The type of service requested * @param serviceType The type of service requested
* @return A shared pointer to an instance of the service * @return A shared pointer to an instance of the service
*/ */
std::shared_ptr<BaseService> CreateService(const Service serviceType); std::shared_ptr<BaseService> CreateService(Service serviceType);
public: public:
/** /**
@ -35,7 +35,7 @@ namespace skyline::service {
* @param serviceType The type of the service * @param serviceType The type of the service
* @return Handle to KService object of the service * @return Handle to KService object of the service
*/ */
KHandle NewSession(const Service serviceType); KHandle NewSession(Service serviceType);
/** /**
* @brief Creates a new service using it's type enum and writes it's handle or virtual handle (If it's a domain request) to IpcResponse * @brief Creates a new service using it's type enum and writes it's handle or virtual handle (If it's a domain request) to IpcResponse
@ -61,7 +61,7 @@ namespace skyline::service {
* @note This only works for services created with `NewService` as sub-interfaces used with `RegisterService` can have multiple instances * @note This only works for services created with `NewService` as sub-interfaces used with `RegisterService` can have multiple instances
*/ */
template<typename Type> template<typename Type>
inline std::shared_ptr<Type> GetService(const Service serviceType) { inline std::shared_ptr<Type> GetService(Service serviceType) {
return std::static_pointer_cast<Type>(serviceMap.at(serviceType)); return std::static_pointer_cast<Type>(serviceMap.at(serviceType));
} }
@ -69,12 +69,12 @@ namespace skyline::service {
* @brief Closes an existing session to a service * @brief Closes an existing session to a service
* @param service The handle of the KService object * @param service The handle of the KService object
*/ */
void CloseSession(const KHandle handle); void CloseSession(KHandle handle);
/** /**
* @brief Handles a Synchronous IPC Request * @brief Handles a Synchronous IPC Request
* @param handle The handle of the object * @param handle The handle of the object
*/ */
void SyncRequestHandler(const KHandle handle); void SyncRequestHandler(KHandle handle);
}; };
} }

View File

@ -9,7 +9,7 @@ namespace skyline::service::settings {
{0x3, SFUNC(ISystemSettingsServer::GetFirmwareVersion)}}) {} {0x3, SFUNC(ISystemSettingsServer::GetFirmwareVersion)}}) {}
void ISystemSettingsServer::GetFirmwareVersion(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void ISystemSettingsServer::GetFirmwareVersion(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
SysVerTitle title{.minor=9, .major=0, .micro=0, .revMajor=4, .platform="NX", .verHash="4de65c071fd0869695b7629f75eb97b2551dbf2f", .dispVer="9.0.0", .dispTitle="NintendoSDK Firmware for NX 9.0.0-4.0"}; SysVerTitle title{.major=9, .minor=0, .micro=0, .revMajor=4, .revMinor=0, .platform="NX", .verHash="4de65c071fd0869695b7629f75eb97b2551dbf2f", .dispVer="9.0.0", .dispTitle="NintendoSDK Firmware for NX 9.0.0-4.0"};
state.process->WriteMemory(title, request.outputBuf.at(0).address); state.process->WriteMemory(title, request.outputBuf.at(0).address);
} }
} }

View File

@ -13,6 +13,7 @@ namespace skyline::service::sm {
void IUserInterface::GetService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IUserInterface::GetService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
std::string serviceName(reinterpret_cast<char *>(request.cmdArg)); std::string serviceName(reinterpret_cast<char *>(request.cmdArg));
if (serviceName.empty()) { if (serviceName.empty()) {
response.errorCode = constant::status::ServiceInvName; response.errorCode = constant::status::ServiceInvName;
} else { } else {

View File

@ -11,20 +11,22 @@ namespace skyline::service::timesrv {
void ITimeZoneService::ToCalendarTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void ITimeZoneService::ToCalendarTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
time_t curTime = std::time(nullptr); time_t curTime = std::time(nullptr);
tm calender = *std::gmtime(&curTime); tm calender = *std::gmtime(&curTime);
CalendarTime calendarTime{ CalendarTime calendarTime{
.year = static_cast<u16>(calender.tm_year), .year = static_cast<u16>(calender.tm_year),
.month = static_cast<u8>(calender.tm_mon), .month = static_cast<u8>(calender.tm_mon),
.day = static_cast<u8>(calender.tm_hour), .day = static_cast<u8>(calender.tm_hour),
.minute = static_cast<u8>(calender.tm_min), .minute = static_cast<u8>(calender.tm_min),
.second = static_cast<u8>(calender.tm_sec) .second = static_cast<u8>(calender.tm_sec),
}; };
response.Push(calendarTime); response.Push(calendarTime);
CalendarAdditionalInfo calendarInfo{ CalendarAdditionalInfo calendarInfo{
.dayWeek = static_cast<u32>(calender.tm_wday), .dayWeek = static_cast<u32>(calender.tm_wday),
.dayMonth = static_cast<u32>(calender.tm_mday), .dayMonth = static_cast<u32>(calender.tm_mday),
.name = *reinterpret_cast<const u64 *>(calender.tm_zone), .name = *reinterpret_cast<const u64 *>(calender.tm_zone),
.dst = static_cast<i32>(calender.tm_isdst), .dst = static_cast<i32>(calender.tm_isdst),
.utcRel = static_cast<u32>(calender.tm_gmtoff) .utcRel = static_cast<u32>(calender.tm_gmtoff),
}; };
response.Push(calendarInfo); response.Push(calendarInfo);
} }

View File

@ -44,6 +44,7 @@ namespace skyline::service::visrv {
std::string displayName(reinterpret_cast<char *>(request.cmdArg)); std::string displayName(reinterpret_cast<char *>(request.cmdArg));
state.logger->Debug("Setting display as: {}", displayName); state.logger->Debug("Setting display as: {}", displayName);
state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(Service::hosbinder_IHOSBinderDriver)->SetDisplay(displayName); state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(Service::hosbinder_IHOSBinderDriver)->SetDisplay(displayName);
response.Push<u64>(0); // There's only one display response.Push<u64>(0); // There's only one display
} }
@ -59,7 +60,9 @@ namespace skyline::service::visrv {
u64 userId; u64 userId;
} input = request.Pop<InputStruct>(); } input = request.Pop<InputStruct>();
state.logger->Debug("Opening Layer: Display Name: {}, Layer ID: {}, User ID: {}", input.displayName, input.layerId, input.userId); state.logger->Debug("Opening Layer: Display Name: {}, Layer ID: {}, User ID: {}", input.displayName, input.layerId, input.userId);
std::string name(input.displayName); std::string name(input.displayName);
Parcel parcel(state); Parcel parcel(state);
LayerParcel data{ LayerParcel data{
.type = 0x20, .type = 0x20,
@ -69,7 +72,8 @@ namespace skyline::service::visrv {
}; };
parcel.WriteData(data); parcel.WriteData(data);
parcel.objects.resize(4); parcel.objects.resize(4);
response.Push(parcel.WriteParcel(request.outputBuf.at(0)));
response.Push<u64>(parcel.WriteParcel(request.outputBuf.at(0)));
} }
void IApplicationDisplayService::CloseLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IApplicationDisplayService::CloseLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
@ -87,12 +91,14 @@ namespace skyline::service::visrv {
void IApplicationDisplayService::SetLayerScalingMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IApplicationDisplayService::SetLayerScalingMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto scalingMode = request.Pop<u64>(); auto scalingMode = request.Pop<u64>();
auto layerId = request.Pop<u64>(); auto layerId = request.Pop<u64>();
state.logger->Debug("Setting Layer Scaling mode to '{}' for layer {}", scalingMode, layerId); state.logger->Debug("Setting Layer Scaling mode to '{}' for layer {}", scalingMode, layerId);
} }
void IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
KHandle handle = state.process->InsertItem(state.gpu->vsyncEvent); KHandle handle = state.process->InsertItem(state.gpu->vsyncEvent);
state.logger->Debug("VSync Event Handle: 0x{:X}", handle); state.logger->Debug("VSync Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);
} }
} }

View File

@ -29,6 +29,7 @@ namespace skyline::service::visrv {
.string = "dispdrv" .string = "dispdrv"
}; };
parcel.WriteData(data); parcel.WriteData(data);
response.Push<u64>(parcel.WriteParcel(request.outputBuf.at(0))); response.Push<u64>(parcel.WriteParcel(request.outputBuf.at(0)));
} }

View File

@ -18,19 +18,23 @@ namespace skyline::service::visrv {
request.Skip<u32>(); request.Skip<u32>();
auto displayId = request.Pop<u64>(); auto displayId = request.Pop<u64>();
state.logger->Debug("Creating Managed Layer on Display: {}", displayId); state.logger->Debug("Creating Managed Layer on Display: {}", displayId);
auto hosBinder = state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(Service::hosbinder_IHOSBinderDriver); auto hosBinder = state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(Service::hosbinder_IHOSBinderDriver);
if (hosBinder->layerStatus != hosbinder::LayerStatus::Uninitialized) if (hosBinder->layerStatus != hosbinder::LayerStatus::Uninitialized)
throw exception("The application is creating more than one layer"); throw exception("The application is creating more than one layer");
hosBinder->layerStatus = hosbinder::LayerStatus::Managed; hosBinder->layerStatus = hosbinder::LayerStatus::Managed;
response.Push<u64>(0); // There's only one layer response.Push<u64>(0); // There's only one layer
} }
void IManagerDisplayService::DestroyManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IManagerDisplayService::DestroyManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto layerId = request.Pop<u64>(); auto layerId = request.Pop<u64>();
state.logger->Debug("Destroying Managed Layer: {}", layerId); state.logger->Debug("Destroying Managed Layer: {}", layerId);
auto hosBinder = state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(Service::hosbinder_IHOSBinderDriver); auto hosBinder = state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(Service::hosbinder_IHOSBinderDriver);
if (hosBinder->layerStatus == hosbinder::LayerStatus::Uninitialized) if (hosBinder->layerStatus == hosbinder::LayerStatus::Uninitialized)
state.logger->Warn("The application is destroying an uninitialized layer"); state.logger->Warn("The application is destroying an uninitialized layer");
hosBinder->layerStatus = hosbinder::LayerStatus::Uninitialized; hosBinder->layerStatus = hosbinder::LayerStatus::Uninitialized;
} }

View File

@ -5,6 +5,7 @@
package emu.skyline package emu.skyline
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
@ -114,6 +115,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
/** /**
* This makes the window fullscreen then sets up [preferenceFd] and [logFd], sets up the performance statistics and finally calls [executeApplication] for executing the application * This makes the window fullscreen then sets up [preferenceFd] and [logFd], sets up the performance statistics and finally calls [executeApplication] for executing the application
*/ */
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)