Compare commits

...
Sign in to create a new pull request.

12 commits

Author SHA1 Message Date
PixelyIon
036416fde7 Address CR Comments (#150)
Correct a single grammatical error
2021-03-23 02:24:16 +05:30
PixelyIon
662470c415 Add Default Run Configuration + Ignore runConfigurations.xml
This commit adds a default runConfiguration which just launches the default activity without any special parameters, it also adds `.idea/runConfigurations.xml` to `.gitignore` as it is a legacy file which may be generated by older versions of Android Studio.
2021-03-23 02:24:16 +05:30
PixelyIon
5da5b518c5 Revamp README
The README had rendering issues on GitHub Mobile due to the "Special Thanks" ("Credits" previously) images not being correctly sized which has been resolved by removing those images. In addition, the badges at the top had unnecessary extra spacing on mobile which was required on desktop and has been fixed by exploiting differential behavior between them regarding nesting HTML line breaks (`<br>`) within a HTML hyperlink tag (`<a>`) which causes a space on desktop while not showing up on mobile.

This commit also refactors the entire file for better readability by changes such as using **bold** to emphasize points and explicit Markdown dividers (`---`) between sections. 

A distinct change is clarifying that Skyline is not a Ryujinx derivative as we found a lot of people who thought this by:
* Renaming the "Credits" section to "Special Thanks"
* Explicitly specifying that Skyline is not based on it in **bold** at the end of the description
2021-03-23 02:24:16 +05:30
PixelyIon
acc5668274 Disable All Warnings from TZCode
This just disables all compiler warnings generated while compiling TZCode as those are irrelevant while compiling Skyline and need to be tackled in that repository.
2021-03-21 20:34:59 +05:30
PixelyIon
1c5281eb0e Fix BT Audio Stuttering Issues
This fixes audio stuttering which occurred on certain BT audio devices by requesting an exclusive stream from Oboe alongside a low-latency stream.

Co-authored-by: Billy Laws <blaws05@gmail.com>
2021-03-21 20:13:29 +05:30
PixelyIon
b0e53d4aad Extend Perfetto Tracing
Add Tracing for SVCs, Services, NVDRV, and Synchronization Primitives. In addition, fix `TRACE_EVENT_END("guest")` being emitted when a signal is received while being in the guest rather than host which would cause an exception. This commit also disables warnings for the Perfetto library as we do not control fixing them.
2021-03-21 20:13:29 +05:30
PixelyIon
c452cd2bf2 Extend SVC table with names
This extend a descriptor table for the SVCs with names for every SVC alongside their function pointer. The names are then used for logging and eventually tracing.
2021-03-21 20:13:03 +05:30
PixelyIon
711d4f8824 Optimize NvDevice Function Lookup
This essentially optimizes NvDevice in the same way services were, refer to the last commit for details
2021-03-21 20:13:03 +05:30
PixelyIon
5149b333d7 Optimize Service Function Lookup
This moves from using std::function with a this pointer binding (which would likely cause a heap allocation) to returning the this pointer in a structure which implements operator() to do the call with it. It also moves to using const char* for strings from std::string_view which was pointless in this scenario due to it's usage being limited to being a C-string for the most part, it also integrates the class name directly into the string which allows us to avoid runtime string concatenation in libfmt and RTTI for finding the class name.
2021-03-21 20:13:03 +05:30
PixelyIon
4e51d76fa6 Logger Function Prefix Support 2021-03-21 20:13:03 +05:30
Billy Laws
a1d90f053a Implement Perfetto Tracing
This commit implements tracing Skyline using https://perfetto.dev/ for SVCs, IPC, Scheduler and Presentation
2021-03-21 20:13:03 +05:30
PixelyIon
c7de7890b6 Improve KMemory + Fix Service Implementation Warning
* Improve KMemory Comments
* Add parameter prefix 'p-' to `KPrivateMemory::UpdatePermission`
* Fix the missing trailing double quote in missing service prints, this was due to `stringName` being padded with extra 0s
2021-03-21 20:10:27 +05:30
41 changed files with 694 additions and 367 deletions

1
.gitignore vendored
View file

@ -50,6 +50,7 @@ build/
# Android Studio
.idea/caches
.idea/assetWizardSettings.xml
.idea/runConfigurations.xml
# Mongo Explorer plugin
.idea/**/mongoSettings.xml

4
.gitmodules vendored
View file

@ -21,3 +21,7 @@
path = app/libraries/tzcode
url = https://github.com/skyline-emu/tz
branch = master
[submodule "app/libraries/perfetto"]
path = app/libraries/perfetto
url = https://android.googlesource.com/platform/external/perfetto
branch = releases/v12.x

View file

@ -171,7 +171,7 @@
</inspection_tool>
<inspection_tool class="CheckedExceptionClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClangTidy" enabled="true" level="WARNING" enabled_by_default="true">
<option name="clangTidyChecks" value="-*,bugprone-argument-comment,bugprone-assert-side-effect,bugprone-bad-signal-to-kill-thread,bugprone-branch-clone,bugprone-copy-constructor-init,bugprone-dangling-handle,bugprone-dynamic-static-initializers,bugprone-fold-init-type,bugprone-forward-declaration-namespace,bugprone-forwarding-reference-overload,bugprone-inaccurate-erase,bugprone-incorrect-roundings,bugprone-integer-division,bugprone-lambda-function-name,bugprone-macro-parentheses,bugprone-macro-repeated-side-effects,bugprone-misplaced-operator-in-strlen-in-alloc,bugprone-misplaced-pointer-arithmetic-in-alloc,bugprone-misplaced-widening-cast,bugprone-move-forwarding-reference,bugprone-multiple-statement-macro,bugprone-no-escape,bugprone-not-null-terminated-result,bugprone-parent-virtual-call,bugprone-posix-return,bugprone-reserved-identifier,bugprone-sizeof-container,bugprone-sizeof-expression,bugprone-spuriously-wake-up-functions,bugprone-string-constructor,bugprone-string-integer-assignment,bugprone-string-literal-with-embedded-nul,bugprone-suspicious-enum-usage,bugprone-suspicious-include,bugprone-suspicious-memset-usage,bugprone-suspicious-missing-comma,bugprone-suspicious-semicolon,bugprone-suspicious-string-compare,bugprone-swapped-arguments,bugprone-terminating-continue,bugprone-throw-keyword-missing,bugprone-too-small-loop-variable,bugprone-undefined-memory-manipulation,bugprone-undelegated-constructor,bugprone-unhandled-self-assignment,bugprone-unused-raii,bugprone-unused-return-value,bugprone-use-after-move,bugprone-virtual-near-miss,cert-dcl21-cpp,cert-dcl58-cpp,cert-err34-c,cert-err58-cpp,cert-err60-cpp,cert-flp30-c,cert-msc50-cpp,cert-msc51-cpp,cert-str34-c,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-pro-type-static-cast-downcast,cppcoreguidelines-slicing,google-default-arguments,google-explicit-constructor,google-runtime-operator,hicpp-exception-baseclass,hicpp-multiway-paths-covered,misc-misplaced-const,misc-new-delete-overloads,misc-no-recursion,misc-non-copyable-objects,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,misc-uniqueptr-reset-release,modernize-avoid-bind,modernize-concat-nested-namespaces,modernize-deprecated-ios-base-aliases,modernize-loop-convert,modernize-make-shared,modernize-make-unique,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-auto-ptr,modernize-replace-disallow-copy-and-assign-macro,modernize-replace-random-shuffle,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-nodiscard,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-transparent-functors,modernize-use-uncaught-exceptions,mpi-buffer-deref,mpi-type-mismatch,openmp-use-default-none,performance-faster-string-find,performance-for-range-copy,performance-implicit-conversion-in-loop,performance-inefficient-algorithm,performance-inefficient-string-concatenation,performance-inefficient-vector-operation,performance-move-const-arg,performance-move-constructor-init,performance-no-automatic-move,performance-noexcept-move-constructor,performance-trivially-destructible,performance-type-promotion-in-math-fn,performance-unnecessary-copy-initialization,performance-unnecessary-value-param,portability-simd-intrinsics,readability-avoid-const-params-in-decls,readability-const-return-type,readability-container-size-empty,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-deleted-default,readability-inconsistent-declaration-parameter-name,readability-make-member-function-const,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-smartptr-get,readability-redundant-string-cstr,readability-redundant-string-init,readability-simplify-subscript-expr,readability-static-accessed-through-instance,readability-static-definition-in-anonymous-namespace,readability-string-compare,readability-uniqueptr-delete-release,readability-use-anyofallof" />
<option name="clangTidyChecks" value="-*,bugprone-argument-comment,bugprone-assert-side-effect,bugprone-bad-signal-to-kill-thread,bugprone-branch-clone,bugprone-copy-constructor-init,bugprone-dangling-handle,bugprone-dynamic-static-initializers,bugprone-fold-init-type,bugprone-forward-declaration-namespace,bugprone-forwarding-reference-overload,bugprone-inaccurate-erase,bugprone-incorrect-roundings,bugprone-integer-division,bugprone-lambda-function-name,bugprone-macro-parentheses,bugprone-macro-repeated-side-effects,bugprone-misplaced-operator-in-strlen-in-alloc,bugprone-misplaced-pointer-arithmetic-in-alloc,bugprone-misplaced-widening-cast,bugprone-move-forwarding-reference,bugprone-multiple-statement-macro,bugprone-no-escape,bugprone-not-null-terminated-result,bugprone-parent-virtual-call,bugprone-posix-return,bugprone-reserved-identifier,bugprone-sizeof-container,bugprone-sizeof-expression,bugprone-spuriously-wake-up-functions,bugprone-string-constructor,bugprone-string-integer-assignment,bugprone-string-literal-with-embedded-nul,bugprone-suspicious-enum-usage,bugprone-suspicious-include,bugprone-suspicious-memset-usage,bugprone-suspicious-missing-comma,bugprone-suspicious-semicolon,bugprone-suspicious-string-compare,bugprone-swapped-arguments,bugprone-terminating-continue,bugprone-throw-keyword-missing,bugprone-too-small-loop-variable,bugprone-undefined-memory-manipulation,bugprone-undelegated-constructor,bugprone-unhandled-self-assignment,bugprone-unused-raii,bugprone-unused-return-value,bugprone-use-after-move,bugprone-virtual-near-miss,cert-dcl21-cpp,cert-dcl58-cpp,cert-err34-c,cert-err52-cpp,cert-err60-cpp,cert-flp30-c,cert-msc50-cpp,cert-msc51-cpp,cert-str34-c,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-pro-type-static-cast-downcast,cppcoreguidelines-slicing,google-default-arguments,google-explicit-constructor,google-runtime-operator,hicpp-exception-baseclass,hicpp-multiway-paths-covered,misc-misplaced-const,misc-new-delete-overloads,misc-no-recursion,misc-non-copyable-objects,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,misc-uniqueptr-reset-release,modernize-avoid-bind,modernize-concat-nested-namespaces,modernize-deprecated-headers,modernize-deprecated-ios-base-aliases,modernize-loop-convert,modernize-make-shared,modernize-make-unique,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-auto-ptr,modernize-replace-disallow-copy-and-assign-macro,modernize-replace-random-shuffle,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-nodiscard,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-transparent-functors,modernize-use-uncaught-exceptions,mpi-buffer-deref,mpi-type-mismatch,openmp-use-default-none,performance-faster-string-find,performance-for-range-copy,performance-implicit-conversion-in-loop,performance-inefficient-algorithm,performance-inefficient-string-concatenation,performance-inefficient-vector-operation,performance-move-const-arg,performance-move-constructor-init,performance-no-automatic-move,performance-noexcept-move-constructor,performance-trivially-destructible,performance-type-promotion-in-math-fn,performance-unnecessary-copy-initialization,performance-unnecessary-value-param,portability-simd-intrinsics,readability-avoid-const-params-in-decls,readability-const-return-type,readability-container-size-empty,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-deleted-default,readability-inconsistent-declaration-parameter-name,readability-make-member-function-const,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-smartptr-get,readability-redundant-string-cstr,readability-redundant-string-init,readability-simplify-subscript-expr,readability-static-accessed-through-instance,readability-static-definition-in-anonymous-namespace,readability-string-compare,readability-uniqueptr-delete-release,readability-use-anyofallof" />
</inspection_tool>
<inspection_tool class="ClassComplexity" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="80" />

60
.idea/runConfigurations/Main.xml generated Normal file
View file

@ -0,0 +1,60 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Main" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
<module name="skyline.app" />
<option name="DEPLOY" value="true" />
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
<option name="DEPLOY_AS_INSTANT" value="false" />
<option name="ARTIFACT_NAME" value="" />
<option name="PM_INSTALL_OPTIONS" value="" />
<option name="ALL_USERS" value="false" />
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
<option name="MODE" value="default_activity" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
<option name="FORCE_STOP_RUNNING_APP" value="true" />
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
<option name="DEBUGGER_TYPE" value="Auto" />
<Auto>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Auto>
<Hybrid>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Hybrid>
<Java />
<Native>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Native>
<Profilers>
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>
<option name="DEEP_LINK" value="" />
<option name="ACTIVITY_CLASS" value="" />
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View file

@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Setting" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
<configuration default="false" name="Settings" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
<module name="skyline.app" />
<option name="DEPLOY" value="true" />
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />

2
.idea/vcs.xml generated
View file

@ -6,7 +6,9 @@
<mapping directory="$PROJECT_DIR$/app/libraries/frozen" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/lz4" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/oboe" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/perfetto" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/pugixml" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/tzcode" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/vkhpp" vcs="Git" />
</component>
</project>

View file

@ -1,34 +1,36 @@
<h1 align="center">
<img height="60%" width="60%" src="https://i.imgur.com/6PJ7Ml2.png"><br>
<a href="https://github.com/skyline-emu/skyline" target="_blank">
<img height="60%" width="60%" src="https://i.imgur.com/6PJ7Ml2.png"><br>
</a>
<a href="https://discord.gg/XnbXNQM" target="_blank">
<img src="https://img.shields.io/discord/545842171459272705?label=Discord&logo=Discord&color=yellow">
</a>
<a href="LICENSE.md" target="_blank">
<img src="https://img.shields.io/badge/License-MPL%202.0-yellow"/><br>
<a href="https://github.com/skyline-emu/skyline/actions/workflows/ci.yml" target="_blank">
<img src="https://github.com/skyline-emu/skyline/actions/workflows/ci.yml/badge.svg"/><br>
</a>
<img src="https://forthebadge.com/images/badges/built-for-android.svg"/>
</h1>
<p align="center">
<i>Skyline is an experimental emulator that runs on ARMv8 Android™ devices and emulates the functionality of a Nintendo Switch™ system. It's licensed under the MPL, refer to the <a href="https://github.com/skyline-emu/skyline/blob/master/LICENSE.md">license file</a> for more information.</i><br/><br>
<b>Skyline</b> is an experimental emulator that runs on <b>ARMv8 Android™</b> devices and emulates the functionality of a <b>Nintendo Switch™</b> system, licensed under <a href="https://github.com/skyline-emu/skyline/blob/master/LICENSE.md"><b>Mozilla Public License 2.0</b></a>
</p>
---
### Contact
You can contact the core developers of Skyline at our [Discord](https://discord.gg/XnbXNQM). If you have any questions, feel free to ask. It's also a good place to just keep up with the emulator, as most talk regarding development goes on over there.
You can contact the core developers of Skyline at our **[Discord](https://discord.gg/XnbXNQM)**. If you have any questions, feel free to ask. It's also a good place to just keep up with the emulator, as most talk regarding development goes on over there.
### Credit
[<img align="left" height="10%" width="10%" src="https://avatars1.githubusercontent.com/u/39036280?v=4"/>](https://ryujinx.org/)
[**Ryujinx**](https://ryujinx.org/)<br>
We've used Ryujinx throughout the project for reference, the amount of accuracy of their HLE kernel implementation is what makes them such an amazing reference. In addition, the team behind the project has been really helpful with any queries we had.
---
[<img align="left" height="10%" width="10%" src="https://avatars3.githubusercontent.com/u/35075882?v=4"/>](https://yuzu-emu.org/)
[**Team yuzu**](https://github.com/yuzu-emu/)<br>
We have recieved a fair share of advice from the team behind [yuzu](https://yuzu-emu.org/). Just like the Ryujinx team, they've been really receptive and helpful for any queries we've had and have provided feedback on our code which was extremely useful during the early stages of the emulator.
### Special Thanks
A few noteworthy teams/projects who've helped us along the way are:
* **[Ryujinx](https://ryujinx.org/):** We've used Ryujinx for reference throughout the project, the amount of accuracy of their HLE kernel implementation is what makes them such an amazing reference. The team behind the project has been really helpful with any queries we've had. **It should be noted that Skyline is not based on Ryujinx**.
[<img align="left" height="10%" width="10%" src="https://avatars3.githubusercontent.com/u/31827450?v=4"/>](https://switchbrew.org/)
[**Switchbrew**](https://github.com/switchbrew/)<br>
We've extensively used Switchbrew whether that be their [wiki](https://switchbrew.org/) with it's collosal amount of information on the Switch that has saved us countless hours of time or [libnx](https://github.com/switchbrew/libnx) which was crucial to initial development of the emulator to ensure that our implementations were correct.
* **[Switchbrew](https://github.com/switchbrew/):** We've extensively used Switchbrew whether that be their **[wiki](https://switchbrew.org/)** with it's colossal amount of information on the Switch that has saved us countless hours of time or **[libnx](https://github.com/switchbrew/libnx)** which was crucial to initial development of the emulator to ensure that our HLE kernel and sysmodule implementations were accurate.
* **[Atmosphère](https://github.com/Atmosphere-NX/Atmosphere):** We've used [libmesosphere](https://github.com/Atmosphere-NX/Atmosphere/tree/master/libraries/libmesosphere) as another reference for our HLE kernel, it's faithfulness to the HOS kernel helps us a lot. This makes it invaluably important for us as a tool for writing code that can accurately emulate HOS behavior without missing any crucial parts.
---
### Disclaimer
* Nintendo Switch is a trademark of Nintendo Co., Ltd.
* Android is a trademark of Google LLC.
* **Nintendo Switch** is a trademark of **Nintendo Co., Ltd**
* **Android** is a trademark of **Google LLC**

View file

@ -17,7 +17,9 @@ if (uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE")
endif ()
add_subdirectory("libraries/fmt")
add_subdirectory("libraries/tzcode")
target_compile_options(tzcode PRIVATE -Wno-everything)
add_subdirectory("libraries/oboe")
include_directories("libraries/oboe/include")
@ -32,6 +34,11 @@ include_directories("libraries/frozen/include")
find_package(mbedtls REQUIRED CONFIG)
# Perfetto SDK
include_directories(libraries/perfetto/sdk)
add_library(perfetto STATIC libraries/perfetto/sdk/perfetto.cc)
target_compile_options(perfetto PRIVATE -Wno-everything)
include_directories(${source_DIR}/skyline)
add_library(skyline SHARED
@ -41,6 +48,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/common/settings.cpp
${source_DIR}/skyline/common/signal.cpp
${source_DIR}/skyline/common/uuid.cpp
${source_DIR}/skyline/common/trace.cpp
${source_DIR}/skyline/nce/guest.S
${source_DIR}/skyline/nce.cpp
${source_DIR}/skyline/jvm.cpp
@ -180,5 +188,5 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/prepo/IPrepoService.cpp
)
# target_precompile_headers(skyline PRIVATE ${source_DIR}/skyline/common.h) # PCH will currently break Intellisense
target_link_libraries(skyline vulkan android fmt lz4_static tzcode oboe mbedtls::mbedcrypto)
target_link_libraries(skyline vulkan android perfetto fmt lz4_static tzcode oboe mbedtls::mbedcrypto)
target_compile_options(skyline PRIVATE -Wall -Wno-unknown-attributes -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c99-designator -Wno-reorder -Wno-missing-braces -Wno-unused-variable -Wno-unused-private-field)

@ -0,0 +1 @@
Subproject commit 3f02be823cef0f54e720c0382ffc4507f48e6e4b

View file

@ -10,6 +10,7 @@
#include "skyline/common.h"
#include "skyline/common/signal.h"
#include "skyline/common/settings.h"
#include "skyline/common/trace.h"
#include "skyline/loader/loader.h"
#include "skyline/vfs/android_asset_filesystem.h"
#include "skyline/os.h"
@ -64,6 +65,12 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
auto start{std::chrono::steady_clock::now()};
// Initialize tracing
perfetto::TracingInitArgs args;
args.backends |= perfetto::kSystemBackend;
perfetto::Tracing::Initialize(args);
perfetto::TrackEvent::Register();
try {
auto os{std::make_shared<skyline::kernel::OS>(jvmManager, logger, settings, std::string(appFilesPath), GetTimeZoneName(), std::make_shared<skyline::vfs::AndroidAssetFileSystem>(AAssetManager_fromJava(env, assetManager)))};
OsWeak = os;
@ -73,24 +80,26 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
auto romUri{env->GetStringUTFChars(romUriJstring, nullptr)};
logger->Info("Launching ROM {}", romUri);
logger->InfoNoPrefix("Launching ROM {}", romUri);
env->ReleaseStringUTFChars(romUriJstring, romUri);
os->Execute(romFd, static_cast<skyline::loader::RomFormat>(romType));
} catch (std::exception &e) {
logger->Error(e.what());
logger->Error("An exception has occurred: {}", e.what());
} catch (const skyline::signal::SignalException &e) {
logger->Error(e.what());
logger->Error("An exception has occurred: {}", e.what());
} catch (...) {
logger->Error("An unknown exception has occurred");
}
perfetto::TrackEvent::Flush();
InputWeak.reset();
logger->Info("Emulation has ended");
logger->InfoNoPrefix("Emulation has ended");
auto end{std::chrono::steady_clock::now()};
logger->Info("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
logger->InfoNoPrefix("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
close(romFd);
}

View file

@ -11,6 +11,8 @@ namespace skyline::audio {
builder.setFramesPerCallback(constant::MixBufferSize);
builder.setUsage(oboe::Usage::Game);
builder.setCallback(this);
builder.setSharingMode(oboe::SharingMode::Exclusive);
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
builder.openManagedStream(outputStream);
outputStream->requestStart();

View file

@ -35,7 +35,7 @@ namespace skyline {
void Logger::WriteHeader(const std::string &str) {
__android_log_write(ANDROID_LOG_INFO, "emu-cpp", str.c_str());
std::lock_guard guard(mtx);
std::lock_guard guard(mutex);
logFile << "\0360\035" << str << '\n';
}
@ -48,7 +48,7 @@ namespace skyline {
__android_log_write(levelAlog[static_cast<u8>(level)], logTag.c_str(), str.c_str());
std::lock_guard guard(mtx);
std::lock_guard guard(mutex);
logFile << "\0361\035" << levelCharacter[static_cast<u8>(level)] << '\035' << threadName << '\035' << str << '\n'; // We use RS (\036) and GS (\035) as our delimiters
}

View file

@ -145,6 +145,14 @@ namespace skyline {
else
return object;
}
/**
* @brief {fmt}::format but with FmtCast built into it
*/
template<typename S, typename... Args>
auto Format(S formatString, Args &&... args) {
return fmt::format(formatString, FmtCast(args)...);
}
}
/**
@ -152,10 +160,6 @@ namespace skyline {
*/
class exception : public std::runtime_error {
public:
/**
* @param formatStr The exception string to be written, with {fmt} formatting
* @param args The arguments based on format_str
*/
template<typename S, typename... Args>
exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(formatStr, util::FmtCast(args)...)) {}
};
@ -451,7 +455,7 @@ namespace skyline {
class Logger {
private:
std::ofstream logFile; //!< An output stream to the log file
std::mutex mtx; //!< A mutex to lock before logging anything
std::mutex mutex; //!< Synchronizes all output I/O to ensure there are no races
public:
enum class LogLevel {
@ -487,34 +491,111 @@ namespace skyline {
void Write(LogLevel level, const std::string &str);
template<typename S, typename... Args>
void Error(const S &formatStr, Args &&... args) {
/**
* @brief A wrapper around a string which captures the calling function using Clang source location builtins
* @note A function needs to be declared for every argument template specialization as CTAD cannot work with implicit casting
* @url https://clang.llvm.org/docs/LanguageExtensions.html#source-location-builtins
*/
template<typename S>
struct FunctionString {
S string;
const char *function;
FunctionString(S string, const char *function = __builtin_FUNCTION()) : string(std::move(string)), function(function) {}
std::string operator*() {
return std::string(function) + ": " + std::string(string);
}
};
template<typename... Args>
void Error(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Error <= configLevel)
Write(LogLevel::Error, fmt::format(formatStr, util::FmtCast(args)...));
Write(LogLevel::Error, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Error(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Error <= configLevel)
Write(LogLevel::Error, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename S, typename... Args>
void Warn(const S &formatStr, Args &&... args) {
void ErrorNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Error <= configLevel)
Write(LogLevel::Error, fmt::format(formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Warn(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Warn <= configLevel)
Write(LogLevel::Warn, fmt::format(formatStr, util::FmtCast(args)...));
Write(LogLevel::Warn, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Warn(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Warn <= configLevel)
Write(LogLevel::Warn, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename S, typename... Args>
void Info(const S &formatStr, Args &&... args) {
void WarnNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Warn <= configLevel)
Write(LogLevel::Warn, fmt::format(formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Info(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Info <= configLevel)
Write(LogLevel::Info, fmt::format(formatStr, util::FmtCast(args)...));
Write(LogLevel::Info, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Info(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Info <= configLevel)
Write(LogLevel::Info, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename S, typename... Args>
void Debug(const S &formatStr, Args &&... args) {
void InfoNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Info <= configLevel)
Write(LogLevel::Info, fmt::format(formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Debug(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Debug <= configLevel)
Write(LogLevel::Debug, fmt::format(formatStr, util::FmtCast(args)...));
Write(LogLevel::Debug, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Debug(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Debug <= configLevel)
Write(LogLevel::Debug, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename S, typename... Args>
void Verbose(const S &formatStr, Args &&... args) {
void DebugNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Debug <= configLevel)
Write(LogLevel::Debug, fmt::format(formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Verbose(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Verbose <= configLevel)
Write(LogLevel::Verbose, fmt::format(formatStr, util::FmtCast(args)...));
Write(LogLevel::Verbose, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Verbose(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Verbose <= configLevel)
Write(LogLevel::Verbose, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename S, typename... Args>
void VerboseNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Verbose <= configLevel)
Write(LogLevel::Verbose, fmt::format(formatString, util::FmtCast(args)...));
}
};

View file

@ -0,0 +1,3 @@
#include "trace.h"
PERFETTO_TRACK_EVENT_STATIC_STORAGE(); //!< Expands into a structure with static storage for all track events

View file

@ -0,0 +1,26 @@
#pragma once
#include <limits>
#include <perfetto.h>
#include <common.h>
#define TRACE_EVENT_FMT(category, formatString, ...) TRACE_EVENT("kernel", nullptr, [&](perfetto::EventContext ctx) { \
ctx.event()->set_name(skyline::util::Format(formatString, __VA_ARGS__)); \
})
PERFETTO_DEFINE_CATEGORIES(
perfetto::Category("scheduler").SetDescription("Events from the HLE scheduler"),
perfetto::Category("kernel").SetDescription("Events from parts of the HLE kernel"),
perfetto::Category("guest").SetDescription("Events relating to guest code"),
perfetto::Category("gpu").SetDescription("Events from the emulated GPU"),
perfetto::Category("service").SetDescription("Events from the HLE sysmodule implementations")
);
namespace skyline::trace {
/**
* @brief Perfetto track IDs for custom tracks, counting down from U64 max to avoid conflicts
*/
enum class TrackIds : u64 {
Presentation = std::numeric_limits<u64>::max(),
};
}

View file

@ -9,7 +9,11 @@ extern skyline::u16 Fps;
extern skyline::u32 FrameTime;
namespace skyline::gpu {
PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state, true)), bufferEvent(std::make_shared<kernel::type::KEvent>(state, true)) {}
PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state, true)), bufferEvent(std::make_shared<kernel::type::KEvent>(state, true)), presentationTrack(static_cast<uint64_t>(trace::TrackIds::Presentation), perfetto::ProcessTrack::Current()) {
auto desc{presentationTrack.Serialize()};
desc.set_name("Presentation");
perfetto::TrackEvent::SetTrackDescriptor(presentationTrack, desc);
}
PresentationEngine::~PresentationEngine() {
if (window)
@ -76,6 +80,8 @@ namespace skyline::gpu {
FrameTime = static_cast<u32>((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals
Fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp));
TRACE_EVENT_INSTANT("gpu", "Present", presentationTrack, "FrameTimeNs", now - frameTimestamp, "Fps", Fps);
frameTimestamp = now;
} else {
frameTimestamp = util::GetTimeNs();

View file

@ -3,6 +3,7 @@
#pragma once
#include <common/trace.h>
#include <kernel/types/KEvent.h>
#include "texture.h"
@ -16,6 +17,7 @@ namespace skyline::gpu {
std::condition_variable windowConditional;
jobject surface{}; //!< The Surface object backing the ANativeWindow
u64 frameTimestamp{}; //!< The timestamp of the last frame being shown
perfetto::Track presentationTrack; //!< Perfetto track used for presentation events
public:
texture::Dimensions resolution{};

View file

@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <common/trace.h>
#include <android/native_window.h>
#include <kernel/types/KProcess.h>
#include <unistd.h>
@ -22,6 +23,7 @@ namespace skyline::gpu {
}
void Texture::SynchronizeHost() {
TRACE_EVENT("gpu", "Texture::SynchronizeHost");
auto pointer{guest->pointer};
auto size{format.GetSize(dimensions)};
backing.resize(size);

View file

@ -3,6 +3,7 @@
#include <unistd.h>
#include <common/signal.h>
#include <common/trace.h>
#include "types/KThread.h"
#include "scheduler.h"
@ -13,12 +14,14 @@ namespace skyline::kernel {
void Scheduler::SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls) {
if (*tls) {
TRACE_EVENT_END("guest");
const auto &state{*reinterpret_cast<nce::ThreadContext *>(*tls)->state};
if (signal == PreemptionSignal)
state.thread->isPreempted = false;
state.scheduler->Rotate(false);
YieldPending = false;
state.scheduler->WaitSchedule();
TRACE_EVENT_BEGIN("guest", "Guest");
} else {
YieldPending = true;
}
@ -150,6 +153,7 @@ namespace skyline::kernel {
return !core->queue.empty() && core->queue.front() == thread;
}};
TRACE_EVENT("scheduler", "WaitSchedule");
if (loadBalance && thread->affinityMask.count() > 1) {
std::chrono::milliseconds loadBalanceThreshold{PreemptiveTimeslice * 2}; //!< The amount of time that needs to pass unscheduled for a thread to attempt load balancing
while (!thread->scheduleCondition.wait_for(lock, loadBalanceThreshold, wakeFunction)) {
@ -177,6 +181,7 @@ namespace skyline::kernel {
auto &thread{state.thread};
auto *core{&cores.at(thread->coreId)};
TRACE_EVENT("scheduler", "TimedWaitSchedule");
std::unique_lock lock(core->mutex);
if (thread->scheduleCondition.wait_for(lock, timeout, [&]() {
if (!thread->affinityMask.test(thread->coreId)) [[unlikely]] {
@ -201,6 +206,7 @@ namespace skyline::kernel {
auto &core{cores.at(thread->coreId)};
std::unique_lock lock(core.mutex);
if (core.queue.front() == thread) {
// If this thread is at the front of the thread queue then we need to rotate the thread
// In the case where this thread was forcefully yielded, we don't need to do this as it's done by the thread which yielded to this thread

View file

@ -3,6 +3,7 @@
#include <os.h>
#include <kernel/types/KProcess.h>
#include <common/trace.h>
#include <vfs/npdm.h>
#include "results.h"
#include "svc.h"
@ -15,7 +16,7 @@ namespace skyline::kernel::svc {
state.ctx->gpr.w0 = result::InvalidSize;
state.ctx->gpr.x1 = 0;
state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size);
state.logger->Warn("'size' not divisible by 2MB: {}", size);
return;
}
@ -25,21 +26,21 @@ namespace skyline::kernel::svc {
state.ctx->gpr.w0 = Result{};
state.ctx->gpr.x1 = reinterpret_cast<u64>(heap->ptr);
state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} - 0x{:X} (0x{:X} bytes)", heap->ptr, heap->ptr + heap->size, heap->size);
state.logger->Debug("Allocated at 0x{:X} - 0x{:X} (0x{:X} bytes)", heap->ptr, heap->ptr + heap->size, heap->size);
}
void SetMemoryAttribute(const DeviceState &state) {
auto pointer{reinterpret_cast<u8 *>(state.ctx->gpr.x0)};
if (!util::PageAligned(pointer)) {
state.ctx->gpr.w0 = result::InvalidAddress;
state.logger->Warn("svcSetMemoryAttribute: 'pointer' not page aligned: 0x{:X}", pointer);
state.logger->Warn("'pointer' not page aligned: 0x{:X}", pointer);
return;
}
size_t size{state.ctx->gpr.x1};
if (!util::PageAligned(size)) {
state.ctx->gpr.w0 = result::InvalidSize;
state.logger->Warn("svcSetMemoryAttribute: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
state.logger->Warn("'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return;
}
@ -49,20 +50,20 @@ namespace skyline::kernel::svc {
auto maskedValue{mask.value | value.value};
if (maskedValue != mask.value || !mask.isUncached || mask.isDeviceShared || mask.isBorrowed || mask.isIpcLocked) {
state.ctx->gpr.w0 = result::InvalidCombination;
state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask.value, value.value);
state.logger->Warn("'mask' invalid: 0x{:X}, 0x{:X}", mask.value, value.value);
return;
}
auto chunk{state.process->memory.Get(pointer)};
if (!chunk) {
state.ctx->gpr.w0 = result::InvalidAddress;
state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", pointer);
state.logger->Warn("Cannot find memory region: 0x{:X}", pointer);
return;
}
if (!chunk->state.attributeChangeAllowed) {
state.ctx->gpr.w0 = result::InvalidState;
state.logger->Warn("svcSetMemoryAttribute: Attribute change not allowed for chunk: 0x{:X}", pointer);
state.logger->Warn("Attribute change not allowed for chunk: 0x{:X}", pointer);
return;
}
@ -72,7 +73,7 @@ namespace skyline::kernel::svc {
newChunk.attributes.isUncached = value.isUncached;
state.process->memory.InsertChunk(newChunk);
state.logger->Debug("svcSetMemoryAttribute: Set CPU caching to {} at 0x{:X} - 0x{:X} (0x{:X} bytes)", !static_cast<bool>(value.isUncached), pointer, pointer + size, size);
state.logger->Debug("Set CPU caching to {} at 0x{:X} - 0x{:X} (0x{:X} bytes)", !static_cast<bool>(value.isUncached), pointer, pointer + size, size);
state.ctx->gpr.w0 = Result{};
}
@ -83,32 +84,32 @@ namespace skyline::kernel::svc {
if (!util::PageAligned(destination) || !util::PageAligned(source)) {
state.ctx->gpr.w0 = result::InvalidAddress;
state.logger->Warn("svcMapMemory: Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
state.logger->Warn("Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return;
}
if (!util::PageAligned(size)) {
state.ctx->gpr.w0 = result::InvalidSize;
state.logger->Warn("svcMapMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
state.logger->Warn("'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return;
}
auto stack{state.process->memory.stack};
if (!stack.IsInside(destination)) {
state.ctx->gpr.w0 = result::InvalidMemoryRegion;
state.logger->Warn("svcMapMemory: Destination not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
state.logger->Warn("Destination not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return;
}
auto chunk{state.process->memory.Get(source)};
if (!chunk) {
state.ctx->gpr.w0 = result::InvalidAddress;
state.logger->Warn("svcMapMemory: Source has no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
state.logger->Warn("Source has no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return;
}
if (!chunk->state.mapAllowed) {
state.ctx->gpr.w0 = result::InvalidState;
state.logger->Warn("svcMapMemory: Source doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X}, Size: 0x{:X}, MemoryState: 0x{:X}", source, destination, size, chunk->state.value);
state.logger->Warn("Source doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X}, Size: 0x{:X}, MemoryState: 0x{:X}", source, destination, size, chunk->state.value);
return;
}
@ -120,7 +121,7 @@ namespace skyline::kernel::svc {
throw exception("svcMapMemory: Cannot find memory object in handle table for address 0x{:X}", source);
object->item->UpdatePermission(source, size, {false, false, false});
state.logger->Debug("svcMapMemory: Mapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size);
state.logger->Debug("Mapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size);
state.ctx->gpr.w0 = Result{};
}
@ -131,20 +132,20 @@ namespace skyline::kernel::svc {
if (!util::PageAligned(destination) || !util::PageAligned(source)) {
state.ctx->gpr.w0 = result::InvalidAddress;
state.logger->Warn("svcUnmapMemory: Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
state.logger->Warn("Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return;
}
if (!util::PageAligned(size)) {
state.ctx->gpr.w0 = result::InvalidSize;
state.logger->Warn("svcUnmapMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
state.logger->Warn("'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return;
}
auto stack{state.process->memory.stack};
if (!stack.IsInside(source)) {
state.ctx->gpr.w0 = result::InvalidMemoryRegion;
state.logger->Warn("svcUnmapMemory: Source not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
state.logger->Warn("Source not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return;
}
@ -152,13 +153,13 @@ namespace skyline::kernel::svc {
auto destChunk{state.process->memory.Get(destination)};
if (!sourceChunk || !destChunk) {
state.ctx->gpr.w0 = result::InvalidAddress;
state.logger->Warn("svcUnmapMemory: Addresses have no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
state.logger->Warn("Addresses have no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size);
return;
}
if (!destChunk->state.mapAllowed) {
state.ctx->gpr.w0 = result::InvalidState;
state.logger->Warn("svcUnmapMemory: Destination doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, destChunk->state.value);
state.logger->Warn("Destination doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, destChunk->state.value);
return;
}
@ -176,7 +177,7 @@ namespace skyline::kernel::svc {
state.process->CloseHandle(sourceObject->handle);
state.logger->Debug("svcUnmapMemory: Unmapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size);
state.logger->Debug("Unmapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size);
state.ctx->gpr.w0 = Result{};
}
@ -197,7 +198,7 @@ namespace skyline::kernel::svc {
.ipcRefCount = 0,
};
state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Region Start: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", pointer, memInfo.address, memInfo.size, memInfo.type, static_cast<bool>(chunk->attributes.isUncached), chunk->permission.r ? 'R' : '-', chunk->permission.w ? 'W' : '-', chunk->permission.x ? 'X' : '-');
state.logger->Debug("Address: 0x{:X}, Region Start: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", pointer, memInfo.address, memInfo.size, memInfo.type, static_cast<bool>(chunk->attributes.isUncached), chunk->permission.r ? 'R' : '-', chunk->permission.w ? 'W' : '-', chunk->permission.x ? 'X' : '-');
} else {
auto addressSpaceEnd{reinterpret_cast<u64>(state.process->memory.addressSpace.address + state.process->memory.addressSpace.size)};
@ -207,7 +208,7 @@ namespace skyline::kernel::svc {
.type = static_cast<u32>(memory::MemoryType::Reserved),
};
state.logger->Debug("svcQueryMemory: Trying to query memory outside of the application's address space: 0x{:X}", pointer);
state.logger->Debug("Trying to query memory outside of the application's address space: 0x{:X}", pointer);
}
*reinterpret_cast<memory::MemoryInfo *>(state.ctx->gpr.x0) = memInfo;
@ -216,7 +217,7 @@ namespace skyline::kernel::svc {
}
void ExitProcess(const DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting process");
state.logger->Debug("Exiting process");
if (state.thread->id)
state.process->Kill(false);
std::longjmp(state.thread->originalCtx, true);
@ -236,13 +237,13 @@ namespace skyline::kernel::svc {
idealCore = (idealCore == IdealCoreUseProcessValue) ? state.process->npdm.meta.idealCore : idealCore;
if (idealCore < 0 || idealCore >= constant::CoreCount) {
state.ctx->gpr.w0 = result::InvalidCoreId;
state.logger->Warn("svcCreateThread: 'idealCore' invalid: {}", idealCore);
state.logger->Warn("'idealCore' invalid: {}", idealCore);
return;
}
if (!state.process->npdm.threadInfo.priority.Valid(priority)) {
state.ctx->gpr.w0 = result::InvalidPriority;
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
state.logger->Warn("'priority' invalid: {}", priority);
return;
}
@ -252,12 +253,12 @@ namespace skyline::kernel::svc {
auto thread{state.process->CreateThread(entry, entryArgument, stackTop, priority, idealCore)};
if (thread) {
state.logger->Debug("svcCreateThread: Created thread #{} with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, Ideal Core: {})", thread->id, thread->handle, entry, entryArgument, stackTop, priority, idealCore);
state.logger->Debug("Created thread #{} with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, Ideal Core: {})", thread->id, thread->handle, entry, entryArgument, stackTop, priority, idealCore);
state.ctx->gpr.w1 = thread->handle;
state.ctx->gpr.w0 = Result{};
} else {
state.logger->Debug("svcCreateThread: Cannot create thread (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, Ideal Core: {})", entry, entryArgument, stackTop, priority, idealCore);
state.logger->Debug("Cannot create thread (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, Ideal Core: {})", entry, entryArgument, stackTop, priority, idealCore);
state.ctx->gpr.w1 = 0;
state.ctx->gpr.w0 = result::OutOfResource;
}
@ -267,17 +268,17 @@ namespace skyline::kernel::svc {
KHandle handle{state.ctx->gpr.w0};
try {
auto thread{state.process->GetHandle<type::KThread>(handle)};
state.logger->Debug("svcStartThread: Starting thread #{}: 0x{:X}", thread->id, handle);
state.logger->Debug("Starting thread #{}: 0x{:X}", thread->id, handle);
thread->Start();
state.ctx->gpr.w0 = Result{};
} catch (const std::out_of_range &) {
state.logger->Warn("svcStartThread: 'handle' invalid: 0x{:X}", handle);
state.logger->Warn("'handle' invalid: 0x{:X}", handle);
state.ctx->gpr.w0 = result::InvalidHandle;
}
}
void ExitThread(const DeviceState &state) {
state.logger->Debug("svcExitThread: Exiting current thread");
state.logger->Debug("Exiting current thread");
std::longjmp(state.thread->originalCtx, true);
}
@ -288,7 +289,8 @@ namespace skyline::kernel::svc {
i64 in{static_cast<i64>(state.ctx->gpr.x0)};
if (in > 0) {
state.logger->Debug("svcSleepThread: Sleeping for {}ns", in);
state.logger->Debug("Sleeping for {}ns", in);
TRACE_EVENT("kernel", "SleepThread", "duration", in);
struct timespec spec{
.tv_sec = static_cast<time_t>(in / 1000000000),
@ -299,22 +301,30 @@ namespace skyline::kernel::svc {
nanosleep(&spec, nullptr);
} else {
switch (in) {
case yieldWithCoreMigration:
state.logger->Debug("svcSleepThread: Waking any appropriate parked threads and yielding");
case yieldWithCoreMigration: {
state.logger->Debug("Waking any appropriate parked threads and yielding");
TRACE_EVENT("kernel", "YieldWithCoreMigration");
state.scheduler->WakeParkedThread();
[[fallthrough]];
case yieldWithoutCoreMigration:
if (in == yieldWithoutCoreMigration)
state.logger->Debug("svcSleepThread: Cooperative yield");
state.scheduler->Rotate();
state.scheduler->WaitSchedule();
break;
}
case yieldToAnyThread:
state.logger->Debug("svcSleepThread: Parking current thread");
case yieldWithoutCoreMigration: {
state.logger->Debug("Cooperative yield");
TRACE_EVENT("kernel", "YieldWithoutCoreMigration");
state.scheduler->Rotate();
state.scheduler->WaitSchedule();
break;
}
case yieldToAnyThread: {
state.logger->Debug("Parking current thread");
TRACE_EVENT("kernel", "YieldToAnyThread");
state.scheduler->ParkThread();
state.scheduler->WaitSchedule(false);
break;
}
default:
break;
@ -327,12 +337,12 @@ namespace skyline::kernel::svc {
try {
auto thread{state.process->GetHandle<type::KThread>(handle)};
u8 priority{thread->priority};
state.logger->Debug("svcGetThreadPriority: Retrieving thread #{}'s priority: {}", thread->id, priority);
state.logger->Debug("Retrieving thread #{}'s priority: {}", thread->id, priority);
state.ctx->gpr.w1 = priority;
state.ctx->gpr.w0 = Result{};
} catch (const std::out_of_range &) {
state.logger->Warn("svcGetThreadPriority: 'handle' invalid: 0x{:X}", handle);
state.logger->Warn("'handle' invalid: 0x{:X}", handle);
state.ctx->gpr.w0 = result::InvalidHandle;
}
}
@ -341,13 +351,13 @@ namespace skyline::kernel::svc {
KHandle handle{state.ctx->gpr.w0};
u8 priority{static_cast<u8>(state.ctx->gpr.w1)};
if (!state.process->npdm.threadInfo.priority.Valid(priority)) {
state.logger->Warn("svcSetThreadPriority: 'priority' invalid: 0x{:X}", priority);
state.logger->Warn("'priority' invalid: 0x{:X}", priority);
state.ctx->gpr.w0 = result::InvalidPriority;
return;
}
try {
auto thread{state.process->GetHandle<type::KThread>(handle)};
state.logger->Debug("svcSetThreadPriority: Setting thread #{}'s priority to {}", thread->id, priority);
state.logger->Debug("Setting thread #{}'s priority to {}", thread->id, priority);
if (thread->priority != priority) {
thread->basePriority = priority;
u8 newPriority{};
@ -362,7 +372,7 @@ namespace skyline::kernel::svc {
}
state.ctx->gpr.w0 = Result{};
} catch (const std::out_of_range &) {
state.logger->Warn("svcSetThreadPriority: 'handle' invalid: 0x{:X}", handle);
state.logger->Warn("'handle' invalid: 0x{:X}", handle);
state.ctx->gpr.w0 = result::InvalidHandle;
}
}
@ -373,13 +383,13 @@ namespace skyline::kernel::svc {
auto thread{state.process->GetHandle<type::KThread>(handle)};
auto idealCore{thread->idealCore};
auto affinityMask{thread->affinityMask};
state.logger->Debug("svcGetThreadCoreMask: Getting thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask);
state.logger->Debug("Getting thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask);
state.ctx->gpr.x2 = affinityMask.to_ullong();
state.ctx->gpr.w1 = idealCore;
state.ctx->gpr.w0 = Result{};
} catch (const std::out_of_range &) {
state.logger->Warn("svcGetThreadCoreMask: 'handle' invalid: 0x{:X}", handle);
state.logger->Warn("'handle' invalid: 0x{:X}", handle);
state.ctx->gpr.w0 = result::InvalidHandle;
}
}
@ -402,25 +412,25 @@ namespace skyline::kernel::svc {
auto processMask{state.process->npdm.threadInfo.coreMask};
if ((processMask | affinityMask) != processMask) {
state.logger->Warn("svcSetThreadCoreMask: 'affinityMask' invalid: {} (Process Mask: {})", affinityMask, processMask);
state.logger->Warn("'affinityMask' invalid: {} (Process Mask: {})", affinityMask, processMask);
state.ctx->gpr.w0 = result::InvalidCoreId;
return;
}
if (affinityMask.none() || !affinityMask.test(idealCore)) {
state.logger->Warn("svcSetThreadCoreMask: 'affinityMask' invalid: {} (Ideal Core: {})", affinityMask, idealCore);
state.logger->Warn("'affinityMask' invalid: {} (Ideal Core: {})", affinityMask, idealCore);
state.ctx->gpr.w0 = result::InvalidCombination;
return;
}
state.logger->Debug("svcSetThreadCoreMask: Setting thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask);
state.logger->Debug("Setting thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask);
std::lock_guard guard(thread->coreMigrationMutex);
thread->idealCore = idealCore;
thread->affinityMask = affinityMask;
if (!affinityMask.test(thread->coreId) && thread->coreId != constant::ParkedCoreId) {
state.logger->Debug("svcSetThreadCoreMask: Migrating thread #{} to Ideal Core C{} -> C{}", thread->id, thread->coreId, idealCore);
state.logger->Debug("Migrating thread #{} to Ideal Core C{} -> C{}", thread->id, thread->coreId, idealCore);
if (thread == state.thread) {
state.scheduler->RemoveThread();
@ -436,7 +446,7 @@ namespace skyline::kernel::svc {
state.ctx->gpr.w0 = Result{};
} catch (const std::out_of_range &) {
state.logger->Warn("svcSetThreadCoreMask: 'handle' invalid: 0x{:X}", handle);
state.logger->Warn("'handle' invalid: 0x{:X}", handle);
state.ctx->gpr.w0 = result::InvalidHandle;
}
}
@ -444,18 +454,19 @@ namespace skyline::kernel::svc {
void GetCurrentProcessorNumber(const DeviceState &state) {
std::lock_guard guard(state.thread->coreMigrationMutex);
auto coreId{state.thread->coreId};
state.logger->Debug("svcGetCurrentProcessorNumber: C{}", coreId);
state.logger->Debug("C{}", coreId);
state.ctx->gpr.w0 = coreId;
}
void ClearEvent(const DeviceState &state) {
KHandle handle{state.ctx->gpr.w0};
TRACE_EVENT_FMT("kernel", "ClearEvent 0x{:X}", handle);
try {
std::static_pointer_cast<type::KEvent>(state.process->GetHandle(handle))->ResetSignal();
state.logger->Debug("svcClearEvent: Clearing 0x{:X}", handle);
state.logger->Debug("Clearing 0x{:X}", handle);
state.ctx->gpr.w0 = Result{};
} catch (const std::out_of_range &) {
state.logger->Warn("svcClearEvent: 'handle' invalid: 0x{:X}", handle);
state.logger->Warn("'handle' invalid: 0x{:X}", handle);
state.ctx->gpr.w0 = result::InvalidHandle;
return;
}
@ -468,31 +479,31 @@ namespace skyline::kernel::svc {
if (!util::PageAligned(pointer)) {
state.ctx->gpr.w0 = result::InvalidAddress;
state.logger->Warn("svcMapSharedMemory: 'pointer' not page aligned: 0x{:X}", pointer);
state.logger->Warn("'pointer' not page aligned: 0x{:X}", pointer);
return;
}
size_t size{state.ctx->gpr.x2};
if (!util::PageAligned(size)) {
state.ctx->gpr.w0 = result::InvalidSize;
state.logger->Warn("svcMapSharedMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
state.logger->Warn("'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return;
}
memory::Permission permission(state.ctx->gpr.w3);
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("'permission' invalid: {}{}{}", permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
state.ctx->gpr.w0 = result::InvalidNewMemoryPermission;
return;
}
state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} - 0x{:X} (0x{:X} bytes) ({}{}{})", pointer, pointer + size, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
state.logger->Debug("Mapping shared memory at 0x{:X} - 0x{:X} (0x{:X} bytes) ({}{}{})", pointer, pointer + size, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
object->Map(pointer, size, permission);
state.ctx->gpr.w0 = Result{};
} catch (const std::out_of_range &) {
state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", static_cast<u32>(state.ctx->gpr.w0));
state.logger->Warn("'handle' invalid: 0x{:X}", static_cast<u32>(state.ctx->gpr.w0));
state.ctx->gpr.w0 = result::InvalidHandle;
}
}
@ -501,26 +512,26 @@ namespace skyline::kernel::svc {
auto pointer{reinterpret_cast<u8 *>(state.ctx->gpr.x1)};
if (!util::PageAligned(pointer)) {
state.ctx->gpr.w0 = result::InvalidAddress;
state.logger->Warn("svcCreateTransferMemory: 'pointer' not page aligned: 0x{:X}", pointer);
state.logger->Warn("'pointer' not page aligned: 0x{:X}", pointer);
return;
}
size_t size{state.ctx->gpr.x2};
if (!util::PageAligned(size)) {
state.ctx->gpr.w0 = result::InvalidSize;
state.logger->Warn("svcCreateTransferMemory: 'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
state.logger->Warn("'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size);
return;
}
memory::Permission permission(state.ctx->gpr.w3);
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("'permission' invalid: {}{}{}", permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
state.ctx->gpr.w0 = result::InvalidNewMemoryPermission;
return;
}
auto tmem{state.process->NewHandle<type::KTransferMemory>(pointer, size, permission)};
state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} - 0x{:X} (0x{:X} bytes) ({}{}{})", pointer, pointer + size, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
state.logger->Debug("Creating transfer memory at 0x{:X} - 0x{:X} (0x{:X} bytes) ({}{}{})", pointer, pointer + size, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
state.ctx->gpr.w0 = Result{};
state.ctx->gpr.w1 = tmem.handle;
@ -530,16 +541,17 @@ namespace skyline::kernel::svc {
KHandle handle{static_cast<KHandle>(state.ctx->gpr.w0)};
try {
state.process->CloseHandle(handle);
state.logger->Debug("svcCloseHandle: Closing 0x{:X}", handle);
state.logger->Debug("Closing 0x{:X}", handle);
state.ctx->gpr.w0 = Result{};
} catch (const std::out_of_range &) {
state.logger->Warn("svcCloseHandle: 'handle' invalid: 0x{:X}", handle);
state.logger->Warn("'handle' invalid: 0x{:X}", handle);
state.ctx->gpr.w0 = result::InvalidHandle;
}
}
void ResetSignal(const DeviceState &state) {
KHandle handle{state.ctx->gpr.w0};
TRACE_EVENT_FMT("kernel", "ResetSignal 0x{:X}", handle);
try {
auto object{state.process->GetHandle(handle)};
switch (object->objectType) {
@ -549,16 +561,16 @@ namespace skyline::kernel::svc {
break;
default: {
state.logger->Warn("svcResetSignal: 'handle' type invalid: 0x{:X} ({})", handle, object->objectType);
state.logger->Warn("'handle' type invalid: 0x{:X} ({})", handle, object->objectType);
state.ctx->gpr.w0 = result::InvalidHandle;
return;
}
}
state.logger->Debug("svcResetSignal: Resetting 0x{:X}", handle);
state.logger->Debug("Resetting 0x{:X}", handle);
state.ctx->gpr.w0 = Result{};
} catch (const std::out_of_range &) {
state.logger->Warn("svcResetSignal: 'handle' invalid: 0x{:X}", handle);
state.logger->Warn("'handle' invalid: 0x{:X}", handle);
state.ctx->gpr.w0 = result::InvalidHandle;
return;
}
@ -588,7 +600,7 @@ namespace skyline::kernel::svc {
break;
default: {
state.logger->Debug("svcWaitSynchronization: An invalid handle was supplied: 0x{:X}", handle);
state.logger->Debug("An invalid handle was supplied: 0x{:X}", handle);
state.ctx->gpr.w0 = result::InvalidHandle;
return;
}
@ -597,12 +609,14 @@ namespace skyline::kernel::svc {
i64 timeout{static_cast<i64>(state.ctx->gpr.x3)};
if (waitHandles.size() == 1) {
state.logger->Debug("svcWaitSynchronization: Waiting on 0x{:X} for {}ns", waitHandles[0], timeout);
state.logger->Debug("Waiting on 0x{:X} for {}ns", waitHandles[0], timeout);
TRACE_EVENT_FMT("kernel", "WaitSynchronization 0x{:X}", waitHandles[0]);
} else if (Logger::LogLevel::Debug <= state.logger->configLevel) {
std::string handleString;
for (const auto &handle : waitHandles)
handleString += fmt::format("* 0x{:X}\n", handle);
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: {}ns", handleString, timeout);
state.logger->Debug("Waiting on handles:\n{}Timeout: {}ns", handleString, timeout);
TRACE_EVENT("kernel", "WaitSynchronizationMultiple");
}
std::unique_lock lock(type::KSyncObject::syncObjectMutex);
@ -615,7 +629,7 @@ namespace skyline::kernel::svc {
u32 index{};
for (const auto &object : objectTable) {
if (object->signalled) {
state.logger->Debug("svcWaitSynchronization: Signalled 0x{:X}", waitHandles[index]);
state.logger->Debug("Signalled 0x{:X}", waitHandles[index]);
state.ctx->gpr.w0 = Result{};
state.ctx->gpr.w1 = index;
return;
@ -624,7 +638,7 @@ namespace skyline::kernel::svc {
}
if (timeout == 0) {
state.logger->Debug("svcWaitSynchronization: No handle is currently signalled");
state.logger->Debug("No handle is currently signalled");
state.ctx->gpr.w0 = result::TimedOut;
return;
}
@ -663,15 +677,15 @@ namespace skyline::kernel::svc {
}
if (wakeObject) {
state.logger->Debug("svcWaitSynchronization: Signalled 0x{:X}", waitHandles[wakeIndex]);
state.logger->Debug("Signalled 0x{:X}", waitHandles[wakeIndex]);
state.ctx->gpr.w0 = Result{};
state.ctx->gpr.w1 = wakeIndex;
} else if (state.thread->cancelSync) {
state.thread->cancelSync = false;
state.logger->Debug("svcWaitSynchronization: Wait has been cancelled");
state.logger->Debug("Wait has been cancelled");
state.ctx->gpr.w0 = result::Cancelled;
} else {
state.logger->Debug("svcWaitSynchronization: Wait has timed out");
state.logger->Debug("Wait has timed out");
state.ctx->gpr.w0 = result::TimedOut;
lock.unlock();
state.scheduler->InsertThread(state.thread);
@ -690,7 +704,7 @@ namespace skyline::kernel::svc {
}
state.ctx->gpr.w0 = Result{};
} catch (const std::out_of_range &) {
state.logger->Warn("svcCancelSynchronization: 'handle' invalid: 0x{:X}", static_cast<u32>(state.ctx->gpr.w0));
state.logger->Warn("'handle' invalid: 0x{:X}", static_cast<u32>(state.ctx->gpr.w0));
state.ctx->gpr.w0 = result::InvalidHandle;
}
}
@ -698,22 +712,23 @@ namespace skyline::kernel::svc {
void ArbitrateLock(const DeviceState &state) {
auto mutex{reinterpret_cast<u32 *>(state.ctx->gpr.x1)};
if (!util::WordAligned(mutex)) {
state.logger->Warn("svcArbitrateLock: 'mutex' not word aligned: 0x{:X}", mutex);
state.logger->Warn("'mutex' not word aligned: 0x{:X}", mutex);
state.ctx->gpr.w0 = result::InvalidAddress;
return;
}
state.logger->Debug("svcArbitrateLock: Locking 0x{:X}", mutex);
state.logger->Debug("Locking 0x{:X}", mutex);
TRACE_EVENT_FMT("kernel", "MutexLock 0x{:X}", mutex);
KHandle ownerHandle{state.ctx->gpr.w0};
KHandle requesterHandle{state.ctx->gpr.w2};
auto result{state.process->MutexLock(mutex, ownerHandle, requesterHandle)};
if (result == Result{})
state.logger->Debug("svcArbitrateLock: Locked 0x{:X}", mutex);
state.logger->Debug("Locked 0x{:X}", mutex);
else if (result == result::InvalidCurrentMemory)
result = Result{}; // If the mutex value isn't expected then it's still successful
else if (result == result::InvalidHandle)
state.logger->Warn("svcArbitrateLock: 'ownerHandle' invalid: 0x{:X} (0x{:X})", ownerHandle, mutex);
state.logger->Warn("'ownerHandle' invalid: 0x{:X} (0x{:X})", ownerHandle, mutex);
state.ctx->gpr.w0 = result;
}
@ -721,14 +736,16 @@ namespace skyline::kernel::svc {
void ArbitrateUnlock(const DeviceState &state) {
auto mutex{reinterpret_cast<u32 *>(state.ctx->gpr.x0)};
if (!util::WordAligned(mutex)) {
state.logger->Warn("svcArbitrateUnlock: 'mutex' not word aligned: 0x{:X}", mutex);
state.logger->Warn("'mutex' not word aligned: 0x{:X}", mutex);
state.ctx->gpr.w0 = result::InvalidAddress;
return;
}
state.logger->Debug("svcArbitrateUnlock: Unlocking 0x{:X}", mutex);
TRACE_EVENT_FMT("kernel", "MutexUnlock 0x{:X}", mutex);
state.logger->Debug("Unlocking 0x{:X}", mutex);
state.process->MutexUnlock(mutex);
state.logger->Debug("svcArbitrateUnlock: Unlocked 0x{:X}", mutex);
state.logger->Debug("Unlocked 0x{:X}", mutex);
state.ctx->gpr.w0 = Result{};
}
@ -736,7 +753,7 @@ namespace skyline::kernel::svc {
void WaitProcessWideKeyAtomic(const DeviceState &state) {
auto mutex{reinterpret_cast<u32 *>(state.ctx->gpr.x0)};
if (!util::WordAligned(mutex)) {
state.logger->Warn("svcWaitProcessWideKeyAtomic: 'mutex' not word aligned: 0x{:X}", mutex);
state.logger->Warn("'mutex' not word aligned: 0x{:X}", mutex);
state.ctx->gpr.w0 = result::InvalidAddress;
return;
}
@ -745,13 +762,13 @@ namespace skyline::kernel::svc {
KHandle requesterHandle{state.ctx->gpr.w2};
i64 timeout{static_cast<i64>(state.ctx->gpr.x3)};
state.logger->Debug("svcWaitProcessWideKeyAtomic: Waiting on 0x{:X} with 0x{:X} for {}ns", conditional, mutex, timeout);
state.logger->Debug("Waiting on 0x{:X} with 0x{:X} for {}ns", conditional, mutex, timeout);
auto result{state.process->ConditionalVariableWait(conditional, mutex, requesterHandle, timeout)};
if (result == Result{})
state.logger->Debug("svcWaitProcessWideKeyAtomic: Waited for 0x{:X} and reacquired 0x{:X}", conditional, mutex);
state.logger->Debug("Waited for 0x{:X} and reacquired 0x{:X}", conditional, mutex);
else if (result == result::TimedOut)
state.logger->Debug("svcWaitProcessWideKeyAtomic: Wait on 0x{:X} has timed out after {}ns", conditional, timeout);
state.logger->Debug("Wait on 0x{:X} has timed out after {}ns", conditional, timeout);
state.ctx->gpr.w0 = result;
}
@ -759,7 +776,7 @@ namespace skyline::kernel::svc {
auto conditional{reinterpret_cast<u32 *>(state.ctx->gpr.x0)};
i32 count{static_cast<i32>(state.ctx->gpr.w1)};
state.logger->Debug("svcSignalProcessWideKey: Signalling 0x{:X} for {} waiters", conditional, count);
state.logger->Debug("Signalling 0x{:X} for {} waiters", conditional, count);
state.process->ConditionalVariableSignal(conditional, count);
state.ctx->gpr.w0 = Result{};
}
@ -785,12 +802,12 @@ namespace skyline::kernel::svc {
if (port.compare("sm:") >= 0) {
handle = state.process->NewHandle<type::KSession>(std::static_pointer_cast<service::BaseService>(state.os->serviceManager.smUserInterface)).handle;
} else {
state.logger->Warn("svcConnectToNamedPort: Connecting to invalid port: '{}'", port);
state.logger->Warn("Connecting to invalid port: '{}'", port);
state.ctx->gpr.w0 = result::NotFound;
return;
}
state.logger->Debug("svcConnectToNamedPort: Connecting to port '{}' at 0x{:X}", port, handle);
state.logger->Debug("Connecting to port '{}' at 0x{:X}", port, handle);
state.ctx->gpr.w1 = handle;
state.ctx->gpr.w0 = Result{};
@ -806,7 +823,7 @@ namespace skyline::kernel::svc {
KHandle handle{state.ctx->gpr.w1};
size_t tid{state.process->GetHandle<type::KThread>(handle)->id};
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, TID: {}", handle, tid);
state.logger->Debug("Handle: 0x{:X}, TID: {}", handle, tid);
state.ctx->gpr.x1 = tid;
state.ctx->gpr.w0 = Result{};
@ -815,9 +832,9 @@ namespace skyline::kernel::svc {
void Break(const DeviceState &state) {
auto reason{state.ctx->gpr.x0};
if (reason & (1ULL << 31)) {
state.logger->Debug("svcBreak: Debugger is being engaged ({})", reason);
state.logger->Debug("Debugger is being engaged ({})", reason);
} else {
state.logger->Error("svcBreak: Exit Stack Trace ({}){}", reason, state.loader->GetStackTrace());
state.logger->Error("Exit Stack Trace ({}){}", reason, state.loader->GetStackTrace());
if (state.thread->id)
state.process->Kill(false);
std::longjmp(state.thread->originalCtx, true);
@ -825,12 +842,12 @@ namespace skyline::kernel::svc {
}
void OutputDebugString(const DeviceState &state) {
auto string{span(reinterpret_cast<u8 *>(state.ctx->gpr.x0), state.ctx->gpr.x1).as_string()};
auto string{span(reinterpret_cast<char *>(state.ctx->gpr.x0), state.ctx->gpr.x1).as_string()};
if (string.back() == '\n')
string.remove_suffix(1);
state.logger->Info("svcOutputDebugString: {}", string);
state.logger->Info("{}", string);
state.ctx->gpr.w0 = Result{};
}
@ -957,12 +974,12 @@ namespace skyline::kernel::svc {
break;
default:
state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", static_cast<u32>(info), id1);
state.logger->Warn("Unimplemented case ID0: {}, ID1: {}", static_cast<u32>(info), id1);
state.ctx->gpr.w0 = result::InvalidEnumValue;
return;
}
state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", static_cast<u32>(info), id1, out);
state.logger->Debug("ID0: {}, ID1: {}, Out: 0x{:X}", static_cast<u32>(info), id1, out);
state.ctx->gpr.x1 = out;
state.ctx->gpr.w0 = Result{};
@ -1017,7 +1034,7 @@ namespace skyline::kernel::svc {
if (memory) {
auto item{static_pointer_cast<type::KPrivateMemory>(memory->item)};
auto initialSize{item->size};
if (item->memState == memory::states::Heap) {
if (item->memoryState == memory::states::Heap) {
if (item->ptr >= pointer) {
if (item->size <= size) {
item->Resize(0);
@ -1044,7 +1061,7 @@ namespace skyline::kernel::svc {
void WaitForAddress(const DeviceState &state) {
auto address{reinterpret_cast<u32 *>(state.ctx->gpr.x0)};
if (!util::WordAligned(address)) [[unlikely]] {
state.logger->Warn("svcWaitForAddress: 'address' not word aligned: 0x{:X}", address);
state.logger->Warn("'address' not word aligned: 0x{:X}", address);
state.ctx->gpr.w0 = result::InvalidAddress;
return;
}
@ -1060,14 +1077,14 @@ namespace skyline::kernel::svc {
Result result;
switch (arbitrationType) {
case ArbitrationType::WaitIfLessThan:
state.logger->Debug("svcWaitForAddress: Waiting on 0x{:X} if less than {} for {}ns", address, value, timeout);
state.logger->Debug("Waiting on 0x{:X} if less than {} for {}ns", address, value, timeout);
result = state.process->WaitForAddress(address, value, timeout, [](u32 *address, u32 value) {
return *address < value;
});
break;
case ArbitrationType::DecrementAndWaitIfLessThan:
state.logger->Debug("svcWaitForAddress: Waiting on and decrementing 0x{:X} if less than {} for {}ns", address, value, timeout);
state.logger->Debug("Waiting on and decrementing 0x{:X} if less than {} for {}ns", address, value, timeout);
result = state.process->WaitForAddress(address, value, timeout, [](u32 *address, u32 value) {
u32 userValue{__atomic_load_n(address, __ATOMIC_SEQ_CST)};
do {
@ -1079,7 +1096,7 @@ namespace skyline::kernel::svc {
break;
case ArbitrationType::WaitIfEqual:
state.logger->Debug("svcWaitForAddress: Waiting on 0x{:X} if equal to {} for {}ns", address, value, timeout);
state.logger->Debug("Waiting on 0x{:X} if equal to {} for {}ns", address, value, timeout);
result = state.process->WaitForAddress(address, value, timeout, [](u32 *address, u32 value) {
return *address == value;
});
@ -1087,18 +1104,18 @@ namespace skyline::kernel::svc {
default:
[[unlikely]]
state.logger->Error("svcWaitForAddress: 'arbitrationType' invalid: {}", arbitrationType);
state.logger->Error("'arbitrationType' invalid: {}", arbitrationType);
state.ctx->gpr.w0 = result::InvalidEnumValue;
return;
}
if (result == Result{})
[[likely]]
state.logger->Debug("svcWaitForAddress: Waited on 0x{:X} successfully", address);
state.logger->Debug("Waited on 0x{:X} successfully", address);
else if (result == result::TimedOut)
state.logger->Debug("svcWaitForAddress: Wait on 0x{:X} has timed out after {}ns", address, timeout);
state.logger->Debug("Wait on 0x{:X} has timed out after {}ns", address, timeout);
else if (result == result::InvalidState)
state.logger->Debug("svcWaitForAddress: The value at 0x{:X} did not satisfy the arbitration condition", address);
state.logger->Debug("The value at 0x{:X} did not satisfy the arbitration condition", address);
state.ctx->gpr.w0 = result;
}
@ -1106,7 +1123,7 @@ namespace skyline::kernel::svc {
void SignalToAddress(const DeviceState &state) {
auto address{reinterpret_cast<u32 *>(state.ctx->gpr.x0)};
if (!util::WordAligned(address)) [[unlikely]] {
state.logger->Warn("svcWaitForAddress: 'address' not word aligned: 0x{:X}", address);
state.logger->Warn("'address' not word aligned: 0x{:X}", address);
state.ctx->gpr.w0 = result::InvalidAddress;
return;
}
@ -1122,19 +1139,19 @@ namespace skyline::kernel::svc {
Result result;
switch (signalType) {
case SignalType::Signal:
state.logger->Debug("svcSignalToAddress: Signalling 0x{:X} for {} waiters", address, count);
state.logger->Debug("Signalling 0x{:X} for {} waiters", address, count);
result = state.process->SignalToAddress(address, value, count);
break;
case SignalType::SignalAndIncrementIfEqual:
state.logger->Debug("svcSignalToAddress: Signalling 0x{:X} and incrementing if equal to {} for {} waiters", address, value, count);
state.logger->Debug("Signalling 0x{:X} and incrementing if equal to {} for {} waiters", address, value, count);
result = state.process->SignalToAddress(address, value, count, [](u32 *address, u32 value, u32) {
return __atomic_compare_exchange_n(address, &value, value + 1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
});
break;
case SignalType::SignalAndModifyBasedOnWaitingThreadCountIfEqual:
state.logger->Debug("svcSignalToAddress: Signalling 0x{:X} and setting to waiting thread count if equal to {} for {} waiters", address, value, count);
state.logger->Debug("Signalling 0x{:X} and setting to waiting thread count if equal to {} for {} waiters", address, value, count);
result = state.process->SignalToAddress(address, value, count, [](u32 *address, u32 value, u32 waiterCount) {
return __atomic_compare_exchange_n(address, &value, waiterCount, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
});
@ -1142,16 +1159,16 @@ namespace skyline::kernel::svc {
default:
[[unlikely]]
state.logger->Error("svcSignalToAddress: 'signalType' invalid: {}", signalType);
state.logger->Error("'signalType' invalid: {}", signalType);
state.ctx->gpr.w0 = result::InvalidEnumValue;
return;
}
if (result == Result{})
[[likely]]
state.logger->Debug("svcSignalToAddress: Signalled 0x{:X} for {} successfully", address, count);
state.logger->Debug("Signalled 0x{:X} for {} successfully", address, count);
else if (result == result::InvalidState)
state.logger->Debug("svcSignalToAddress: The value at 0x{:X} did not satisfy the mutation condition", address);
state.logger->Debug("The value at 0x{:X} did not satisfy the mutation condition", address);
state.ctx->gpr.w0 = result;
}

View file

@ -229,136 +229,157 @@ namespace skyline::kernel::svc {
void SignalToAddress(const DeviceState &state);
/**
* @brief The SVC Table maps all SVCs to their corresponding functions
* @brief A per-SVC descriptor with it's name and a function pointer
* @note The descriptor is nullable, the validity of the descriptor can be checked with the boolean operator
*/
static std::array<void (*)(const DeviceState &), 0x80> SvcTable{
nullptr, // 0x00 (Does not exist)
SetHeapSize, // 0x01
nullptr, // 0x02
SetMemoryAttribute, // 0x03
MapMemory, // 0x04
UnmapMemory, // 0x05
QueryMemory, // 0x06
ExitProcess, // 0x07
CreateThread, // 0x08
StartThread, // 0x09
ExitThread, // 0x0A
SleepThread, // 0x0B
GetThreadPriority, // 0x0C
SetThreadPriority, // 0x0D
GetThreadCoreMask, // 0x0E
SetThreadCoreMask, // 0x0F
GetCurrentProcessorNumber, // 0x10
nullptr, // 0x11
ClearEvent, // 0x12
MapSharedMemory, // 0x13
nullptr, // 0x14
CreateTransferMemory, // 0x15
CloseHandle, // 0x16
ResetSignal, // 0x17
WaitSynchronization, // 0x18
CancelSynchronization, // 0x19
ArbitrateLock, // 0x1A
ArbitrateUnlock, // 0x1B
WaitProcessWideKeyAtomic, // 0x1C
SignalProcessWideKey, // 0x1D
GetSystemTick, // 0x1E
ConnectToNamedPort, // 0x1F
nullptr, // 0x20
SendSyncRequest, // 0x21
nullptr, // 0x22
nullptr, // 0x23
nullptr, // 0x24
GetThreadId, // 0x25
Break, // 0x26
OutputDebugString, // 0x27
nullptr, // 0x28
GetInfo, // 0x29
nullptr, // 0x2A
nullptr, // 0x2B
MapPhysicalMemory, // 0x2C
UnmapPhysicalMemory, // 0x2D
nullptr, // 0x2E
nullptr, // 0x2F
nullptr, // 0x30
nullptr, // 0x31
nullptr, // 0x32
nullptr, // 0x33
WaitForAddress, // 0x34
SignalToAddress, // 0x35
nullptr, // 0x36
nullptr, // 0x37
nullptr, // 0x38
nullptr, // 0x39
nullptr, // 0x3A
nullptr, // 0x3B
nullptr, // 0x3C
nullptr, // 0x3D
nullptr, // 0x3E
nullptr, // 0x3F
nullptr, // 0x40
nullptr, // 0x41
nullptr, // 0x42
nullptr, // 0x43
nullptr, // 0x44
nullptr, // 0x45
nullptr, // 0x46
nullptr, // 0x47
nullptr, // 0x48
nullptr, // 0x49
nullptr, // 0x4A
nullptr, // 0x4B
nullptr, // 0x4C
nullptr, // 0x4D
nullptr, // 0x4E
nullptr, // 0x4F
nullptr, // 0x50
nullptr, // 0x51
nullptr, // 0x52
nullptr, // 0x53
nullptr, // 0x54
nullptr, // 0x55
nullptr, // 0x56
nullptr, // 0x57
nullptr, // 0x58
nullptr, // 0x59
nullptr, // 0x5A
nullptr, // 0x5B
nullptr, // 0x5C
nullptr, // 0x5D
nullptr, // 0x5E
nullptr, // 0x5F
nullptr, // 0x60
nullptr, // 0x61
nullptr, // 0x62
nullptr, // 0x63
nullptr, // 0x64
nullptr, // 0x65
nullptr, // 0x66
nullptr, // 0x67
nullptr, // 0x68
nullptr, // 0x69
nullptr, // 0x6A
nullptr, // 0x6B
nullptr, // 0x6C
nullptr, // 0x6D
nullptr, // 0x6E
nullptr, // 0x6F
nullptr, // 0x70
nullptr, // 0x71
nullptr, // 0x72
nullptr, // 0x73
nullptr, // 0x74
nullptr, // 0x75
nullptr, // 0x76
nullptr, // 0x77
nullptr, // 0x78
nullptr, // 0x79
nullptr, // 0x7A
nullptr, // 0x7B
nullptr, // 0x7C
nullptr, // 0x7D
nullptr, // 0x7E
nullptr // 0x7F
struct SvcDescriptor {
void (*function)(const DeviceState &); //!< A function pointer to a HLE implementation of the SVC
const char* name; //!< A pointer to a static string of the SVC name, the underlying data should not be mutated
operator bool() {
return function;
}
};
#define SVC_NONE SvcDescriptor{} //!< A macro with a placeholder value for the SVC not being implemented or not existing
#define SVC_STRINGIFY(name) #name
#define SVC_ENTRY(function) SvcDescriptor{function, SVC_STRINGIFY(Svc ## function)} //!< A macro which automatically stringifies the function name as the name to prevent pointless duplication
/**
* @brief The SVC table maps all SVCs to their corresponding functions
*/
static constexpr std::array<SvcDescriptor, 0x80> SvcTable{
SVC_NONE, // 0x00 (Does not exist)
SVC_ENTRY(SetHeapSize), // 0x01
SVC_NONE, // 0x02
SVC_ENTRY(SetMemoryAttribute), // 0x03
SVC_ENTRY(MapMemory), // 0x04
SVC_ENTRY(UnmapMemory), // 0x05
SVC_ENTRY(QueryMemory), // 0x06
SVC_ENTRY(ExitProcess), // 0x07
SVC_ENTRY(CreateThread), // 0x08
SVC_ENTRY(StartThread), // 0x09
SVC_ENTRY(ExitThread), // 0x0A
SVC_ENTRY(SleepThread), // 0x0B
SVC_ENTRY(GetThreadPriority), // 0x0C
SVC_ENTRY(SetThreadPriority), // 0x0D
SVC_ENTRY(GetThreadCoreMask), // 0x0E
SVC_ENTRY(SetThreadCoreMask), // 0x0F
SVC_ENTRY(GetCurrentProcessorNumber), // 0x10
SVC_NONE, // 0x11
SVC_ENTRY(ClearEvent), // 0x12
SVC_ENTRY(MapSharedMemory), // 0x13
SVC_NONE, // 0x14
SVC_ENTRY(CreateTransferMemory), // 0x15
SVC_ENTRY(CloseHandle), // 0x16
SVC_ENTRY(ResetSignal), // 0x17
SVC_ENTRY(WaitSynchronization), // 0x18
SVC_ENTRY(CancelSynchronization), // 0x19
SVC_ENTRY(ArbitrateLock), // 0x1A
SVC_ENTRY(ArbitrateUnlock), // 0x1B
SVC_ENTRY(WaitProcessWideKeyAtomic), // 0x1C
SVC_ENTRY(SignalProcessWideKey), // 0x1D
SVC_ENTRY(GetSystemTick), // 0x1E
SVC_ENTRY(ConnectToNamedPort), // 0x1F
SVC_NONE, // 0x20
SVC_ENTRY(SendSyncRequest), // 0x21
SVC_NONE, // 0x22
SVC_NONE, // 0x23
SVC_NONE, // 0x24
SVC_ENTRY(GetThreadId), // 0x25
SVC_ENTRY(Break), // 0x26
SVC_ENTRY(OutputDebugString), // 0x27
SVC_NONE, // 0x28
SVC_ENTRY(GetInfo), // 0x29
SVC_NONE, // 0x2A
SVC_NONE, // 0x2B
SVC_ENTRY(MapPhysicalMemory), // 0x2C
SVC_ENTRY(UnmapPhysicalMemory), // 0x2D
SVC_NONE, // 0x2E
SVC_NONE, // 0x2F
SVC_NONE, // 0x30
SVC_NONE, // 0x31
SVC_NONE, // 0x32
SVC_NONE, // 0x33
SVC_ENTRY(WaitForAddress), // 0x34
SVC_ENTRY(SignalToAddress), // 0x35
SVC_NONE, // 0x36
SVC_NONE, // 0x37
SVC_NONE, // 0x38
SVC_NONE, // 0x39
SVC_NONE, // 0x3A
SVC_NONE, // 0x3B
SVC_NONE, // 0x3C
SVC_NONE, // 0x3D
SVC_NONE, // 0x3E
SVC_NONE, // 0x3F
SVC_NONE, // 0x40
SVC_NONE, // 0x41
SVC_NONE, // 0x42
SVC_NONE, // 0x43
SVC_NONE, // 0x44
SVC_NONE, // 0x45
SVC_NONE, // 0x46
SVC_NONE, // 0x47
SVC_NONE, // 0x48
SVC_NONE, // 0x49
SVC_NONE, // 0x4A
SVC_NONE, // 0x4B
SVC_NONE, // 0x4C
SVC_NONE, // 0x4D
SVC_NONE, // 0x4E
SVC_NONE, // 0x4F
SVC_NONE, // 0x50
SVC_NONE, // 0x51
SVC_NONE, // 0x52
SVC_NONE, // 0x53
SVC_NONE, // 0x54
SVC_NONE, // 0x55
SVC_NONE, // 0x56
SVC_NONE, // 0x57
SVC_NONE, // 0x58
SVC_NONE, // 0x59
SVC_NONE, // 0x5A
SVC_NONE, // 0x5B
SVC_NONE, // 0x5C
SVC_NONE, // 0x5D
SVC_NONE, // 0x5E
SVC_NONE, // 0x5F
SVC_NONE, // 0x60
SVC_NONE, // 0x61
SVC_NONE, // 0x62
SVC_NONE, // 0x63
SVC_NONE, // 0x64
SVC_NONE, // 0x65
SVC_NONE, // 0x66
SVC_NONE, // 0x67
SVC_NONE, // 0x68
SVC_NONE, // 0x69
SVC_NONE, // 0x6A
SVC_NONE, // 0x6B
SVC_NONE, // 0x6C
SVC_NONE, // 0x6D
SVC_NONE, // 0x6E
SVC_NONE, // 0x6F
SVC_NONE, // 0x70
SVC_NONE, // 0x71
SVC_NONE, // 0x72
SVC_NONE, // 0x73
SVC_NONE, // 0x74
SVC_NONE, // 0x75
SVC_NONE, // 0x76
SVC_NONE, // 0x77
SVC_NONE, // 0x78
SVC_NONE, // 0x79
SVC_NONE, // 0x7A
SVC_NONE, // 0x7B
SVC_NONE, // 0x7C
SVC_NONE, // 0x7D
SVC_NONE, // 0x7E
SVC_NONE // 0x7F
};
#undef SVC_NONE
#undef SVC_STRINGIFY
#undef SVC_ENTRY
}

View file

@ -8,7 +8,7 @@
#include "KProcess.h"
namespace skyline::kernel::type {
KPrivateMemory::KPrivateMemory(const DeviceState &state, u8 *ptr, size_t size, memory::Permission permission, memory::MemoryState memState) : ptr(ptr), size(size), permission(permission), memState(memState), KMemory(state, KType::KPrivateMemory) {
KPrivateMemory::KPrivateMemory(const DeviceState &state, u8 *ptr, size_t size, memory::Permission permission, memory::MemoryState memState) : ptr(ptr), size(size), permission(permission), memoryState(memState), KMemory(state, KType::KPrivateMemory) {
if (!state.process->memory.base.IsInside(ptr) || !state.process->memory.base.IsInside(ptr + size))
throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", ptr, ptr + size);
if (!util::PageAligned(ptr) || !util::PageAligned(size))
@ -40,7 +40,7 @@ namespace skyline::kernel::type {
.ptr = ptr + size,
.size = nSize - size,
.permission = permission,
.state = memState,
.state = memoryState,
});
}
@ -60,22 +60,22 @@ namespace skyline::kernel::type {
throw exception("An occurred while remapping private memory: {}", strerror(errno));
}
void KPrivateMemory::UpdatePermission(u8 *ptr, size_t size, memory::Permission permission) {
ptr = std::clamp(ptr, this->ptr, this->ptr + this->size);
size = std::min(size, static_cast<size_t>((this->ptr + this->size) - ptr));
void KPrivateMemory::UpdatePermission(u8 *pPtr, size_t pSize, memory::Permission pPermission) {
pPtr = std::clamp(pPtr, ptr, ptr + size);
pSize = std::min(pSize, static_cast<size_t>((ptr + size) - pPtr));
if (ptr && !util::PageAligned(ptr))
throw exception("KPrivateMemory permission updated with a non-page-aligned address: 0x{:X}", ptr);
if (pPtr && !util::PageAligned(pPtr))
throw exception("KPrivateMemory permission updated with a non-page-aligned address: 0x{:X}", pPtr);
// If a static code region has been mapped as writable it needs to be changed to mutable
if (memState == memory::states::CodeStatic && permission.w)
memState = memory::states::CodeMutable;
if (memoryState == memory::states::CodeStatic && pPermission.w)
memoryState = memory::states::CodeMutable;
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr,
.size = size,
.permission = permission,
.state = memState,
.ptr = pPtr,
.size = pSize,
.permission = pPermission,
.state = memoryState,
});
}

View file

@ -8,13 +8,14 @@
namespace skyline::kernel::type {
/**
* @brief KPrivateMemory is used to map memory local to the guest process
* @note This does not reflect a kernel object in Horizon OS, it is an abstraction which makes things simpler to manage in Skyline instead
*/
class KPrivateMemory : public KMemory {
public:
u8 *ptr{};
size_t size{};
memory::Permission permission;
memory::MemoryState memState;
memory::MemoryState memoryState;
/**
* @param permission The permissions for the allocated memory (As reported to the application, host memory permissions aren't reflected by this)
@ -22,10 +23,14 @@ namespace skyline::kernel::type {
*/
KPrivateMemory(const DeviceState &state, u8 *ptr, size_t size, memory::Permission permission, memory::MemoryState memState);
/**
* @note There is no check regarding if any expansions will cause the memory mapping to leak into other mappings
* @note Any extensions will have the same permissions and memory state as the initial mapping as opposed to extending the end
*/
void Resize(size_t size);
/**
* @note Only contents of any overlapping regions will be retained
* @note This does not copy over anything, only contents of any overlapping regions will be retained
*/
void Remap(u8 *ptr, size_t size);
@ -33,7 +38,7 @@ namespace skyline::kernel::type {
return span(ptr, size);
}
void UpdatePermission(u8 *ptr, size_t size, memory::Permission permission) override;
void UpdatePermission(u8 *pPtr, size_t pSize, memory::Permission pPermission) override;
/**
* @brief The destructor of private memory, it deallocates the memory

View file

@ -3,8 +3,8 @@
#include <nce.h>
#include <os.h>
#include <common/trace.h>
#include <kernel/results.h>
#include "KProcess.h"
namespace skyline::kernel::type {
@ -104,6 +104,8 @@ namespace skyline::kernel::type {
constexpr u32 HandleWaitersBit{1UL << 30}; //!< A bit which denotes if a mutex psuedo-handle has waiters or not
Result KProcess::MutexLock(u32 *mutex, KHandle ownerHandle, KHandle tag) {
TRACE_EVENT_FMT("kernel", "MutexLock 0x{:X}", mutex);
std::shared_ptr<KThread> owner;
try {
owner = GetHandle<KThread>(ownerHandle);
@ -142,6 +144,8 @@ namespace skyline::kernel::type {
}
void KProcess::MutexUnlock(u32 *mutex) {
TRACE_EVENT_FMT("kernel", "MutexUnlock 0x{:X}", mutex);
std::lock_guard lock(state.thread->waiterMutex);
auto &waiters{state.thread->waiters};
auto nextOwnerIt{std::find_if(waiters.begin(), waiters.end(), [mutex](const std::shared_ptr<KThread> &thread) { return thread->waitKey == mutex; })};
@ -203,6 +207,8 @@ namespace skyline::kernel::type {
}
Result KProcess::ConditionalVariableWait(u32 *key, u32 *mutex, KHandle tag, i64 timeout) {
TRACE_EVENT_FMT("kernel", "ConditionalVariableWait 0x{:X} (0x{:X})", key, mutex);
{
std::lock_guard lock(syncWaiterMutex);
auto queue{syncWaiters.equal_range(key)};
@ -242,6 +248,8 @@ namespace skyline::kernel::type {
}
void KProcess::ConditionalVariableSignal(u32 *key, i32 amount) {
TRACE_EVENT_FMT("kernel", "ConditionalVariableSignal 0x{:X}", key);
std::lock_guard lock(syncWaiterMutex);
auto queue{syncWaiters.equal_range(key)};
@ -254,6 +262,8 @@ namespace skyline::kernel::type {
}
Result KProcess::WaitForAddress(u32 *address, u32 value, i64 timeout, bool (*arbitrationFunction)(u32 *, u32)) {
TRACE_EVENT_FMT("kernel", "WaitForAddress 0x{:X}", address);
{
std::lock_guard lock(syncWaiterMutex);
if (!arbitrationFunction(address, value)) [[unlikely]]
@ -287,6 +297,8 @@ namespace skyline::kernel::type {
}
Result KProcess::SignalToAddress(u32 *address, u32 value, i32 amount, bool(*mutateFunction)(u32 *address, u32 value, u32 waiterCount)) {
TRACE_EVENT_FMT("kernel", "SignalToAddress 0x{:X}", address);
std::lock_guard lock(syncWaiterMutex);
auto queue{syncWaiters.equal_range(address)};

View file

@ -8,14 +8,15 @@
#include "KProcess.h"
namespace skyline::kernel::type {
KSharedMemory::KSharedMemory(const DeviceState &state, size_t size, memory::MemoryState memState, KType type) : initialState(memState), KMemory(state, type) {
KSharedMemory::KSharedMemory(const DeviceState &state, size_t size, memory::MemoryState memState, KType type) : memoryState(memState), KMemory(state, type) {
fd = ASharedMemory_create("KSharedMemory", size);
if (fd < 0)
throw exception("An error occurred while creating shared memory: {}", fd);
kernel.ptr = reinterpret_cast<u8 *>(mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0));
if (kernel.ptr == MAP_FAILED)
throw exception("An occurred while mapping shared memory: {}", strerror(errno));
[[unlikely]]
throw exception("An occurred while mapping shared memory: {}", strerror(errno));
kernel.size = size;
}
@ -28,14 +29,15 @@ namespace skyline::kernel::type {
guest.ptr = reinterpret_cast<u8 *>(mmap(ptr, size, permission.Get(), MAP_SHARED | (ptr ? MAP_FIXED : 0), fd, 0));
if (guest.ptr == MAP_FAILED)
throw exception("An error occurred while mapping shared memory in guest");
[[unlikely]]
throw exception("An error occurred while mapping shared memory in guest: {}", strerror(errno));
guest.size = size;
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = guest.ptr,
.size = size,
.permission = permission,
.state = initialState,
.state = memoryState,
});
return guest.ptr;
@ -47,15 +49,15 @@ namespace skyline::kernel::type {
if (guest.Valid()) {
mprotect(ptr, size, permission.Get());
if (guest.ptr == MAP_FAILED)
throw exception("An error occurred while updating shared memory's permissions in guest");
[[unlikely]]
throw exception("An error occurred while updating shared memory's permissions in guest: {}", strerror(errno));
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = ptr,
.size = size,
.permission = permission,
.state = initialState,
.state = memoryState,
});
}
}
@ -65,7 +67,7 @@ namespace skyline::kernel::type {
munmap(kernel.ptr, kernel.size);
if (state.process && guest.Valid()) {
mmap(guest.ptr, guest.size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
mmap(guest.ptr, guest.size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); // As this is the destructor, we cannot throw on this failing
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = guest.ptr,
.size = guest.size,

View file

@ -7,12 +7,12 @@
namespace skyline::kernel::type {
/**
* @brief KSharedMemory is used to retain two mappings of the same underlying memory, allowing persistence of the memory
* @brief KSharedMemory is used to retain two mappings of the same underlying memory, allowing sharing memory between two processes
*/
class KSharedMemory : public KMemory {
private:
int fd; //!< A file descriptor to the underlying shared memory
memory::MemoryState initialState;
memory::MemoryState memoryState; //!< The state of the memory as supplied initially, this is retained for any mappings
public:
struct MapInfo {

View file

@ -4,6 +4,7 @@
#include <cxxabi.h>
#include <unistd.h>
#include <common/signal.h>
#include <common/trace.h>
#include <nce.h>
#include <os.h>
#include "KProcess.h"
@ -82,6 +83,8 @@ namespace skyline::kernel::type {
state.scheduler->WaitSchedule();
}
TRACE_EVENT_BEGIN("guest", "Guest");
asm volatile(
"MRS X0, TPIDR_EL0\n\t"
"MSR TPIDR_EL0, %x0\n\t" // Set TLS to ThreadContext

View file

@ -7,7 +7,7 @@
namespace skyline::kernel::type {
/**
* @brief KTransferMemory is used to transfer memory from one application to another on HOS, we emulate this abstraction using KSharedMemory as it's functionally indistinguishable for the guest and allows access from the kernel regardless of if it's mapped on the guest
* @brief KTransferMemory is used to transfer memory from one application to another on HOS, we emulate this abstraction using KSharedMemory as it's essentially the same with the main difference being that KSharedMemory is allocated by the kernel while KTransferMemory is created from memory that's been allocated by the guest beforehand
*/
class KTransferMemory : public KSharedMemory {
public:

View file

@ -4,8 +4,8 @@
#include <cxxabi.h>
#include <unistd.h>
#include "common/signal.h"
#include "common/trace.h"
#include "os.h"
#include "gpu.h"
#include "jvm.h"
#include "kernel/types/KProcess.h"
#include "kernel/svc.h"
@ -14,15 +14,17 @@
#include "nce.h"
namespace skyline::nce {
void NCE::SvcHandler(u16 svc, ThreadContext *ctx) {
void NCE::SvcHandler(u16 svcId, ThreadContext *ctx) {
TRACE_EVENT_END("guest");
const auto &state{*ctx->state};
auto svc{kernel::svc::SvcTable[svcId]};
try {
auto function{kernel::svc::SvcTable[svc]};
if (function) [[likely]] {
state.logger->Debug("SVC called 0x{:X}", svc);
(*function)(state);
if (svc) [[likely]] {
TRACE_EVENT("kernel", perfetto::StaticString{svc.name});
(svc.function)(state);
} else [[unlikely]] {
throw exception("Unimplemented SVC 0x{:X}", svc);
throw exception("Unimplemented SVC 0x{:X}", svcId);
}
while (kernel::Scheduler::YieldPending) [[unlikely]] {
@ -32,7 +34,7 @@ namespace skyline::nce {
}
} catch (const signal::SignalException &e) {
if (e.signal != SIGINT) {
state.logger->Error("{} (SVC: 0x{:X})\nStack Trace:{}", e.what(), svc, state.loader->GetStackTrace(e.frames));
state.logger->ErrorNoPrefix("{} (SVC: 0x{:X})\nStack Trace:{}", e.what(), svc.name, state.loader->GetStackTrace(e.frames));
if (state.thread->id) {
signal::BlockSignal({SIGINT});
state.process->Kill(false);
@ -41,7 +43,11 @@ namespace skyline::nce {
abi::__cxa_end_catch(); // We call this prior to the longjmp to cause the exception object to be destroyed
std::longjmp(state.thread->originalCtx, true);
} catch (const std::exception &e) {
state.logger->Error("{} (SVC: 0x{:X})\nStack Trace:{}", e.what(), svc, state.loader->GetStackTrace());
if (svc)
state.logger->ErrorNoPrefix("{} (SVC: {})\nStack Trace:{}", e.what(), svc.name, state.loader->GetStackTrace());
else
state.logger->ErrorNoPrefix("{} (SVC: 0x{:X})\nStack Trace:{}", e.what(), svcId, state.loader->GetStackTrace());
if (state.thread->id) {
signal::BlockSignal({SIGINT});
state.process->Kill(false);
@ -49,6 +55,8 @@ namespace skyline::nce {
abi::__cxa_end_catch();
std::longjmp(state.thread->originalCtx, true);
}
TRACE_EVENT_BEGIN("guest", "Guest");
}
void NCE::SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls) {

View file

@ -14,7 +14,7 @@ namespace skyline::nce {
private:
const DeviceState &state;
static void SvcHandler(u16 svc, ThreadContext *ctx);
static void SvcHandler(u16 svcId, ThreadContext *ctx);
public:
static void SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls);

View file

@ -20,9 +20,6 @@ namespace skyline::service::am {
*/
Result GetApplicationFunctions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
//#undef SFUNC_BASE
//#define SFUNC_BASE(id, Class, BaseClass, Function) std::pair<u32, std::pair<std::function<Result(Class*, type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view>>{id, {&CallBaseFunction<Class, BaseClass, BaseClass::Function>, #Function}}
SERVICE_DECL(
SFUNC_BASE(0x0, IApplicationProxy, BaseProxy, GetCommonStateGetter),
SFUNC_BASE(0x1, IApplicationProxy, BaseProxy, GetSelfController),

View file

@ -11,7 +11,7 @@ namespace skyline::service::audio {
Result IAudioRendererManager::OpenAudioRenderer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
IAudioRenderer::AudioRendererParameters params{request.Pop<IAudioRenderer::AudioRendererParameters>()};
state.logger->Debug("IAudioRendererManager: Opening a rev {} IAudioRenderer with sample rate: {}, voice count: {}, effect count: {}", IAudioRenderer::ExtractVersionFromRevision(params.revision), params.sampleRate, params.voiceCount, params.effectCount);
state.logger->Debug("Opening a rev {} IAudioRenderer with sample rate: {}, voice count: {}, effect count: {}", IAudioRenderer::ExtractVersionFromRevision(params.revision), params.sampleRate, params.voiceCount, params.effectCount);
manager.RegisterService(std::make_shared<IAudioRenderer::IAudioRenderer>(state, manager, params), session, response);

View file

@ -2,6 +2,7 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <cxxabi.h>
#include <common/trace.h>
#include "base_service.h"
namespace skyline::service {
@ -19,18 +20,19 @@ namespace skyline::service {
}
Result service::BaseService::HandleRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
std::pair<std::function<Result(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view> function;
ServiceFunctionDescriptor function;
try {
function = GetServiceFunction(request.payload->value);
state.logger->Debug("Service: {} @ {}", function.second, GetName());
state.logger->DebugNoPrefix("Service: {}", function.name);
} catch (const std::out_of_range &) {
state.logger->Warn("Cannot find function in service '{0}': 0x{1:X} ({1})", GetName(), static_cast<u32>(request.payload->value));
return {};
}
TRACE_EVENT("service", perfetto::StaticString{function.name});
try {
return function.first(session, request, response);
return function(session, request, response);
} catch (const std::exception &e) {
throw exception("{} (Service: {} @ {})", e.what(), function.second, GetName());
throw exception("{} (Service: {})", e.what(), function.name);
}
}
}

View file

@ -5,14 +5,25 @@
#include <kernel/ipc.h>
#define SFUNC(id, Class, Function) std::pair<u32, std::pair<std::function<Result(Class*, type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view>>{id, {&Class::Function, #Function}}
#define SFUNC_BASE(id, Class, BaseClass, Function) std::pair<u32, std::pair<std::function<Result(Class*, type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view>>{id, {&CallBaseFunction<Class, BaseClass, decltype(&BaseClass::Function), &BaseClass::Function>, #Function}}
#define SERVICE_STRINGIFY(string) #string
#define SFUNC(id, Class, Function) std::pair<u32, std::pair<Result(Class::*)(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &), const char*>>{id, {&Class::Function, SERVICE_STRINGIFY(Class::Function)}}
#define SFUNC_BASE(id, Class, BaseClass, Function) std::pair<u32, std::pair<Result(Class::*)(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &), const char*>>{id, {&Class::CallBaseFunction<BaseClass, decltype(&BaseClass::Function), &BaseClass::Function>, SERVICE_STRINGIFY(Class::Function)}}
#define SERVICE_DECL_AUTO(name, value) decltype(value) name = value
#define SERVICE_DECL(...) \
SERVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \
std::pair<std::function<Result(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view> GetServiceFunction(u32 id) override { \
auto& function{functions.at(id)}; \
return std::make_pair(std::bind(function.first, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), function.second); \
#define SERVICE_DECL(...) \
private: \
template<typename BaseClass, typename BaseFunctionType, BaseFunctionType BaseFunction> \
Result CallBaseFunction(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { \
return (static_cast<BaseClass *>(this)->*BaseFunction)(session, request, response); \
} \
SERVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \
protected: \
ServiceFunctionDescriptor GetServiceFunction(u32 id) override { \
auto& function{functions.at(id)}; \
return ServiceFunctionDescriptor{ \
reinterpret_cast<DerivedService*>(this), \
reinterpret_cast<decltype(ServiceFunctionDescriptor::function)>(function.first), \
function.second \
}; \
}
#define SRVREG(class, ...) std::make_shared<class>(state, manager, ##__VA_ARGS__)
@ -37,10 +48,20 @@ namespace skyline::service {
const DeviceState &state;
ServiceManager &manager;
template<typename Class, typename BaseClass, typename BaseFunctionType, BaseFunctionType BaseFunction>
static constexpr Result CallBaseFunction(Class *clazz, type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return (static_cast<BaseClass *>(clazz)->*BaseFunction)(session, request, response);
}
class DerivedService; //!< A placeholder derived class which is used for class function semantics
/**
* @brief A per-function descriptor for HLE service functions
*/
struct ServiceFunctionDescriptor {
DerivedService *clazz; //!< A pointer to the class that this was derived from, it's used as the 'this' pointer for the function
Result (DerivedService::*function)(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &); //!< A function pointer to a HLE implementation of the service function
const char *name; //!< A pointer to a static string in the format "Class::Function" for the specific service class/function
constexpr Result operator()(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return (clazz->*function)(session, request, response);
}
};
public:
BaseService(const DeviceState &state, ServiceManager &manager) : state(state), manager(manager) {}
@ -50,12 +71,12 @@ namespace skyline::service {
*/
virtual ~BaseService() = default;
virtual std::pair<std::function<Result(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view> GetServiceFunction(u32 id) {
virtual ServiceFunctionDescriptor GetServiceFunction(u32 id) {
throw std::out_of_range("GetServiceFunction not implemented");
}
/**
* @return The name of the class
* @return A string with the name of the service class
* @note The lifetime of the returned string is tied to that of the class
*/
const std::string &GetName();

View file

@ -22,7 +22,7 @@ namespace skyline::service::hosbinder {
out.Push<u32>(0);
out.Push(queue.at(slot)->gbpBuffer);
state.logger->Debug("RequestBuffer: Slot: {}", slot, sizeof(GbpBuffer));
state.logger->Debug("Slot: {}", slot, sizeof(GbpBuffer));
}
void GraphicBufferProducer::DequeueBuffer(Parcel &in, Parcel &out) {
@ -46,7 +46,7 @@ namespace skyline::service::hosbinder {
out.Push(*slot);
out.Push(std::array<u32, 13>{1, 0x24}); // Unknown
state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Slot: {}", width, height, format, usage, *slot);
state.logger->Debug("Width: {}, Height: {}, Format: {}, Usage: {}, Slot: {}", width, height, format, usage, *slot);
}
void GraphicBufferProducer::QueueBuffer(Parcel &in, Parcel &out) {
@ -81,7 +81,7 @@ namespace skyline::service::hosbinder {
};
out.Push(output);
state.logger->Debug("QueueBuffer: Timestamp: {}, Auto Timestamp: {}, Crop: [T: {}, B: {}, L: {}, R: {}], Scaling Mode: {}, Transform: {}, Sticky Transform: {}, Swap Interval: {}, Slot: {}", data.timestamp, data.autoTimestamp, data.crop.top, data.crop.bottom, data.crop.left, data.crop.right, data.scalingMode, data.transform, data.stickyTransform, data.swapInterval, data.slot);
state.logger->Debug("Timestamp: {}, Auto Timestamp: {}, Crop: [T: {}, B: {}, L: {}, R: {}], Scaling Mode: {}, Transform: {}, Sticky Transform: {}, Swap Interval: {}, Slot: {}", data.timestamp, data.autoTimestamp, data.crop.top, data.crop.bottom, data.crop.left, data.crop.right, data.scalingMode, data.transform, data.stickyTransform, data.swapInterval, data.slot);
}
void GraphicBufferProducer::CancelBuffer(Parcel &in) {
@ -90,7 +90,7 @@ namespace skyline::service::hosbinder {
queue.at(slot)->status = BufferStatus::Free;
state.logger->Debug("CancelBuffer: Slot: {}", slot);
state.logger->Debug("Slot: {}", slot);
}
void GraphicBufferProducer::Connect(Parcel &out) {
@ -152,7 +152,7 @@ namespace skyline::service::hosbinder {
queue[data.slot] = std::make_shared<Buffer>(gbpBuffer, texture->InitializeTexture());
state.gpu->presentation.bufferEvent->Signal();
state.logger->Debug("SetPreallocatedBuffer: Slot: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}, Size: 0x{:X}", data.slot, gbpBuffer.magic, gbpBuffer.width, gbpBuffer.height, gbpBuffer.stride, gbpBuffer.format, gbpBuffer.usage, gbpBuffer.index, gbpBuffer.nvmapId, gbpBuffer.nvmapHandle, gbpBuffer.offset, (1U << gbpBuffer.blockHeightLog2), gbpBuffer.size);
state.logger->Debug("Slot: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}, Size: 0x{:X}", data.slot, gbpBuffer.magic, gbpBuffer.width, gbpBuffer.height, gbpBuffer.stride, gbpBuffer.format, gbpBuffer.usage, gbpBuffer.index, gbpBuffer.nvmapId, gbpBuffer.nvmapHandle, gbpBuffer.offset, (1U << gbpBuffer.blockHeightLog2), gbpBuffer.size);
}
void GraphicBufferProducer::OnTransact(TransactionCode code, Parcel &in, Parcel &out) {

View file

@ -23,7 +23,7 @@ namespace skyline::service::hosbinder {
// If this was not done then we would need to maintain an array of GraphicBufferProducer objects for each layer and send the request for it specifically
// There would also need to be an external compositor which composites all the graphics buffers submitted to every GraphicBufferProducer
state.logger->Debug("TransactParcel: Layer ID: {}, Code: {}", layerId, code);
state.logger->Debug("Layer ID: {}, Code: {}", layerId, code);
producer->OnTransact(code, in, out);
out.WriteParcel(request.outputBuf.at(0));

View file

@ -76,7 +76,7 @@ namespace skyline::service::nvdrv {
if (event != nullptr) {
auto handle{state.process->InsertItem<type::KEvent>(event)};
state.logger->Debug("QueryEvent: FD: {}, Event ID: {}, Handle: 0x{:X}", fd, eventId, handle);
state.logger->Debug("FD: {}, Event ID: {}, Handle: 0x{:X}", fd, eventId, handle);
response.copyHandles.push_back(handle);
response.Push(device::NvStatus::Success);

View file

@ -2,6 +2,7 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <cxxabi.h>
#include <common/trace.h>
#include "nvdevice.h"
namespace skyline::service::nvdrv::device {
@ -30,18 +31,19 @@ namespace skyline::service::nvdrv::device {
}
}()};
std::pair<std::function<NvStatus(IoctlType, span<u8>, span<u8>)>, std::string_view> function;
NvDeviceFunctionDescriptor function;
try {
function = GetIoctlFunction(cmd);
state.logger->Debug("{}: {} @ {}", typeString, GetName(), function.second);
state.logger->DebugNoPrefix("{}: {}", typeString, function.name);
} catch (std::out_of_range &) {
state.logger->Warn("Cannot find IOCTL for device '{}': 0x{:X}", GetName(), cmd);
return NvStatus::NotImplemented;
}
TRACE_EVENT("service", perfetto::StaticString{function.name});
try {
return function.first(type, buffer, inlineBuffer);
return function(type, buffer, inlineBuffer);
} catch (const std::exception &e) {
throw exception("{} ({} @ {}: {})", e.what(), typeString, GetName(), function.second);
throw exception("{} ({}: {})", e.what(), typeString, function.name);
}
}
}

View file

@ -6,13 +6,18 @@
#include <kernel/ipc.h>
#include <kernel/types/KEvent.h>
#define NVFUNC(id, Class, Function) std::pair<u32, std::pair<std::function<NvStatus(Class*, IoctlType, span<u8>, span<u8>)>, std::string_view>>{id, {&Class::Function, #Function}}
#define NV_STRINGIFY(string) #string
#define NVFUNC(id, Class, Function) std::pair<u32, std::pair<NvStatus(Class::*)(IoctlType, span<u8>, span<u8>), const char*>>{id, {&Class::Function, NV_STRINGIFY(Class::Function)}}
#define NVDEVICE_DECL_AUTO(name, value) decltype(value) name = value
#define NVDEVICE_DECL(...) \
NVDEVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \
std::pair<std::function<NvStatus(IoctlType, span<u8>, span<u8>)>, std::string_view> GetIoctlFunction(u32 id) override { \
auto& function{functions.at(id)}; \
return std::make_pair(std::bind(function.first, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), function.second); \
#define NVDEVICE_DECL(...) \
NVDEVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \
NvDeviceFunctionDescriptor GetIoctlFunction(u32 id) override { \
auto& function{functions.at(id)}; \
return NvDeviceFunctionDescriptor{ \
reinterpret_cast<DerivedDevice*>(this), \
reinterpret_cast<decltype(NvDeviceFunctionDescriptor::function)>(function.first), \
function.second \
}; \
}
namespace skyline::service::nvdrv::device {
@ -67,12 +72,27 @@ namespace skyline::service::nvdrv::device {
protected:
const DeviceState &state;
class DerivedDevice; //!< A placeholder derived class which is used for class function semantics
/**
* @brief A per-function descriptor for NvDevice functions
*/
struct NvDeviceFunctionDescriptor {
DerivedDevice *clazz; //!< A pointer to the class that this was derived from, it's used as the 'this' pointer for the function
NvStatus (DerivedDevice::*function)(IoctlType, span<u8>, span<u8>); //!< A function pointer to the implementation of the function
const char *name; //!< A pointer to a static string in the format "Class::Function" for the specific device class/function
constexpr NvStatus operator()(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
return (clazz->*function)(type, buffer, inlineBuffer);
}
};
public:
NvDevice(const DeviceState &state) : state(state) {}
virtual ~NvDevice() = default;
virtual std::pair<std::function<NvStatus(IoctlType, span<u8>, span<u8>)>, std::string_view> GetIoctlFunction(u32 id) = 0;
virtual NvDeviceFunctionDescriptor GetIoctlFunction(u32 id) = 0;
/**
* @return The name of the class

View file

@ -2,6 +2,7 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <kernel/types/KProcess.h>
#include <common/trace.h>
#include "sm/IUserInterface.h"
#include "settings/ISettingsServer.h"
#include "settings/ISystemSettingsServer.h"
@ -146,6 +147,7 @@ namespace skyline::service {
}
void ServiceManager::SyncRequestHandler(KHandle handle) {
TRACE_EVENT("kernel", "ServiceManager::SyncRequestHandler");
auto session{state.process->GetHandle<type::KSession>(handle)};
state.logger->Verbose("----IPC Start----");
state.logger->Verbose("Handle is 0x{:X}", handle);

View file

@ -20,7 +20,7 @@ namespace skyline::service::sm {
manager.NewService(name, session, response);
return {};
} catch (std::out_of_range &) {
std::string_view stringName(reinterpret_cast<char *>(&name), sizeof(u64));
std::string_view stringName(span(reinterpret_cast<char *>(&name), sizeof(u64)).as_string(true));
state.logger->Warn("Service has not been implemented: \"{}\"", stringName);
return result::InvalidServiceName;
}