diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 410dc16c..2cb7b162 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -82,8 +82,8 @@
-
-
+
+
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 00000000..61a9130c
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/discord.xml b/.idea/discord.xml
index 78159505..0bf47d29 100644
--- a/.idea/discord.xml
+++ b/.idea/discord.xml
@@ -2,6 +2,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index dde74879..98e42156 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -175,6 +175,7 @@
+
@@ -226,6 +227,7 @@
+
@@ -257,6 +259,8 @@
+
+
@@ -280,6 +284,7 @@
+
@@ -407,11 +412,13 @@
+
+
@@ -561,6 +568,7 @@
+
@@ -672,7 +680,7 @@
-
+
@@ -825,6 +833,7 @@
+
@@ -931,10 +940,12 @@
+
+
@@ -1283,7 +1294,9 @@
+
+
@@ -1324,6 +1337,7 @@
+
@@ -1360,9 +1374,11 @@
+
+
@@ -1639,6 +1655,7 @@
+
@@ -1654,11 +1671,14 @@
+
+
+
@@ -1694,6 +1714,7 @@
+
@@ -1782,6 +1803,7 @@
+
@@ -1799,6 +1821,7 @@
+
@@ -1807,6 +1830,10 @@
+
+
+
+
@@ -1958,6 +1985,7 @@
+
@@ -1966,6 +1994,7 @@
+
@@ -1978,6 +2007,8 @@
+
+
@@ -1986,6 +2017,7 @@
+
@@ -2071,8 +2103,10 @@
+
+
@@ -2130,6 +2164,7 @@
+
@@ -2234,6 +2269,7 @@
+
@@ -2342,6 +2378,7 @@
+
@@ -2449,6 +2486,7 @@
+
@@ -2462,23 +2500,31 @@
+
+
+
+
+
+
-
+
+
+
@@ -2527,6 +2573,7 @@
+
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 00000000..478e4948
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 12f38e22..3ce2f5e6 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -43,7 +43,7 @@
-
+
diff --git a/.idea/runConfigurations/Controller_Configuration.xml b/.idea/runConfigurations/Controller_Configuration.xml
index 2770de74..6bd03f50 100644
--- a/.idea/runConfigurations/Controller_Configuration.xml
+++ b/.idea/runConfigurations/Controller_Configuration.xml
@@ -1,11 +1,12 @@
-
+
+
@@ -41,11 +42,16 @@
+
+
+
+
+
diff --git a/.idea/runConfigurations/Setting.xml b/.idea/runConfigurations/Setting.xml
index 8d90e67a..aa8992a6 100644
--- a/.idea/runConfigurations/Setting.xml
+++ b/.idea/runConfigurations/Setting.xml
@@ -1,11 +1,12 @@
-
+
+
@@ -41,11 +42,16 @@
+
+
+
+
+
diff --git a/.idea/scopes/SkylineNative.xml b/.idea/scopes/SkylineNative.xml
index 9c119923..7d8f277b 100644
--- a/.idea/scopes/SkylineNative.xml
+++ b/.idea/scopes/SkylineNative.xml
@@ -1,3 +1,3 @@
-
+
\ No newline at end of file
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index ffd9558a..1a233c5d 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -34,6 +34,8 @@ add_library(skyline SHARED
${source_DIR}/emu_jni.cpp
${source_DIR}/loader_jni.cpp
${source_DIR}/skyline/common.cpp
+ ${source_DIR}/skyline/common/settings.cpp
+ ${source_DIR}/skyline/common/signal.cpp
${source_DIR}/skyline/nce/guest.S
${source_DIR}/skyline/nce.cpp
${source_DIR}/skyline/jvm.cpp
diff --git a/app/src/main/cpp/emu_jni.cpp b/app/src/main/cpp/emu_jni.cpp
index 754d713d..3ddf330e 100644
--- a/app/src/main/cpp/emu_jni.cpp
+++ b/app/src/main/cpp/emu_jni.cpp
@@ -7,22 +7,25 @@
#include
#include "skyline/loader/loader.h"
#include "skyline/common.h"
+#include "skyline/common/settings.h"
#include "skyline/os.h"
#include "skyline/jvm.h"
#include "skyline/gpu.h"
#include "skyline/input.h"
+#include "skyline/kernel/types/KProcess.h"
-skyline::u16 fps;
-skyline::u32 frametime;
-std::weak_ptr gpuWeak;
-std::weak_ptr inputWeak;
+skyline::u16 Fps;
+skyline::u32 FrameTime;
+std::weak_ptr OsWeak;
+std::weak_ptr GpuWeak;
+std::weak_ptr InputWeak;
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring) {
- fps = 0;
- frametime = 0;
+ Fps = 0;
+ FrameTime = 0;
pthread_setname_np(pthread_self(), "EmuMain");
- setpriority(PRIO_PROCESS, static_cast(gettid()), -8); // Set the priority of this process to the highest value
+ setpriority(PRIO_PGRP, static_cast(gettid()), -8); // Set the priority of this process to the highest value
auto jvmManager{std::make_shared(env, instance)};
auto settings{std::make_shared(preferenceFd)};
@@ -35,9 +38,10 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
auto start{std::chrono::steady_clock::now()};
try {
- skyline::kernel::OS os(jvmManager, logger, settings, std::string(appFilesPath));
- gpuWeak = os.state.gpu;
- inputWeak = os.state.input;
+ auto os{std::make_shared(jvmManager, logger, settings, std::string(appFilesPath))};
+ OsWeak = os;
+ GpuWeak = os->state.gpu;
+ InputWeak = os->state.input;
jvmManager->InitializeControllers();
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
@@ -45,14 +49,14 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
logger->Info("Launching ROM {}", romUri);
env->ReleaseStringUTFChars(romUriJstring, romUri);
- os.Execute(romFd, static_cast(romType));
+ os->Execute(romFd, static_cast(romType));
} catch (std::exception &e) {
logger->Error(e.what());
} catch (...) {
logger->Error("An unknown exception has occurred");
}
- inputWeak.reset();
+ InputWeak.reset();
logger->Info("Emulation has ended");
@@ -62,38 +66,50 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
close(romFd);
}
-extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_exitGuest(JNIEnv *, jobject, jboolean halt) {
- // TODO
- exit(0);
+extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_stopEmulation(JNIEnv *, jobject) {
+ auto os{OsWeak.lock()};
+ while (!os)
+ os = OsWeak.lock();
+ auto process{os->state.process};
+ while (!process) {
+ process = os->state.process;
+ __sync_synchronize();
+ }
+ while (!process->mainThread)
+ __sync_synchronize();
+ auto thread{process->mainThread}; // We just need to kill the main thread, it'll kill the rest itself
+ while (!thread->running)
+ __sync_synchronize();
+ thread->Kill(true);
}
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setSurface(JNIEnv *, jobject, jobject surface) {
- auto gpu{gpuWeak.lock()};
+ auto gpu{GpuWeak.lock()};
while (!gpu)
- gpu = gpuWeak.lock();
+ gpu = GpuWeak.lock();
gpu->presentation.UpdateSurface(surface);
}
extern "C" JNIEXPORT jint Java_emu_skyline_EmulationActivity_getFps(JNIEnv *, jobject) {
- return fps;
+ return Fps;
}
extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIEnv *, jobject) {
- return static_cast(frametime) / 100;
+ return static_cast(FrameTime) / 100;
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setController(JNIEnv *, jobject, jint index, jint type, jint partnerIndex) {
- auto input{inputWeak.lock()};
+ auto input{InputWeak.lock()};
std::lock_guard guard(input->npad.mutex);
input->npad.controllers[index] = skyline::input::GuestController{static_cast(type), static_cast(partnerIndex)};
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_updateControllers(JNIEnv *, jobject) {
- inputWeak.lock()->npad.Update();
+ InputWeak.lock()->npad.Update();
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jint index, jlong mask, jboolean pressed) {
- auto input{inputWeak.lock()};
+ auto input{InputWeak.lock()};
if (!input)
return; // We don't mind if we miss button updates while input hasn't been initialized
auto device{input->npad.controllers[index].device};
@@ -102,7 +118,7 @@ extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonSt
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValue(JNIEnv *, jobject, jint index, jint axis, jint value) {
- auto input{inputWeak.lock()};
+ auto input{InputWeak.lock()};
if (!input)
return; // We don't mind if we miss axis updates while input hasn't been initialized
auto device{input->npad.controllers[index].device};
@@ -113,7 +129,7 @@ extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValu
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setTouchState(JNIEnv *env, jobject, jintArray pointsJni) {
using Point = skyline::input::TouchScreenPoint;
- auto input{inputWeak.lock()};
+ auto input{InputWeak.lock()};
if (!input)
return; // We don't mind if we miss touch updates while input hasn't been initialized
jboolean isCopy{false};
diff --git a/app/src/main/cpp/skyline/audio.cpp b/app/src/main/cpp/skyline/audio.cpp
index f28eb4bb..254c2ac9 100644
--- a/app/src/main/cpp/skyline/audio.cpp
+++ b/app/src/main/cpp/skyline/audio.cpp
@@ -16,6 +16,10 @@ namespace skyline::audio {
outputStream->requestStart();
}
+ Audio::~Audio() {
+ outputStream->requestStop();
+ }
+
std::shared_ptr Audio::OpenTrack(u8 channelCount, u32 sampleRate, const std::function &releaseCallback) {
std::lock_guard trackGuard(trackLock);
diff --git a/app/src/main/cpp/skyline/audio.h b/app/src/main/cpp/skyline/audio.h
index d5209aa0..b0ba80d6 100644
--- a/app/src/main/cpp/skyline/audio.h
+++ b/app/src/main/cpp/skyline/audio.h
@@ -19,6 +19,8 @@ namespace skyline::audio {
public:
Audio(const DeviceState &state);
+ ~Audio();
+
/**
* @brief Opens a new track that can be used to play sound
* @param channelCount The amount channels that are present in the track
diff --git a/app/src/main/cpp/skyline/audio/common.h b/app/src/main/cpp/skyline/audio/common.h
index 6cb9a6bc..996e84f2 100644
--- a/app/src/main/cpp/skyline/audio/common.h
+++ b/app/src/main/cpp/skyline/audio/common.h
@@ -4,7 +4,7 @@
#pragma once
#include
-#include "circular_buffer.h"
+#include
namespace skyline {
namespace constant {
diff --git a/app/src/main/cpp/skyline/audio/track.h b/app/src/main/cpp/skyline/audio/track.h
index ce63dde0..acf7b742 100644
--- a/app/src/main/cpp/skyline/audio/track.h
+++ b/app/src/main/cpp/skyline/audio/track.h
@@ -4,6 +4,7 @@
#pragma once
#include
+#include
#include "common.h"
namespace skyline::audio {
diff --git a/app/src/main/cpp/skyline/common.cpp b/app/src/main/cpp/skyline/common.cpp
index d3731da2..e1b41d39 100644
--- a/app/src/main/cpp/skyline/common.cpp
+++ b/app/src/main/cpp/skyline/common.cpp
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
-#include
#include
#include "common.h"
#include "nce.h"
@@ -11,63 +10,6 @@
#include "kernel/types/KThread.h"
namespace skyline {
- Settings::Settings(int fd) {
- tinyxml2::XMLDocument pref;
-
- auto fileDeleter = [](FILE *file) { fclose(file); };
- std::unique_ptr file{fdopen(fd, "r"), fileDeleter};
- if (pref.LoadFile(file.get()))
- throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
-
- tinyxml2::XMLElement *elem{pref.LastChild()->FirstChild()->ToElement()};
- while (elem) {
- switch (elem->Value()[0]) {
- case 's':
- stringMap[elem->FindAttribute("name")->Value()] = elem->GetText();
- break;
-
- case 'b':
- boolMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->BoolValue();
- break;
-
- case 'i':
- intMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->IntValue();
- break;
-
- default:
- __android_log_print(ANDROID_LOG_WARN, "emu-cpp", "Settings type is missing: %s for %s", elem->Value(), elem->FindAttribute("name")->Value());
- break;
- };
-
- if (elem->NextSibling())
- elem = elem->NextSibling()->ToElement();
- else
- break;
- }
-
- pref.Clear();
- }
-
- std::string Settings::GetString(const std::string &key) {
- return stringMap.at(key);
- }
-
- bool Settings::GetBool(const std::string &key) {
- return boolMap.at(key);
- }
-
- int Settings::GetInt(const std::string &key) {
- return intMap.at(key);
- }
-
- void Settings::List(const std::shared_ptr &logger) {
- for (auto &iter : stringMap)
- logger->Info("Key: {}, Value: {}, Type: String", iter.first, GetString(iter.first));
-
- for (auto &iter : boolMap)
- logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first));
- }
-
Logger::Logger(const std::string &path, LogLevel configLevel) : configLevel(configLevel) {
logFile.open(path, std::ios::trunc);
UpdateTag();
@@ -95,6 +37,7 @@ namespace skyline {
std::lock_guard guard(mtx);
logFile << "0|" << str << "\n";
+ logFile.flush();
}
void Logger::Write(LogLevel level, std::string str) {
@@ -112,10 +55,11 @@ namespace skyline {
std::lock_guard guard(mtx);
logFile << "1|" << levelCharacter[static_cast(level)] << '|' << threadName << '|' << str << '\n';
+ logFile.flush();
}
- DeviceState::DeviceState(kernel::OS *os, std::shared_ptr &process, std::shared_ptr jvmManager, std::shared_ptr settings, std::shared_ptr logger)
- : os(os), jvm(std::move(jvmManager)), settings(std::move(settings)), logger(std::move(logger)), process(process) {
+ DeviceState::DeviceState(kernel::OS *os, std::shared_ptr jvmManager, std::shared_ptr settings, std::shared_ptr logger)
+ : os(os), jvm(std::move(jvmManager)), settings(std::move(settings)), logger(std::move(logger)) {
// We assign these later as they use the state in their constructor and we don't want null pointers
nce = std::make_shared(*this);
gpu = std::make_shared(*this);
diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h
index fb9d31f0..5f7a0a04 100644
--- a/app/src/main/cpp/skyline/common.h
+++ b/app/src/main/cpp/skyline/common.h
@@ -141,7 +141,7 @@ namespace skyline {
template
size_t PointerValue(T *item) {
- return reinterpret_cast(item);
+ return reinterpret_cast(item);
}
/**
@@ -440,48 +440,7 @@ namespace skyline {
}
};
- /**
- * @brief The Settings class is used to access the parameters set in the Java component of the application
- */
- class Settings {
- private:
- std::unordered_map stringMap; //!< A mapping from all keys to their corresponding string value
- std::unordered_map boolMap; //!< A mapping from all keys to their corresponding boolean value
- std::unordered_map intMap; //!< A mapping from all keys to their corresponding integer value
-
- public:
- /**
- * @param fd An FD to the preference XML file
- */
- Settings(int fd);
-
- /**
- * @brief Retrieves a particular setting as a string
- * @param key The key of the setting
- * @return The string value of the setting
- */
- std::string GetString(const std::string &key);
-
- /**
- * @brief Retrieves a particular setting as a boolean
- * @param key The key of the setting
- * @return The boolean value of the setting
- */
- bool GetBool(const std::string &key);
-
- /**
- * @brief Retrieves a particular setting as a integer
- * @param key The key of the setting
- * @return The integer value of the setting
- */
- int GetInt(const std::string &key);
-
- /**
- * @brief Writes all settings keys and values to syslog, this function is for development purposes
- */
- void List(const std::shared_ptr &logger);
- };
-
+ class Settings;
namespace nce {
class NCE;
struct ThreadContext;
@@ -511,19 +470,19 @@ namespace skyline {
* @brief The state of the entire emulator is contained within this class, all objects related to emulation are tied into it
*/
struct DeviceState {
- DeviceState(kernel::OS *os, std::shared_ptr &process, std::shared_ptr jvmManager, std::shared_ptr settings, std::shared_ptr logger);
+ DeviceState(kernel::OS *os, std::shared_ptr jvmManager, std::shared_ptr settings, std::shared_ptr logger);
kernel::OS *os;
- std::shared_ptr &process;
- thread_local static std::shared_ptr thread; //!< The KThread of the thread which accesses this object
- thread_local static nce::ThreadContext *ctx; //!< The context of the guest thread for the corresponding host thread
- std::shared_ptr nce;
- std::shared_ptr gpu;
- std::shared_ptr audio;
- std::shared_ptr input;
- std::shared_ptr loader;
std::shared_ptr jvm;
std::shared_ptr settings;
std::shared_ptr logger;
+ std::shared_ptr loader;
+ std::shared_ptr gpu;
+ std::shared_ptr audio;
+ std::shared_ptr input;
+ std::shared_ptr nce;
+ std::shared_ptr process;
+ thread_local static std::shared_ptr thread; //!< The KThread of the thread which accesses this object
+ thread_local static nce::ThreadContext *ctx; //!< The context of the guest thread for the corresponding host thread
};
}
diff --git a/app/src/main/cpp/skyline/audio/circular_buffer.h b/app/src/main/cpp/skyline/common/circular_buffer.h
similarity index 99%
rename from app/src/main/cpp/skyline/audio/circular_buffer.h
rename to app/src/main/cpp/skyline/common/circular_buffer.h
index c6344d63..3ef8747b 100644
--- a/app/src/main/cpp/skyline/audio/circular_buffer.h
+++ b/app/src/main/cpp/skyline/common/circular_buffer.h
@@ -5,7 +5,7 @@
#include
-namespace skyline::audio {
+namespace skyline {
/**
* @brief An abstraction of an array into a circular buffer
* @tparam Type The type of elements stored in the buffer
diff --git a/app/src/main/cpp/skyline/gpu/circular_queue.h b/app/src/main/cpp/skyline/common/circular_queue.h
similarity index 97%
rename from app/src/main/cpp/skyline/gpu/circular_queue.h
rename to app/src/main/cpp/skyline/common/circular_queue.h
index ec0ea971..bf37b644 100644
--- a/app/src/main/cpp/skyline/gpu/circular_queue.h
+++ b/app/src/main/cpp/skyline/common/circular_queue.h
@@ -5,9 +5,9 @@
#include
-namespace skyline::gpu {
+namespace skyline {
/**
- * @brief An efficient consumer-producer structure with internal synchronization
+ * @brief An efficient consumer-producer oriented queue with internal synchronization
*/
template
class CircularQueue {
diff --git a/app/src/main/cpp/skyline/common/settings.cpp b/app/src/main/cpp/skyline/common/settings.cpp
new file mode 100644
index 00000000..8c01eef9
--- /dev/null
+++ b/app/src/main/cpp/skyline/common/settings.cpp
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
+
+#include
+#include
+#include "settings.h"
+
+namespace skyline {
+ Settings::Settings(int fd) {
+ tinyxml2::XMLDocument pref;
+
+ auto fileDeleter = [](FILE *file) { fclose(file); };
+ std::unique_ptr file{fdopen(fd, "r"), fileDeleter};
+ if (pref.LoadFile(file.get()))
+ throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
+
+ tinyxml2::XMLElement *elem{pref.LastChild()->FirstChild()->ToElement()};
+ while (elem) {
+ switch (elem->Value()[0]) {
+ case 's':
+ stringMap[elem->FindAttribute("name")->Value()] = elem->GetText();
+ break;
+
+ case 'b':
+ boolMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->BoolValue();
+ break;
+
+ case 'i':
+ intMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->IntValue();
+ break;
+
+ default:
+ __android_log_print(ANDROID_LOG_WARN, "emu-cpp", "Settings type is missing: %s for %s", elem->Value(), elem->FindAttribute("name")->Value());
+ break;
+ };
+
+ if (elem->NextSibling())
+ elem = elem->NextSibling()->ToElement();
+ else
+ break;
+ }
+
+ pref.Clear();
+ }
+
+ std::string Settings::GetString(const std::string &key) {
+ return stringMap.at(key);
+ }
+
+ bool Settings::GetBool(const std::string &key) {
+ return boolMap.at(key);
+ }
+
+ int Settings::GetInt(const std::string &key) {
+ return intMap.at(key);
+ }
+
+ void Settings::List(const std::shared_ptr &logger) {
+ for (auto &iter : stringMap)
+ logger->Info("{} = \"{}\"", iter.first, GetString(iter.first));
+
+ for (auto &iter : boolMap)
+ logger->Info("{} = {}", iter.first, GetBool(iter.first));
+ }
+}
diff --git a/app/src/main/cpp/skyline/common/settings.h b/app/src/main/cpp/skyline/common/settings.h
new file mode 100644
index 00000000..8ae006b1
--- /dev/null
+++ b/app/src/main/cpp/skyline/common/settings.h
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
+
+#pragma once
+
+#include
+
+namespace skyline {
+ /**
+ * @brief The Settings class is used to access preferences set in the Kotlin component of Skyline
+ */
+ class Settings {
+ private:
+ std::unordered_map stringMap; //!< A mapping from all keys to their corresponding string value
+ std::unordered_map boolMap; //!< A mapping from all keys to their corresponding boolean value
+ std::unordered_map intMap; //!< A mapping from all keys to their corresponding integer value
+
+ public:
+ /**
+ * @param fd An FD to the preference XML file
+ */
+ Settings(int fd);
+
+ /**
+ * @brief Retrieves a particular setting as a string
+ * @param key The key of the setting
+ * @return The string value of the setting
+ */
+ std::string GetString(const std::string &key);
+
+ /**
+ * @brief Retrieves a particular setting as a boolean
+ * @param key The key of the setting
+ * @return The boolean value of the setting
+ */
+ bool GetBool(const std::string &key);
+
+ /**
+ * @brief Retrieves a particular setting as a integer
+ * @param key The key of the setting
+ * @return The integer value of the setting
+ */
+ int GetInt(const std::string &key);
+
+ /**
+ * @brief Writes all settings keys and values to syslog, this function is for development purposes
+ */
+ void List(const std::shared_ptr &logger);
+ };
+}
diff --git a/app/src/main/cpp/skyline/common/signal.cpp b/app/src/main/cpp/skyline/common/signal.cpp
new file mode 100644
index 00000000..52cd49a2
--- /dev/null
+++ b/app/src/main/cpp/skyline/common/signal.cpp
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
+
+#include
+#include
+#include
+#include
+#include "signal.h"
+
+namespace skyline::signal {
+ thread_local SignalException signalException;
+
+ void ExceptionThrow() {
+ throw signalException;
+ }
+
+ void ExceptionalSignalHandler(int signal, siginfo *info, ucontext *context) {
+ signalException.signal = signal;
+ signalException.pc = context->uc_mcontext.pc;
+ if (signal == SIGSEGV)
+ signalException.faultAddress = info->si_addr;
+ context->uc_mcontext.pc = reinterpret_cast(&ExceptionThrow);
+ }
+
+ template
+ Signature GetLibcFunction(const char *symbol) {
+ void *libc{dlopen("libc.so", RTLD_LOCAL | RTLD_LAZY)};
+ if (!libc)
+ throw exception("dlopen-ing libc has failed with: {}", dlerror());
+ auto function{reinterpret_cast(dlsym(libc, symbol))};
+ if (!function)
+ throw exception("Cannot find '{}' in libc: {}", symbol, dlerror());
+ return function;
+ }
+
+ void Sigaction(int signal, const struct sigaction *action, struct sigaction *oldAction) {
+ static decltype(&sigaction) real{};
+ if (!real)
+ real = GetLibcFunction("sigaction");
+ if (real(signal, action, oldAction) < 0)
+ throw exception("sigaction has failed with {}", strerror(errno));
+ }
+
+ static void *(*TlsRestorer)(){};
+
+ void SetTlsRestorer(void *(*function)()) {
+ TlsRestorer = function;
+ }
+
+ std::array DefaultSignalHandlers;
+
+ struct ThreadSignalHandler {
+ pthread_key_t key;
+ std::atomic count;
+
+ void Decrement();
+
+ static void DecrementStatic(ThreadSignalHandler *thiz) {
+ thiz->Decrement();
+ }
+ };
+
+ std::array ThreadSignalHandlers;
+
+ void ThreadSignalHandler::Decrement() {
+ u32 current;
+ while ((current = count.load()) && !count.compare_exchange_strong(current, --current));
+ if (current == 0) {
+ int signal{static_cast(this - ThreadSignalHandlers.data())};
+
+ struct sigaction oldAction;
+ Sigaction(signal, nullptr, &oldAction);
+
+ struct sigaction action{
+ .sa_sigaction = DefaultSignalHandlers.at(signal),
+ .sa_flags = oldAction.sa_flags,
+ };
+ Sigaction(signal, &action);
+ }
+ }
+
+ void ThreadSignalHandler(int signal, siginfo *info, ucontext *context) {
+ void *tls{}; // The TLS value prior to being restored if it is
+ if (TlsRestorer)
+ tls = TlsRestorer();
+
+ auto handler{reinterpret_cast(pthread_getspecific(ThreadSignalHandlers.at(signal).key))};
+ if (handler) {
+ handler(signal, info, context, tls);
+ } else {
+ auto defaultHandler{DefaultSignalHandlers.at(signal)};
+ if (defaultHandler)
+ defaultHandler(signal, info, context);
+ }
+
+ if (tls)
+ asm volatile("MSR TPIDR_EL0, %x0"::"r"(tls));
+ }
+
+ void SetSignalHandler(std::initializer_list signals, void (*function)(int, struct siginfo *, ucontext *, void *)) {
+ static std::array signalHandlerOnce{};
+
+ stack_t stack;
+ sigaltstack(nullptr, &stack);
+ struct sigaction action{
+ .sa_sigaction = reinterpret_cast(ThreadSignalHandler),
+ .sa_flags = SA_RESTART | SA_SIGINFO | (stack.ss_sp && stack.ss_size ? SA_ONSTACK : 0),
+ };
+
+ for (int signal : signals) {
+ auto &threadHandler{ThreadSignalHandlers.at(signal)};
+ std::call_once(signalHandlerOnce[signal], [signal, action, &threadHandler]() {
+ if (int result = pthread_key_create(&threadHandler.key, reinterpret_cast(&ThreadSignalHandler::DecrementStatic)))
+ throw exception("Failed to create per-thread signal handler pthread key: {}", strerror(result));
+
+ struct sigaction oldAction;
+ Sigaction(signal, &action, &oldAction);
+ if (oldAction.sa_flags && oldAction.sa_flags != action.sa_flags)
+ throw exception("Old sigaction flags aren't equivalent to the replaced signal: {:#b} | {:#b}", oldAction.sa_flags, action.sa_flags);
+
+ DefaultSignalHandlers.at(signal) = oldAction.sa_sigaction;
+ });
+ if (!pthread_getspecific(ThreadSignalHandlers.at(signal).key))
+ threadHandler.count++;
+ pthread_setspecific(ThreadSignalHandlers.at(signal).key, reinterpret_cast(function));
+ }
+ }
+
+ void Sigprocmask(int how, const sigset_t &set, sigset_t *oldSet) {
+ static decltype(&pthread_sigmask) real{};
+ if (!real)
+ real = GetLibcFunction("sigprocmask");
+ if (real(how, &set, oldSet) < 0)
+ throw exception("sigprocmask has failed with {}", strerror(errno));
+ }
+}
diff --git a/app/src/main/cpp/skyline/common/signal.h b/app/src/main/cpp/skyline/common/signal.h
new file mode 100644
index 00000000..9a1281c5
--- /dev/null
+++ b/app/src/main/cpp/skyline/common/signal.h
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
+
+#pragma once
+
+#include
+
+namespace skyline::signal {
+ /**
+ * @brief An exception object that is designed specifically to hold Linux signals
+ * @note This doesn't inherit std::exception as it shouldn't be caught as such
+ * @note Refer to the manpage siginfo(3) for information on members
+ */
+ class SignalException {
+ public:
+ int signal{};
+ u64 pc{};
+ void *faultAddress{};
+
+ inline std::string what() const {
+ if (!faultAddress)
+ return fmt::format("Signal: {} (PC: 0x{:X})", strsignal(signal), pc);
+ else
+ return fmt::format("Signal: {} @ 0x{:X} (PC: 0x{:X})", strsignal(signal), reinterpret_cast(faultAddress), pc);
+ }
+ };
+
+ /**
+ * @brief A signal handler which automatically throws an exception with the corresponding signal metadata in a SignalException
+ */
+ void ExceptionalSignalHandler(int signal, siginfo *, ucontext *context);
+
+ /**
+ * @brief Our delegator for sigaction, we need to do this due to sigchain hooking bionic's sigaction and it intercepting signals before they're passed onto userspace
+ * This not only leads to performance degradation but also requires host TLS to be in the TLS register which we cannot ensure for in-guest signals
+ */
+ void Sigaction(int signal, const struct sigaction *action, struct sigaction *oldAction = nullptr);
+
+ /**
+ * @brief If the TLS value of the code running prior to a signal has a custom TLS value, this should be used to restore it
+ * @param function A function which is inert if the TLS isn't required to be restored, it should return nullptr if TLS wasn't restored else the old TLS value
+ */
+ void SetTlsRestorer(void *(*function)());
+
+ /**
+ * @brief A wrapper around Sigaction to make it easy to set a sigaction signal handler for multiple signals and also allow for thread-local signal handlers
+ * @param function A sa_action callback with the old TLS (If present) as the 4th argument
+ */
+ void SetSignalHandler(std::initializer_list signals, void (*function)(int, struct siginfo *, ucontext *, void *));
+
+ inline void SetSignalHandler(std::initializer_list signals, void (*function)(int, struct siginfo *, ucontext *)) {
+ SetSignalHandler(signals, reinterpret_cast(function));
+ }
+
+ /**
+ * @brief Our delegator for sigprocmask, required due to libsigchain hooking this
+ */
+ void Sigprocmask(int how, const sigset_t &set, sigset_t *oldSet = nullptr);
+
+ inline void BlockSignal(std::initializer_list signals) {
+ sigset_t set{};
+ for (int signal : signals)
+ sigaddset(&set, signal);
+ Sigprocmask(SIG_BLOCK, set, nullptr);
+ }
+}
diff --git a/app/src/main/cpp/skyline/gpu.h b/app/src/main/cpp/skyline/gpu.h
index e1e4bf1a..c5036525 100644
--- a/app/src/main/cpp/skyline/gpu.h
+++ b/app/src/main/cpp/skyline/gpu.h
@@ -24,8 +24,8 @@ namespace skyline::gpu {
std::shared_ptr maxwellCompute;
std::shared_ptr maxwellDma;
std::shared_ptr keplerMemory;
- gpfifo::GPFIFO gpfifo;
std::array syncpoints{};
+ gpfifo::GPFIFO gpfifo;
inline GPU(const DeviceState &state) : state(state), presentation(state), memoryManager(state), gpfifo(state), fermi2D(std::make_shared(state)), keplerMemory(std::make_shared(state)), maxwell3D(std::make_shared(state)), maxwellCompute(std::make_shared(state)), maxwellDma(std::make_shared(state)) {}
};
diff --git a/app/src/main/cpp/skyline/gpu/gpfifo.cpp b/app/src/main/cpp/skyline/gpu/gpfifo.cpp
index e724929e..66aff668 100644
--- a/app/src/main/cpp/skyline/gpu/gpfifo.cpp
+++ b/app/src/main/cpp/skyline/gpu/gpfifo.cpp
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
+#include
+#include
#include
#include
#include "gpfifo.h"
@@ -86,19 +88,38 @@ namespace skyline::gpu::gpfifo {
void GPFIFO::Run() {
pthread_setname_np(pthread_self(), "GPFIFO");
- pushBuffers->Process([this](PushBuffer& pushBuffer){
- if (pushBuffer.segment.empty())
- pushBuffer.Fetch(state.gpu->memoryManager);
- Process(pushBuffer.segment);
- });
+ try {
+ signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}, signal::ExceptionalSignalHandler);
+ pushBuffers->Process([this](PushBuffer &pushBuffer) {
+ if (pushBuffer.segment.empty())
+ pushBuffer.Fetch(state.gpu->memoryManager);
+ Process(pushBuffer.segment);
+ });
+ } catch (const signal::SignalException &e) {
+ if (e.signal != SIGINT) {
+ state.logger->Write(Logger::LogLevel::Error, e.what());
+ signal::BlockSignal({SIGINT});
+ state.process->mainThread->Kill(false);
+ }
+ } catch (const std::exception &e) {
+ state.logger->Write(Logger::LogLevel::Error, e.what());
+ state.process->mainThread->Kill(false);
+ }
}
void GPFIFO::Push(span entries) {
bool beforeBarrier{true};
- pushBuffers->AppendTranform(entries, [&beforeBarrier, this](const GpEntry& entry){
+ pushBuffers->AppendTranform(entries, [&beforeBarrier, this](const GpEntry &entry) {
if (entry.sync == GpEntry::Sync::Wait)
beforeBarrier = false;
return PushBuffer(entry, state.gpu->memoryManager, beforeBarrier);
});
}
+
+ GPFIFO::~GPFIFO() {
+ if (thread.joinable()) {
+ pthread_kill(thread.native_handle(), SIGINT);
+ thread.join();
+ }
+ }
}
diff --git a/app/src/main/cpp/skyline/gpu/gpfifo.h b/app/src/main/cpp/skyline/gpu/gpfifo.h
index 2366d545..058faa91 100644
--- a/app/src/main/cpp/skyline/gpu/gpfifo.h
+++ b/app/src/main/cpp/skyline/gpu/gpfifo.h
@@ -3,7 +3,7 @@
#pragma once
-#include "circular_queue.h"
+#include
#include "engines/gpfifo.h"
#include "memory_manager.h"
@@ -163,6 +163,8 @@ namespace skyline::gpu {
public:
GPFIFO(const DeviceState &state) : state(state), gpfifoEngine(state) {}
+ ~GPFIFO();
+
/**
* @param numBuffers The amount of push-buffers to allocate in the circular buffer
*/
diff --git a/app/src/main/cpp/skyline/gpu/presentation_engine.cpp b/app/src/main/cpp/skyline/gpu/presentation_engine.cpp
index f23ea28b..deeedc2e 100644
--- a/app/src/main/cpp/skyline/gpu/presentation_engine.cpp
+++ b/app/src/main/cpp/skyline/gpu/presentation_engine.cpp
@@ -5,8 +5,8 @@
#include "jvm.h"
#include "presentation_engine.h"
-extern skyline::u16 fps;
-extern skyline::u32 frametime;
+extern skyline::u16 Fps;
+extern skyline::u32 FrameTime;
namespace skyline::gpu {
PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared(state)), bufferEvent(std::make_shared(state)) {}
@@ -29,7 +29,7 @@ namespace skyline::gpu {
if (!env->IsSameObject(newSurface, nullptr))
surface = env->NewGlobalRef(newSurface);
if (surface) {
- window = ANativeWindow_fromSurface(state.jvm->GetEnv(), surface);
+ window = ANativeWindow_fromSurface(env, surface);
ANativeWindow_acquire(window);
resolution.width = static_cast(ANativeWindow_getWidth(window));
resolution.height = static_cast(ANativeWindow_getHeight(window));
@@ -73,8 +73,8 @@ namespace skyline::gpu {
if (frameTimestamp) {
auto now{util::GetTimeNs()};
- frametime = static_cast((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals
- fps = static_cast(constant::NsInSecond / (now - frameTimestamp));
+ FrameTime = static_cast((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals
+ Fps = static_cast(constant::NsInSecond / (now - frameTimestamp));
frameTimestamp = now;
} else {
diff --git a/app/src/main/cpp/skyline/gpu/presentation_engine.h b/app/src/main/cpp/skyline/gpu/presentation_engine.h
index e66d6ff5..17d79f6a 100644
--- a/app/src/main/cpp/skyline/gpu/presentation_engine.h
+++ b/app/src/main/cpp/skyline/gpu/presentation_engine.h
@@ -14,7 +14,7 @@ namespace skyline::gpu {
const DeviceState &state;
std::mutex windowMutex;
std::condition_variable windowConditional;
- jobject surface; //!< The Surface object backing the ANativeWindow
+ jobject surface{}; //!< The Surface object backing the ANativeWindow
u64 frameTimestamp{}; //!< The timestamp of the last frame being shown
public:
diff --git a/app/src/main/cpp/skyline/jvm.cpp b/app/src/main/cpp/skyline/jvm.cpp
index d6d2bf06..892bfd33 100644
--- a/app/src/main/cpp/skyline/jvm.cpp
+++ b/app/src/main/cpp/skyline/jvm.cpp
@@ -8,18 +8,19 @@ namespace skyline {
* @brief A thread-local wrapper over JNIEnv and JavaVM which automatically handles attaching and detaching threads
*/
struct JniEnvironment {
- static inline JNIEnv *env{};
+ JNIEnv *env{};
static inline JavaVM *vm{};
bool attached{};
- JniEnvironment(JNIEnv *environment) {
+ void Initialize(JNIEnv *environment) {
env = environment;
if (env->GetJavaVM(&vm) < 0)
throw exception("Cannot get JavaVM from environment");
+ attached = true;
}
JniEnvironment() {
- if (vm) {
+ if (vm && !attached) {
vm->AttachCurrentThread(&env, nullptr);
attached = true;
}
@@ -31,10 +32,14 @@ namespace skyline {
}
operator JNIEnv *() {
+ if (!attached)
+ throw exception("Not attached");
return env;
}
- JNIEnv* operator->() {
+ JNIEnv *operator->() {
+ if (!attached)
+ throw exception("Not attached");
return env;
}
};
@@ -42,7 +47,7 @@ namespace skyline {
thread_local inline JniEnvironment env;
JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(environ->NewGlobalRef(instance)), instanceClass(reinterpret_cast(environ->NewGlobalRef(environ->GetObjectClass(instance)))), initializeControllersId(environ->GetMethodID(instanceClass, "initializeControllers", "()V")), vibrateDeviceId(environ->GetMethodID(instanceClass, "vibrateDevice", "(I[J[I)V")), clearVibrationDeviceId(environ->GetMethodID(instanceClass, "clearVibrationDevice", "(I)V")) {
- env = JniEnvironment(environ);
+ env.Initialize(environ);
}
JvmManager::~JvmManager() {
diff --git a/app/src/main/cpp/skyline/kernel/memory.cpp b/app/src/main/cpp/skyline/kernel/memory.cpp
index fddd710e..cb4eaafe 100644
--- a/app/src/main/cpp/skyline/kernel/memory.cpp
+++ b/app/src/main/cpp/skyline/kernel/memory.cpp
@@ -7,6 +7,11 @@
namespace skyline::kernel {
MemoryManager::MemoryManager(const DeviceState &state) : state(state) {}
+ MemoryManager::~MemoryManager() {
+ if (base.address && base.size)
+ munmap(reinterpret_cast(base.address), base.size);
+ }
+
constexpr size_t RegionAlignment{1ULL << 21}; //!< The minimum alignment of a HOS memory region
void MemoryManager::InitializeVmm(memory::AddressSpaceType type) {
diff --git a/app/src/main/cpp/skyline/kernel/memory.h b/app/src/main/cpp/skyline/kernel/memory.h
index 52c1a981..08d6a989 100644
--- a/app/src/main/cpp/skyline/kernel/memory.h
+++ b/app/src/main/cpp/skyline/kernel/memory.h
@@ -226,6 +226,8 @@ namespace skyline {
MemoryManager(const DeviceState &state);
+ ~MemoryManager();
+
/**
* @note This should be called before any mappings in the VMM or calls to InitalizeRegions are done
*/
diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp
index f1b86a6b..6ce98c9e 100644
--- a/app/src/main/cpp/skyline/kernel/svc.cpp
+++ b/app/src/main/cpp/skyline/kernel/svc.cpp
@@ -169,7 +169,7 @@ namespace skyline::kernel::svc {
std::memcpy(source, destination, size);
- auto sourceObject{state.process->GetMemoryObject(destination)};
+ auto sourceObject{state.process->GetMemoryObject(source)};
if (!sourceObject)
throw exception("svcUnmapMemory: Cannot find source memory object in handle table for address 0x{:X}", source);
@@ -216,7 +216,9 @@ namespace skyline::kernel::svc {
void ExitProcess(const DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting process");
- exit(0);
+ if (state.thread->id)
+ state.process->mainThread->Kill(false);
+ std::longjmp(state.thread->originalCtx, true);
}
constexpr i32 IdealCoreDontCare{-1};
@@ -269,8 +271,7 @@ namespace skyline::kernel::svc {
void ExitThread(const DeviceState &state) {
state.logger->Debug("svcExitThread: Exiting current thread: {}", state.thread->id);
- state.thread->Kill();
- pthread_exit(nullptr);
+ std::longjmp(state.thread->originalCtx, true);
}
void SleepThread(const DeviceState &state) {
@@ -708,34 +709,34 @@ namespace skyline::kernel::svc {
void GetInfo(const DeviceState &state) {
enum class InfoState : u32 {
// 1.0.0+
- AllowedCpuIdBitmask = 0,
- AllowedThreadPriorityMask = 1,
- AliasRegionBaseAddr = 2,
- AliasRegionSize = 3,
- HeapRegionBaseAddr = 4,
- HeapRegionSize = 5,
- TotalMemoryAvailable = 6,
- TotalMemoryUsage = 7,
- IsCurrentProcessBeingDebugged = 8,
- ResourceLimit = 9,
- IdleTickCount = 10,
- RandomEntropy = 11,
+ AllowedCpuIdBitmask = 0,
+ AllowedThreadPriorityMask = 1,
+ AliasRegionBaseAddr = 2,
+ AliasRegionSize = 3,
+ HeapRegionBaseAddr = 4,
+ HeapRegionSize = 5,
+ TotalMemoryAvailable = 6,
+ TotalMemoryUsage = 7,
+ IsCurrentProcessBeingDebugged = 8,
+ ResourceLimit = 9,
+ IdleTickCount = 10,
+ RandomEntropy = 11,
// 2.0.0+
- AddressSpaceBaseAddr = 12,
- AddressSpaceSize = 13,
- StackRegionBaseAddr = 14,
- StackRegionSize = 15,
+ AddressSpaceBaseAddr = 12,
+ AddressSpaceSize = 13,
+ StackRegionBaseAddr = 14,
+ StackRegionSize = 15,
// 3.0.0+
- TotalSystemResourceAvailable = 16,
- TotalSystemResourceUsage = 17,
- TitleId = 18,
+ TotalSystemResourceAvailable = 16,
+ TotalSystemResourceUsage = 17,
+ TitleId = 18,
// 4.0.0+
- PrivilegedProcessId = 19,
+ PrivilegedProcessId = 19,
// 5.0.0+
- UserExceptionContextAddr = 20,
+ UserExceptionContextAddr = 20,
// 6.0.0+
TotalMemoryAvailableWithoutSystemResource = 21,
- TotalMemoryUsageWithoutSystemResource = 22,
+ TotalMemoryUsageWithoutSystemResource = 22,
};
InfoState info{static_cast(state.ctx->gpr.w1)};
diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp
index 93e90aec..453a3ac3 100644
--- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp
+++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp
@@ -50,14 +50,15 @@ namespace skyline::kernel::type {
}
std::shared_ptr KProcess::CreateThread(void *entry, u64 argument, void *stackTop, i8 priority, i8 idealCore) {
- if (!stackTop && threads.empty()) { //!< Main thread stack is created by the kernel and owned by the process
+ if (!stackTop && !mainThread) { //!< Main thread stack is created by the kernel and owned by the process
mainThreadStack = mainThreadStack.make_shared(state, reinterpret_cast(state.process->memory.stack.address), state.process->npdm.meta.mainThreadStackSize, memory::Permission{true, true, false}, memory::states::Stack);
if (mprotect(mainThreadStack->ptr, PAGE_SIZE, PROT_NONE))
throw exception("Failed to create guard page for thread stack at 0x{:X}", mainThreadStack->ptr);
stackTop = mainThreadStack->ptr + mainThreadStack->size;
}
- auto thread{NewHandle(this, threads.size(), entry, argument, stackTop, (priority == -1) ? state.process->npdm.meta.mainThreadPriority : priority, (idealCore == -1) ? state.process->npdm.meta.idealCore : idealCore).item};
- threads.push_back(thread);
+ auto thread{NewHandle(this, threadIndex++, entry, argument, stackTop, (priority == -1) ? state.process->npdm.meta.mainThreadPriority : priority, (idealCore == -1) ? state.process->npdm.meta.idealCore : idealCore).item};
+ if (!mainThread)
+ mainThread = thread;
return thread;
}
diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h
index efbff1c4..901aaa75 100644
--- a/app/src/main/cpp/skyline/kernel/types/KProcess.h
+++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h
@@ -24,13 +24,10 @@ namespace skyline {
* @brief KProcess manages process-global state such as memory, kernel handles allocated to the process and synchronization primitives
*/
class KProcess : public KSyncObject {
- public:
- MemoryManager memory; // This is here to ensure it is present during the destruction of dependent objects
+ public: // We have intermittent public/private members to ensure proper construction/destruction order
+ MemoryManager memory;
private:
- std::vector> handles;
- std::shared_mutex handleMutex;
-
struct WaitStatus {
std::atomic_bool flag{false};
i8 priority;
@@ -47,6 +44,8 @@ namespace skyline {
std::mutex mutexLock;
std::mutex conditionalLock;
+ size_t threadIndex{}; //!< The ID assigned to the next created thread
+
/**
* @brief The status of a single TLS page (A page is 4096 bytes on ARMv8)
* Each TLS page has 8 slots, each 0x200 (512) bytes in size
@@ -69,10 +68,15 @@ namespace skyline {
public:
std::shared_ptr mainThreadStack;
std::shared_ptr heap;
- std::vector> threads;
std::vector> tlsPages;
+ std::shared_ptr mainThread;
vfs::NPDM npdm;
+ private:
+ std::shared_mutex handleMutex;
+ std::vector> handles;
+
+ public:
KProcess(const DeviceState &state);
/**
diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp
index f56ee33e..68b73dec 100644
--- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp
+++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp
@@ -64,7 +64,7 @@ namespace skyline::kernel::type {
if (kernel.Valid())
munmap(kernel.ptr, kernel.size);
- if (guest.Valid()) {
+ if (state.process && guest.Valid()) {
mmap(guest.ptr, guest.size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = guest.ptr,
diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.cpp b/app/src/main/cpp/skyline/kernel/types/KThread.cpp
index 10d94fef..c3dcd168 100644
--- a/app/src/main/cpp/skyline/kernel/types/KThread.cpp
+++ b/app/src/main/cpp/skyline/kernel/types/KThread.cpp
@@ -2,8 +2,8 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include
-#include
#include
+#include
#include
#include
#include "KProcess.h"
@@ -15,29 +15,19 @@ namespace skyline::kernel::type {
}
KThread::~KThread() {
- Kill();
- }
-
- /**
- * @brief Our delegator for sigaction, we need to do this due to sigchain hooking bionic's sigaction and it intercepting signals before they're passed onto userspace
- * This not only leads to performance degradation but also requires host TLS to be in the TLS register which we cannot ensure for in-guest signals
- */
- inline void Sigaction(int signal, const struct sigaction &action, struct sigaction *oldAction = nullptr) {
- static decltype(&sigaction) realSigaction{};
- if (!realSigaction) {
- void *libc{dlopen("libc.so", RTLD_LOCAL | RTLD_LAZY)};
- if (!libc)
- throw exception("dlopen-ing libc has failed with: {}", dlerror());
- realSigaction = reinterpret_cast(dlsym(libc, "sigaction"));
- if (!realSigaction)
- throw exception("Cannot find 'sigaction' in libc: {}", dlerror());
+ if (running && pthread != pthread_self()) {
+ pthread_kill(pthread, SIGINT);
+ if (thread)
+ thread->join();
+ else
+ pthread_join(pthread, nullptr);
}
- if (realSigaction(signal, &action, oldAction) < 0)
- throw exception("sigaction has failed with {}", strerror(errno));
}
void KThread::StartThread() {
- pthread_setname_np(pthread_self(), fmt::format("HOS-{}", id).c_str());
+ std::array threadName;
+ pthread_getname_np(pthread, threadName.data(), threadName.size());
+ pthread_setname_np(pthread, fmt::format("HOS-{}", id).c_str());
state.logger->UpdateTag();
if (!ctx.tpidrroEl0)
@@ -47,12 +37,17 @@ namespace skyline::kernel::type {
state.ctx = &ctx;
state.thread = shared_from_this();
- struct sigaction sigact{
- .sa_sigaction = &nce::NCE::SignalHandler,
- .sa_flags = SA_SIGINFO,
- };
- for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
- Sigaction(signal, sigact);
+ if (setjmp(originalCtx)) { // Returns 1 if it's returning from guest, 0 otherwise
+ running = false;
+ Signal();
+
+ pthread_setname_np(pthread, threadName.data());
+ state.logger->UpdateTag();
+
+ return;
+ }
+
+ signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}, nce::NCE::SignalHandler);
asm volatile(
"MRS X0, TPIDR_EL0\n\t"
@@ -136,28 +131,32 @@ namespace skyline::kernel::type {
}
void KThread::Start(bool self) {
+ std::unique_lock lock(mutex);
if (!running) {
running = true;
state.logger->Debug("Starting thread #{}", id);
- if (self)
+ if (self) {
+ pthread = pthread_self();
+ lock.unlock();
StartThread();
- else
+ } else {
thread.emplace(&KThread::StartThread, this);
+ pthread = thread->native_handle();
+ }
}
}
- void KThread::Kill() {
+ void KThread::Kill(bool join) {
+ std::lock_guard lock(mutex);
if (running) {
+ pthread_kill(pthread, SIGINT);
+ if (join)
+ pthread_join(pthread, nullptr);
running = false;
- Signal();
}
}
void KThread::UpdatePriority(i8 priority) {
this->priority = priority;
- auto priorityValue{constant::AndroidPriority.Rescale(constant::HosPriority, priority)};
-
- if (setpriority(PRIO_PROCESS, getpid(), priorityValue) == -1)
- throw exception("Couldn't set thread priority to {} for #{}", priorityValue, id);
}
}
diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.h b/app/src/main/cpp/skyline/kernel/types/KThread.h
index bd599ace..f25ddaf7 100644
--- a/app/src/main/cpp/skyline/kernel/types/KThread.h
+++ b/app/src/main/cpp/skyline/kernel/types/KThread.h
@@ -3,6 +3,7 @@
#pragma once
+#include
#include
#include "KSyncObject.h"
#include "KPrivateMemory.h"
@@ -35,7 +36,6 @@ namespace skyline {
namespace constant {
constexpr u8 CoreCount{4}; // The amount of cores an HOS process can be scheduled onto (User applications can only be on the first 3 cores, the last one is reserved for the system)
- constexpr kernel::type::Priority AndroidPriority{19, -8}; //!< The range of priorities for Android
constexpr kernel::type::Priority HosPriority{0, 63}; //!< The range of priorities for Horizon OS
}
@@ -43,25 +43,28 @@ namespace skyline {
/**
* @brief KThread manages a single thread of execution which is responsible for running guest code and kernel code which is invoked by the guest
*/
- class KThread : public KSyncObject, public std::enable_shared_from_this {
+ class KThread : public KSyncObject, public std::enable_shared_from_this {
private:
KProcess *parent;
std::optional thread; //!< If this KThread is backed by a host thread then this'll hold it
+ pthread_t pthread{}; //!< The pthread_t for the host thread running this guest thread
void StartThread();
public:
+ std::mutex mutex; //!< Synchronizes all thread state changes
bool running{false};
std::atomic cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in
KHandle handle;
size_t id; //!< Index of thread in parent process's KThread vector
- nce::ThreadContext ctx{};
+ nce::ThreadContext ctx{}; //!< The context of the guest thread during the last SVC
+ jmp_buf originalCtx; //!< The context of the host thread prior to jumping into guest code
- void* entry;
+ void *entry;
u64 entryArgument;
- void* stackTop;
+ void *stackTop;
i8 priority;
i8 idealCore;
@@ -80,15 +83,10 @@ namespace skyline {
void Start(bool self = false);
/**
- * @brief Updates the internal state of the thread to signal it being dead
- * @note This should only be called by the host thread running this guest thread
+ * @param join Returns after the guest thread has joined rather than instantly
*/
- void Kill();
+ void Kill(bool join);
- /**
- * @brief Sets the host priority using setpriority with a rescaled the priority from HOS to Android
- * @note It also affects guest scheduler behavior, this isn't purely for host
- */
void UpdatePriority(i8 priority);
};
}
diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp
index d3f0a636..b8e5acf3 100644
--- a/app/src/main/cpp/skyline/nce.cpp
+++ b/app/src/main/cpp/skyline/nce.cpp
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
-#include
#include
+#include "common/signal.h"
#include "os.h"
#include "gpu.h"
#include "jvm.h"
@@ -23,57 +23,84 @@ namespace skyline::nce {
} else {
throw exception("Unimplemented SVC 0x{:X}", svc);
}
+ } catch (const signal::SignalException &e) {
+ if (e.signal != SIGINT) {
+ state.logger->Error("{} (SVC: 0x{:X})", e.what(), svc);
+ if (state.thread->id) {
+ signal::BlockSignal({SIGINT});
+ state.process->mainThread->Kill(false);
+ }
+ }
+ std::longjmp(state.thread->originalCtx, true);
} catch (const std::exception &e) {
state.logger->Error("{} (SVC: 0x{:X})", e.what(), svc);
- exit(0);
+ if (state.thread->id) {
+ signal::BlockSignal({SIGINT});
+ state.process->mainThread->Kill(false);
+ }
+ std::longjmp(state.thread->originalCtx, true);
}
}
- void NCE::SignalHandler(int signal, siginfo *, void *context) {
+ void NCE::SignalHandler(int signal, siginfo *info, ucontext *context, void *oldTls) {
+ if (oldTls) {
+ const auto &state{*reinterpret_cast(oldTls)->state};
+
+ state.logger->Warn("Thread #{} has crashed due to signal: {}", state.thread->id, strsignal(signal));
+
+ std::string raw;
+ std::string trace;
+ std::string cpuContext;
+
+ const auto &ctx{reinterpret_cast(context)->uc_mcontext};
+ constexpr u16 instructionCount{20}; // The amount of previous instructions to print
+ auto offset{ctx.pc - (instructionCount * sizeof(u32)) + (2 * sizeof(u32))};
+ span instructions(reinterpret_cast(offset), instructionCount);
+ for (auto &instruction : instructions) {
+ instruction = __builtin_bswap32(instruction);
+
+ if (offset == ctx.pc)
+ trace += fmt::format("\n-> 0x{:X} : 0x{:08X}", offset, instruction);
+ else
+ trace += fmt::format("\n 0x{:X} : 0x{:08X}", offset, instruction);
+
+ raw += fmt::format("{:08X}", instruction);
+ offset += sizeof(u32);
+ }
+
+ if (ctx.fault_address)
+ cpuContext += fmt::format("\nFault Address: 0x{:X}", ctx.fault_address);
+
+ if (ctx.sp)
+ cpuContext += fmt::format("\nStack Pointer: 0x{:X}", ctx.sp);
+
+ for (u8 index{}; index < ((sizeof(mcontext_t::regs) / sizeof(u64)) - 2); index += 2)
+ cpuContext += fmt::format("\n{}X{}: 0x{:<16X} {}{}: 0x{:X}", index < 10 ? ' ' : '\0', index, ctx.regs[index], index < 10 ? ' ' : '\0', index + 1, ctx.regs[index]);
+
+ state.logger->Debug("Process Trace:{}", trace);
+ state.logger->Debug("Raw Instructions: 0x{}", raw);
+ state.logger->Debug("CPU Context:{}", cpuContext);
+
+ context->uc_mcontext.pc = reinterpret_cast(&std::longjmp);
+ context->uc_mcontext.regs[0] = reinterpret_cast(state.thread->originalCtx);
+ context->uc_mcontext.regs[1] = true;
+ } else {
+ signal::ExceptionalSignalHandler(signal, info, context); //!< Delegate throwing a host exception to the exceptional signal handler
+ }
+ }
+
+ void *NceTlsRestorer() {
ThreadContext *threadCtx;
asm volatile("MRS %x0, TPIDR_EL0":"=r"(threadCtx));
+ if (threadCtx->magic != constant::SkyTlsMagic)
+ return nullptr;
asm volatile("MSR TPIDR_EL0, %x0"::"r"(threadCtx->hostTpidrEl0));
-
- const auto &state{*threadCtx->state};
- state.logger->Warn("Thread #{} has crashed due to signal: {}", state.thread->id, strsignal(signal));
-
- std::string raw;
- std::string trace;
- std::string cpuContext;
-
- const auto &ctx{reinterpret_cast(context)->uc_mcontext};
- constexpr u16 instructionCount{20}; // The amount of previous instructions to print
- auto offset{ctx.pc - (instructionCount * sizeof(u32)) + (2 * sizeof(u32))};
- span instructions(reinterpret_cast(offset), instructionCount);
- for (auto &instruction : instructions) {
- instruction = __builtin_bswap32(instruction);
-
- if (offset == ctx.pc)
- trace += fmt::format("\n-> 0x{:X} : 0x{:08X}", offset, instruction);
- else
- trace += fmt::format("\n 0x{:X} : 0x{:08X}", offset, instruction);
-
- raw += fmt::format("{:08X}", instruction);
- offset += sizeof(u32);
- }
-
- if (ctx.fault_address)
- cpuContext += fmt::format("\nFault Address: 0x{:X}", ctx.fault_address);
-
- if (ctx.sp)
- cpuContext += fmt::format("\nStack Pointer: 0x{:X}", ctx.sp);
-
- for (u8 index{}; index < ((sizeof(mcontext_t::regs) / sizeof(u64)) - 2); index += 2)
- cpuContext += fmt::format("\n{}X{}: 0x{:<16X} {}{}: 0x{:X}", index < 10 ? ' ' : '\0', index, ctx.regs[index], index < 10 ? ' ' : '\0', index + 1, ctx.regs[index]);
-
- state.logger->Warn("Process Trace:{}", trace);
- state.logger->Warn("Raw Instructions: 0x{}", raw);
- state.logger->Warn("CPU Context:{}", cpuContext);
-
- asm volatile("MSR TPIDR_EL0, %x0"::"r"(threadCtx));
+ return threadCtx;
}
- NCE::NCE(DeviceState &state) : state(state) {}
+ NCE::NCE(const DeviceState &state) : state(state) {
+ signal::SetTlsRestorer(&NceTlsRestorer);
+ }
constexpr u8 MainSvcTrampolineSize{17}; // Size of the main SVC trampoline function in u32 units
constexpr u32 TpidrEl0{0x5E82}; // ID of TPIDR_EL0 in MRS
diff --git a/app/src/main/cpp/skyline/nce.h b/app/src/main/cpp/skyline/nce.h
index fd2b6385..2347f23b 100644
--- a/app/src/main/cpp/skyline/nce.h
+++ b/app/src/main/cpp/skyline/nce.h
@@ -8,18 +8,18 @@
namespace skyline::nce {
/**
- * @brief The NCE (Native Code Execution) class is responsible for managing the state of catching instructions and directly controlling processes/threads
+ * @brief The NCE (Native Code Execution) class is responsible for managing state relevant to the layer between the host and guest
*/
class NCE {
private:
- DeviceState &state;
+ const DeviceState &state;
static void SvcHandler(u16 svc, ThreadContext* ctx);
public:
- static void SignalHandler(int signal, siginfo *info, void *context);
+ static void SignalHandler(int signal, siginfo *info, ucontext *context, void *oldTls);
- NCE(DeviceState &state);
+ NCE(const DeviceState &state);
struct PatchData {
size_t size; //!< Size of the .patch section
diff --git a/app/src/main/cpp/skyline/nce/guest.h b/app/src/main/cpp/skyline/nce/guest.h
index 69a4ebe2..7b379eab 100644
--- a/app/src/main/cpp/skyline/nce/guest.h
+++ b/app/src/main/cpp/skyline/nce/guest.h
@@ -7,6 +7,9 @@
namespace skyline {
struct DeviceState;
+ namespace constant {
+ constexpr u64 SkyTlsMagic{util::MakeMagic("SKYTLS")};
+ }
namespace nce {
struct WRegister {
u32 lower;
@@ -96,6 +99,7 @@ namespace skyline {
u8 *tpidrroEl0; //!< Emulated HOS TPIDRRO_EL0
u8 *tpidrEl0; //!< Emulated HOS TPIDR_EL0
const DeviceState *state;
+ u64 magic{constant::SkyTlsMagic};
};
namespace guest {
diff --git a/app/src/main/cpp/skyline/os.cpp b/app/src/main/cpp/skyline/os.cpp
index 414e844c..421fd0ef 100644
--- a/app/src/main/cpp/skyline/os.cpp
+++ b/app/src/main/cpp/skyline/os.cpp
@@ -12,7 +12,7 @@
#include "os.h"
namespace skyline::kernel {
- OS::OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings, const std::string &appFilesPath) : state(this, process, jvmManager, settings, logger), serviceManager(state), appFilesPath(appFilesPath) {}
+ OS::OS(std::shared_ptr &jvmManager, std::shared_ptr &logger, std::shared_ptr &settings, const std::string &appFilesPath) : state(this, jvmManager, settings, logger), serviceManager(state), appFilesPath(appFilesPath) {}
void OS::Execute(int romFd, loader::RomFormat romType) {
auto romFile{std::make_shared(romFd)};
@@ -29,6 +29,7 @@ namespace skyline::kernel {
else
throw exception("Unsupported ROM extension.");
+ auto& process{state.process};
process = std::make_shared(state);
auto entry{state.loader->LoadProcessData(process, state)};
process->InitializeHeap();
diff --git a/app/src/main/cpp/skyline/os.h b/app/src/main/cpp/skyline/os.h
index ec53d361..c04ac281 100644
--- a/app/src/main/cpp/skyline/os.h
+++ b/app/src/main/cpp/skyline/os.h
@@ -13,7 +13,6 @@ namespace skyline::kernel {
class OS {
public:
DeviceState state;
- std::shared_ptr process;
service::ServiceManager serviceManager;
std::string appFilesPath; //!< The full path to the app's files directory
diff --git a/app/src/main/cpp/skyline/services/account/IProfile.cpp b/app/src/main/cpp/skyline/services/account/IProfile.cpp
index 13b0a5f5..c4129193 100644
--- a/app/src/main/cpp/skyline/services/account/IProfile.cpp
+++ b/app/src/main/cpp/skyline/services/account/IProfile.cpp
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
+#include
#include "IProfile.h"
namespace skyline::service::account {
diff --git a/app/src/main/cpp/skyline/services/am/controller/ICommonStateGetter.cpp b/app/src/main/cpp/skyline/services/am/controller/ICommonStateGetter.cpp
index 6feabdcf..8b756f8e 100644
--- a/app/src/main/cpp/skyline/services/am/controller/ICommonStateGetter.cpp
+++ b/app/src/main/cpp/skyline/services/am/controller/ICommonStateGetter.cpp
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
+#include
#include
#include "ICommonStateGetter.h"
diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt
index e346b171..18a1dca9 100644
--- a/app/src/main/java/emu/skyline/EmulationActivity.kt
+++ b/app/src/main/java/emu/skyline/EmulationActivity.kt
@@ -68,11 +68,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
private external fun executeApplication(romUri : String, romType : Int, romFd : Int, preferenceFd : Int, appFilesPath : String)
/**
- * This sets the halt flag in libskyline to the provided value, if set to true it causes libskyline to halt emulation
- *
- * @param halt The value to set halt to
+ * Terminate of all emulator threads and cause [emulationThread] to return
*/
- private external fun exitGuest(halt : Boolean)
+ private external fun stopEmulation()
/**
* This sets the surface object in libskyline to the provided value, emulation is halted if set to null
@@ -233,31 +231,26 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
* This is used to stop the currently executing ROM and replace it with the one specified in the new intent
*/
override fun onNewIntent(intent : Intent?) {
+ super.onNewIntent(intent)
shouldFinish = false
- exitGuest(true)
+ stopEmulation()
emulationThread.join()
shouldFinish = true
executeApplication(intent?.data!!)
-
- super.onNewIntent(intent)
}
- /**
- * This is used to halt emulation entirely
- */
override fun onDestroy() {
+ super.onDestroy()
shouldFinish = false
- exitGuest(true)
- emulationThread.join(1000)
+ stopEmulation()
+ emulationThread.join()
vibrators.forEach { (_, vibrator) -> vibrator.cancel() }
vibrators.clear()
-
- super.onDestroy()
}
/**