Compare commits

..

110 Commits

Author SHA1 Message Date
Liam
2c56e94702 kernel: make current thread pointer thread local 2022-06-23 00:28:00 -04:00
bunnei
95b844dbae Merge pull request #8491 from Morph1984/extra-assert
KPageTable: Remove extraneous assert
2022-06-22 14:47:07 -07:00
bunnei
9da4e62573 Merge pull request #8483 from liamwhite/fire-emblem-three-semaphores
kernel: wait for threads to stop on pause
2022-06-22 14:46:33 -07:00
Morph
1c8f6ba18f KPageTable: Remove extraneous assert
Since start is always 0 and VAddr is unsigned, we can safely remove this assert.
2022-06-21 21:28:54 -04:00
Morph
ab0e71d7cb Merge pull request #8455 from lat9nq/mingw-clang
ci/windows: Use Clang for MinGW builds
2022-06-21 20:21:13 -04:00
bunnei
737c446fc1 Merge pull request #8432 from liamwhite/watchpoint
core/debugger: memory breakpoint support
2022-06-21 16:04:57 -07:00
bunnei
73e13aa090 Merge pull request #8468 from liamwhite/dispatch-tracking
kernel: fix some uses of disable_count
2022-06-21 15:30:27 -07:00
liamwhite
0d5792cc57 Merge pull request #8487 from german77/system-button
service: am: Stub PerformSystemButtonPressingIfInFocus
2022-06-20 16:59:26 -04:00
Narr the Reg
f37b2e6f10 service: am: Stub PerformSystemButtonPressingIfInFocus
Used by Ring Fit Adventure
2022-06-20 12:35:58 -05:00
Liam
24d7aaf43c kernel: wait for threads to stop on pause 2022-06-18 16:54:33 -04:00
Morph
5b2b15091f Merge pull request #8476 from liamwhite/gpu-wasnt-ready
core: fix initialization in single core, sync GPU mode
2022-06-17 03:08:15 -04:00
lat9nq
c42fde2a37 ci/windows: Build using Clang
Uses the MinGWClangCross toolchain script to build yuzu. Disables our
bundled SDL2 to use the system ones that have been modified to not use
`-mwindows`. Also set's `-e` to stop the script on an error (as opposed
to packaging nothing).

Uses LLVM's linker for linking yuzu. Adds -femulated-tls due to a
libstdc++ incompatibility between GCC and Clang in vulkan_common.
2022-06-16 23:57:39 -04:00
lat9nq
fef3d8acb5 CMakeModules: Add MinGWClangCross
Facilitates what programs we need for cross-compiling to Windows from
Linux using LLVM's compilers. Based on MinGWCross
2022-06-16 23:57:39 -04:00
lat9nq
e56410b404 ci/windows: Split up cmake command
Improves readability.
2022-06-16 23:57:39 -04:00
Liam
a6371fb69d core: fix initialization in single core, sync GPU mode 2022-06-16 23:43:35 -04:00
Morph
a33e7c13fa Merge pull request #8472 from german77/tace
common: param_package: Demote DEBUG to TRACE for getters
2022-06-16 16:43:32 -04:00
Morph
945f3222ae Merge pull request #8474 from DCNick3/yuzu-cmd-respect-log-filter
Make yuzu-cmd respect log_filter setting
2022-06-16 16:43:18 -04:00
Nikita Strygin
9e384ed54b Make yuzu-cmd respect log_filter setting
Because logging infrastructure initializes before the loading of the
config, it reads the default setting for log_filter and ignores the one
set in config. To change log_filter after logging initialization some
additional calls need to be made.
2022-06-16 23:39:50 +03:00
liamwhite
561f5c9c14 Merge pull request #8473 from DCNick3/implement-exit-process
Implement ExitProcess svc
2022-06-16 15:45:02 -04:00
Nikita Strygin
cf7e4bda92 Implement ExitProcess svc
Currently this just stops all the emulation
This works under assumption that only application will try to use
ExitProcess, with services not touching it
If application exits - it quite makes sense to end the emulation
2022-06-16 21:35:34 +03:00
Liam
208ed712f4 core/debugger: memory breakpoint support 2022-06-16 13:18:07 -04:00
Narr the Reg
d1f2f5f146 common: param_package: Demote DEBUG to TRACE for getters 2022-06-16 10:27:59 -05:00
Liam
744a208763 kernel: fix some uses of disable_count 2022-06-15 20:53:49 -04:00
Fernando S
f86b770ff7 Merge pull request #8457 from liamwhite/kprocess-suspend
kernel: implement KProcess suspension
2022-06-16 02:41:12 +02:00
liamwhite
0ae4eae9a6 Merge pull request #8460 from Morph1984/bounded-q
bounded_threadsafe_queue: Use constexpr capacity and mask
2022-06-15 19:39:22 -04:00
Morph
25429998e3 bounded_threadsafe_queue: Use constexpr capacity and mask
While this is the primary change, we also:
- Remove the mpsc namespace and rename Queue to MPSCQueue
- Make Slot a private struct within MPSCQueue
- Remove the AlignedAllocator template argument, as we use std::allocator
- Replace instances of mask + 1 with capacity, and mask + 2 with capacity + 1
2022-06-15 16:59:13 -04:00
bunnei
5ace5c1b7a Merge pull request #8317 from german77/notifa
service: notifa: Implement most part of this service
2022-06-15 09:53:50 -07:00
Mai
23514388ed Merge pull request #8464 from liamwhite/break-debug
kernel: notify debugger on break SVC
2022-06-15 11:55:54 -04:00
Mai
f117351783 Merge pull request #8465 from Morph1984/why-msvc
vk_compute_pass: Explicitly cast to VkAccessFlags
2022-06-15 11:55:40 -04:00
Morph
4572634a4e vk_compute_pass: Explicitly cast to VkAccessFlags
According to the standard, a narrowing conversion is an implicit conversion from an integer or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except when the value is a literal or constant expression.
MSVC, unlike GCC or Clang, determines this to be a narrowing conversion despite the enumeration exclusively containing values that fit within the range of a 32 bit integer, emitting a warning since designated initializers prohibit narrowing conversions.
To solve this, explicitly cast to the type we are initializing.
2022-06-15 07:12:16 -04:00
Mai
103997ee56 Merge pull request #8383 from Morph1984/shadow-of-the-past
yuzu: Make variable shadowing a compile-time error
2022-06-14 21:08:58 -04:00
Mai
c9de5474bf Merge pull request #8462 from liamwhite/dynarmic-profile
core: centralize profile scope for Dynarmic
2022-06-14 21:07:47 -04:00
Liam
a7358ff1d4 kernel: notify debugger on break SVC 2022-06-14 21:06:23 -04:00
Liam
20eab9fed9 core: centralize profile scope for Dynarmic 2022-06-14 18:19:04 -04:00
Morph
7620e1a631 externals: Update cpp-httplib to latest 2022-06-14 14:09:51 -04:00
Morph
0eeee431dc main: Eliminate variable shadowing 2022-06-14 14:09:51 -04:00
Liam
888f499188 kernel: implement KProcess suspension 2022-06-14 10:04:11 -04:00
Morph
c6e7ca562a Merge pull request #8461 from Morph1984/msvc-narrow-conv
vk_compute_pass: Use VK_ACCESS_NONE
2022-06-14 09:28:45 -04:00
Morph
a9b4dd022c Merge pull request #8434 from german77/uuid
input_common: Replace usage of string guid to common uuid
2022-06-14 09:28:32 -04:00
Morph
5568763a57 vk_compute_pass: Use VK_ACCESS_NONE
This enumeration was introduced in Vulkan 1.3, prefer using this instead of defaulting the enum.

Also resolves a narrowing conversion warning on MSVC.
2022-06-14 09:14:13 -04:00
Mai
a3b12e3809 Merge pull request #8439 from liamwhite/monkey-compiler
general: fix compilation on GCC 12
2022-06-14 08:34:16 -04:00
Morph
742f021fdf wait_tree: Eliminate variable shadowing 2022-06-14 08:30:09 -04:00
Morph
95bcf6ac38 configure_ringcon: Eliminate variable shadowing 2022-06-14 08:30:09 -04:00
Morph
e371961219 configure_touch_from_button: Eliminate variable shadowing 2022-06-14 08:30:09 -04:00
Morph
5503338f21 configure_per_game: Eliminate variable shadowing 2022-06-14 08:30:08 -04:00
Morph
fe7184c2a8 configure_input_player: Eliminate variable shadowing 2022-06-14 08:30:08 -04:00
Morph
1c83014526 configure_dialog: Eliminate variable shadowing 2022-06-14 08:30:08 -04:00
Morph
2d903e3ce6 bootmanager: Eliminate variable shadowing 2022-06-14 08:30:08 -04:00
Morph
e29e8eec2f game_list: Eliminate variable shadowing 2022-06-14 08:30:07 -04:00
Mai
dc47d0f624 Merge pull request #8459 from Morph1984/wextra-gcc
vk_compute_pass: Silence Wextra warning
2022-06-14 08:22:38 -04:00
Morph
8b55f2c615 externals: microprofileui: Eliminate variable shadowing 2022-06-14 05:52:15 -04:00
Morph
fcfe192e83 vk_compute_pass: Silence Wextra warning
Silences a warning about using enumerated and non-enumerated types in a conditional expression.
2022-06-14 05:29:57 -04:00
Liam
bd38aefc57 kernel: fix passthrough of local captures in lambda 2022-06-13 20:09:32 -04:00
Liam
feaf010fa2 common/assert: rework ASSERT handling to avoid std::function usage 2022-06-13 20:09:32 -04:00
Liam
ebecdd3a74 general: fix compilation on MinGW GCC 12 2022-06-13 20:09:32 -04:00
Liam
a29ddcee40 common/assert: add unlikely 2022-06-13 20:09:32 -04:00
Liam
d11547024c general: fix compilation on GCC 12 2022-06-13 20:09:30 -04:00
Liam
6f59e2676b kernel: ensure class token lambda exit is unreachable 2022-06-13 20:09:00 -04:00
Liam
8fea7e56e5 kernel: fix inconsistency in AutoObjectTraits macro definitions 2022-06-13 20:09:00 -04:00
Liam
58fea44eb5 common: Don't test ASSERT conditions inline 2022-06-13 20:09:00 -04:00
Liam
084d7d6b01 common: Change semantics of UNREACHABLE to unconditionally crash 2022-06-13 20:09:00 -04:00
liamwhite
bd3bfe411d Merge pull request #8458 from lat9nq/no-constexpr-flow-block
structured_control_flow: Remove constexpr Flow::Block
2022-06-13 20:06:38 -04:00
lat9nq
963ed37fd6 structured_control_flow: Remove constexpr Flow::Block
This seems to be unsupported in newer libstdc++ versions due to
Flow::Block's base class being a non-literal type. It's not clear to me
why this was permitted in earlier versions.
2022-06-13 19:18:20 -04:00
bunnei
741da9c8bf Merge pull request #8388 from liamwhite/simpler-pause
CpuManager: simplify pausing
2022-06-13 15:48:03 -07:00
Morph
69d92a19a5 yuzu_cmd: Eliminate variable shadowing 2022-06-13 18:19:23 -04:00
Morph
8671aa8dd0 audio_core: Remove -Werror=unused-parameter
Removing this as we don't enforce unused parameter warnings elsewhere in the project, and explicitly specify -Wno-unused-parameter in the main CMakeLists.
2022-06-13 18:19:23 -04:00
Morph
efc89c032b CMakeLists: Make variable shadowing a compile-time error
Now that the entire project is free of variable shadowing, we can enforce this as a compile time error to prevent any further introduction of this logic bug.
2022-06-13 18:19:23 -04:00
Morph
d0328f49f1 externals: microprofile: Eliminate variable shadowing 2022-06-13 18:19:23 -04:00
Morph
c1bd602e4c common: Eliminate variable shadowing
GCC/Clang treats variables within lambdas as potentially shadowing those outside the lambda, despite them not being captured inside the lambda's capture list.
2022-06-13 18:19:22 -04:00
Morph
b3d6f7bdd8 yuzu: Eliminate variable shadowing 2022-06-13 18:19:22 -04:00
Morph
12156b199a web_service: Eliminate variable shadowing 2022-06-13 18:19:22 -04:00
Morph
a0407a8e64 Merge pull request #8446 from liamwhite/cmd-gdb
core/debugger: support operation in yuzu-cmd
2022-06-13 14:38:37 -04:00
Morph
7582717c9d Merge pull request #8454 from liamwhite/inaddr-any
core/debugger: allow remote connections
2022-06-13 14:38:20 -04:00
bunnei
ec85eac3c9 Merge pull request #8443 from liamwhite/code-mem
kernel: fix KCodeMemory initialization
2022-06-13 11:32:27 -07:00
Liam
fb4b507ba4 core/debugger: allow remote connections 2022-06-12 11:50:50 -04:00
liamwhite
7ea78699a1 Merge pull request #8450 from lioncash/undef
gdbstub_arch: Add missing virtual destructor
2022-06-11 19:59:18 -04:00
Lioncash
80ad90651e gdbstub_arch: Add missing virtual destructor
The class is used polymorphically, so it's undefined behavior to delete
instances of GDBStubA64 and GDBStubA32 from the base class pointer.
2022-06-11 18:23:22 -04:00
Mai M
b94739cfa7 Merge pull request #8353 from Docteh/msvc_report_runtime
log the MSVC runtime version when running on MSVC build
2022-06-11 13:21:23 -04:00
Mai M
89e00c442d Merge pull request #8427 from Docteh/deprecate_qdesktop
deprecate usage of QDesktopWidget for going fullscreen
2022-06-11 13:20:36 -04:00
Mai M
d796341d33 Merge pull request #8449 from Docteh/translate_placeholder
retranslate the game list placeholder
2022-06-11 13:19:18 -04:00
bunnei
5282efac1b Merge pull request #8413 from behunin/bounded-queue
gpu_thread: Move to bounded queue
2022-06-11 00:07:18 -07:00
bunnei
ae83d5c6d3 Merge pull request #8393 from lat9nq/default-vulkan
general: Set renderer_backend's default to Vulkan
2022-06-11 00:06:59 -07:00
Kyle Kienapfel
3370546a7a log the MSVC runtime version when running on MSVC build
This might be useful information, not 100% sure.

[   0.958068] Frontend <Info> yuzu\main.cpp:GMainWindow:275: yuzu Version: yuzu Development Build | master-0b9ef3c0b-dirty
[   0.958095] Frontend <Info> yuzu\main.cpp:LogRuntimes:220: MSVC Compiler: 1931 Runtime: 14.32.31326.0
2022-06-10 20:37:47 -07:00
Kyle Kienapfel
2ff606628c UI: retranslate the game list placeholder
This is the "Double-click to add a new folder to the game list" message
that shows up when users first launch yuzu and is most likely never seen
again. Previously this message was not re-translated.
2022-06-10 20:15:52 -07:00
Mai M
20576ebb43 Merge pull request #8405 from Docteh/dock_undock
ui: Status bars dock button becomes DOCKED/HANDHELD button
2022-06-10 23:11:29 -04:00
Mai M
6f81160160 Merge pull request #8333 from Docteh/translate_hotkeys
UI: Translate hotkey labels in configuration
2022-06-10 23:10:28 -04:00
Mai M
266e086706 Merge pull request #8318 from Docteh/cmake-qt56-entry
Update some files with Qt 5.15.2 best practices in mind
2022-06-10 23:09:49 -04:00
Mai M
9561a2f5b1 Merge pull request #8448 from german77/gesturetypo
service: hid: Fix gesture regression
2022-06-10 15:09:22 -04:00
Narr the Reg
bc8699a9fa service: hid: Fix gesture regression 2022-06-10 13:14:31 -05:00
Liam
c3cc65a11e yuzu-cmd: ignore bogus timeous from SDL 2022-06-10 12:49:18 -04:00
Liam
1f0fee33ed core/debugger: fix a number of shutdown deadlocks 2022-06-10 09:17:12 -04:00
Liam
de6c0defb3 core/debugger: support operation in yuzu-cmd 2022-06-10 09:11:02 -04:00
Liam
6c659c3a16 kernel: fix KCodeMemory initialization 2022-06-09 12:33:28 -04:00
Liam
af022294dd CpuManager: simplify pausing 2022-06-08 21:47:29 -04:00
Narr the Reg
28877cea31 input_common: Replace usage of string guid to common uuid 2022-06-06 19:56:37 -05:00
Kyle Kienapfel
941b663352 deprecate usage of QDesktopWidget for going fullscreen
Idea works as follows, while going fullscreen we compare the current window geometry with
available screens and ask for an intersection rectangle, we go fullscreen where most of
the window is located

GuessCurrentScreen could also potentially be used to see which screen
the window is on for dynamic DPI handling
2022-06-05 20:18:27 -07:00
lat9nq
4544407af6 configure_graphics: Remove unused include 2022-06-04 04:18:21 -04:00
Levi Behunin
4dd6bcd206 gpu_thread: Move to bounded queue 2022-06-02 19:37:46 -06:00
Kyle Kienapfel
054732210e ui: Status bars dock button becomes dock/undock button
For people not used to the Yuzu UI it's not always clear if the emulated
console is docked or not.  The other items update their text when clicked,
this PR brings the DOCK button in line with this.

DOCK -> DOCKED or HANDHELD
2022-06-01 17:22:53 -07:00
lat9nq
422525e3fb main: Insert warning text on broken Vulkan
Co-authored-by: Schplee <24275329+Schplee@users.noreply.github.com>
2022-05-30 10:58:19 -04:00
lat9nq
2dafb27055 main: Save config on broken Vulkan detect
Prevents possible issues if someone were to open yuzu repeatedly over
and over again.
2022-05-30 10:58:19 -04:00
lat9nq
500b01076e yuzu-qt: Make has_broken_vulkan only for crashes
Being able to catch and handle a Vulkan exception is not what this is
for.
2022-05-30 10:58:18 -04:00
lat9nq
b43ae9d5ed vulkan_library: Add debug logging 2022-05-30 10:57:59 -04:00
lat9nq
f22867efc5 yuzu-qt: Attempt to workaround broken Vulkan installations
This does a few things in order to make the default setting Vulkan
workable.

- When yuzu boots, it just opens the Vulkan library.
  - If it works, all good and we continue with Vulkan as the default.
  - If something breaks, a new file in the config directory will be left
    behind (this is deleted normally).
- If Vulkan is not working, has_broken_vulkan is set to true.
  - The first time this happens, a warning is displayed to notify the
    user.
  - This forces use of OpenGL, and Vulkan cannot be selected.
  - The Shader Backend selector is made accessible for use in custom
    configurations.
  - To disable has_broken_vulkan, the user needs to press a button in
    Graphics Configuration to manually run the Vulkan device
    enumeration.
2022-05-30 10:57:59 -04:00
lat9nq
67fa743414 default_ini: Reflect new renderer backend default setting 2022-05-29 21:38:36 -04:00
lat9nq
5799fa4d7d settings: Set Vulkan to the default renderer backend 2022-05-29 21:38:36 -04:00
Kyle K
499c89790b motion touch ui: move remaining connection out of .ui file
Two reasons for this:
1. Out of 7 connections, 6 are in ConfigureMotionTouch::ConnectEvents,
   this is the outlier.
2. Qt6 doesn't moc the connection properly
2022-05-29 18:37:38 -07:00
Kyle K
75bf2c20eb Update some files with Qt 5.15.2 best practices in mind
There was some discussion about updating to Qt6 and I figured I would
work on some smaller parts. For Windows platform the WinMain function has moved
from the Qt5::WinMain to a new one called Qt6::EntryPointPrivate

Also Qt5 supports versionless CMake targets
https://www.qt.io/blog/versionless-cmake-targets-qt-5.15

These other changes in this commit are to support Qt6, but in ways that don't mess with Qt5.

src/yuzu/bootmanager.cpp: Qt6 complains about not being able to know to use QPoint or QPointF, picking QPoint
src/yuzu/bootmanager.h: Qt6 prefers that QStringList.h be included rather than an empty class definition
src/yuzu/configuration/configure_system.cpp: toULongLong intends to return unsigned 64 bit integer, but
   Settings::values.rng_seed is only 32 bits wide
src/yuzu/game_list.cpp: Qt6 returns a different datatype for QStringList.length than Qt5,
   it used to be int, but in Qt6 its now qsizetype
src/yuzu/loading_screen.cpp: Qt5's for QStyleOption.init say to switch to initFrom.
   The QStyleOption.init doesn't exist in Qt6
src/yuzu/main.cpp: Another QPointer and QStringList.size, lets standardize on size()
2022-05-29 09:21:52 -07:00
Kyle K
669a9a644d UI: Translate hotkey labels in configuration
Another request from GillianMC.

The translated strings have been placed in a separate "Hotkeys" context as an alternative
 to having to add the tr function to the Config class, or adding them to ConfigureHotkeys
context which is quite long. The English strings get attached to the items in the Action
column as "data", and are used for RetranslateUI and saving the hotkey configuration.
2022-05-18 22:05:19 -07:00
german77
cc6a4bedfc service: notifa: Implement most part of this service
Implements partially RegisterAlarmSetting, UpdateAlarmSetting, LoadApplicationParameter, DeleteAlarmSetting.
Needed for Fitness `Boxing 2: Rhythm & Exercise` and `Ring Fit Adventure`.
2022-05-09 10:28:04 -05:00
188 changed files with 2323 additions and 1045 deletions

View File

@@ -1,12 +1,27 @@
#!/bin/bash -ex
set -e
cd /yuzu
ccache -s
mkdir build || true && cd build
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON
ninja
LDFLAGS="-fuse-ld=lld"
# -femulated-tls required due to an incompatibility between GCC and Clang
# TODO(lat9nq): If this is widespread, we probably need to add this to CMakeLists where appropriate
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_FLAGS="-femulated-tls" \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWClangCross.cmake" \
-DDISPLAY_VERSION=$1 \
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
-DENABLE_QT_TRANSLATION=ON \
-DUSE_CCACHE=ON \
-DYUZU_USE_BUNDLED_SDL2=OFF \
-DYUZU_USE_EXTERNAL_SDL2=OFF \
-GNinja
ninja yuzu yuzu-cmd
ccache -s

View File

@@ -0,0 +1,55 @@
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
set(SDL2_PATH ${MINGW_PREFIX})
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
# Specify the cross compiler
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}clang)
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}clang++)
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
set(CMAKE_C_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
set(CMAKE_CXX_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
set(CMAKE_C_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
set(CMAKE_CXX_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
# Mingw tools
set(STRIP ${MINGW_TOOL_PREFIX}strip)
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
# ccache wrapper
option(USE_CCACHE "Use ccache for compilation" OFF)
if(USE_CCACHE)
find_program(CCACHE ccache)
if(CCACHE)
message(STATUS "Using ccache found in PATH")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
else(CCACHE)
message(WARNING "USE_CCACHE enabled, but no ccache found")
endif(CCACHE)
endif(USE_CCACHE)
# Search for programs in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Echo modified cmake vars to screen for debugging purposes
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
message("")
message("Custom cmake vars: (blank = system default)")
message("-----------------------------------------")
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
message("* WINDRES : ${WINDRES}")
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
message("* STRIP : ${STRIP}")
message("* USE_CCACHE : ${USE_CCACHE}")
message("")
# So that the debug info only appears once
set(ENV{MINGW_DEBUG_INFO} SHOWN)
endif()

View File

@@ -58,6 +58,19 @@ QPushButton#GPUStatusBarButton:!checked {
color: #109010;
}
QPushButton#DockingStatusBarButton {
min-width: 0px;
color: #000000;
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#DockingStatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#buttonRefreshDevices {
min-width: 21px;
min-height: 21px;

View File

@@ -1304,6 +1304,19 @@ QPushButton#GPUStatusBarButton:!checked {
color: #40dd40;
}
QPushButton#DockingStatusBarButton {
min-width: 0px;
color: #ffffff;
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#DockingStatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#buttonRefreshDevices {
min-width: 23px;
min-height: 23px;

View File

@@ -2207,6 +2207,19 @@ QPushButton#GPUStatusBarButton:!checked {
color: #40dd40;
}
QPushButton#DockingStatusBarButton {
min-width: 0px;
color: #ffffff;
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#DockingStatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#buttonRefreshDevices {
min-width: 19px;
min-height: 19px;

View File

@@ -40,6 +40,11 @@ target_include_directories(mbedtls PUBLIC ./mbedtls/include)
add_library(microprofile INTERFACE)
target_include_directories(microprofile INTERFACE ./microprofile)
# GCC bugs
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND MINGW)
target_compile_options(microprofile INTERFACE "-Wno-array-bounds")
endif()
# libusb
if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
add_subdirectory(libusb)

View File

@@ -1246,7 +1246,7 @@ struct MicroProfileScopeLock
{
bool bUseLock;
std::recursive_mutex& m;
MicroProfileScopeLock(std::recursive_mutex& m) : bUseLock(g_bUseLock), m(m)
MicroProfileScopeLock(std::recursive_mutex& m_) : bUseLock(g_bUseLock), m(m_)
{
if(bUseLock)
m.lock();

View File

@@ -213,8 +213,8 @@ struct MicroProfileCustom
struct SOptionDesc
{
SOptionDesc(){}
SOptionDesc(uint8_t nSubType, uint8_t nIndex, const char* fmt, ...):nSubType(nSubType), nIndex(nIndex)
SOptionDesc()=default;
SOptionDesc(uint8_t nSubType_, uint8_t nIndex_, const char* fmt, ...):nSubType(nSubType_), nIndex(nIndex_)
{
va_list args;
va_start (args, fmt);
@@ -573,10 +573,10 @@ inline void MicroProfileToolTipMeta(MicroProfileStringArray* pToolTip)
}
else
{
for(int i = 0; i < MICROPROFILE_META_MAX; ++i)
for(int k = 0; k < MICROPROFILE_META_MAX; ++k)
{
nMetaSumInclusive[i] += nMetaSum[i];
nMetaSum[i] = 0;
nMetaSumInclusive[k] += nMetaSum[k];
nMetaSum[k] = 0;
}
}
break;
@@ -708,10 +708,10 @@ inline void MicroProfileDrawFloatTooltip(uint32_t nX, uint32_t nY, uint32_t nTok
if(UI.nMouseLeftMod)
{
int nIndex = (g_MicroProfileUI.LockedToolTipFront + MICROPROFILE_TOOLTIP_MAX_LOCKED - 1) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
g_MicroProfileUI.nLockedToolTipColor[nIndex] = S.TimerInfo[nTimerId].nColor;
MicroProfileStringArrayCopy(&g_MicroProfileUI.LockedToolTips[nIndex], &ToolTip);
g_MicroProfileUI.LockedToolTipFront = nIndex;
int nToolTipIndex = (g_MicroProfileUI.LockedToolTipFront + MICROPROFILE_TOOLTIP_MAX_LOCKED - 1) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
g_MicroProfileUI.nLockedToolTipColor[nToolTipIndex] = S.TimerInfo[nTimerId].nColor;
MicroProfileStringArrayCopy(&g_MicroProfileUI.LockedToolTips[nToolTipIndex], &ToolTip);
g_MicroProfileUI.LockedToolTipFront = nToolTipIndex;
}
}
@@ -917,9 +917,8 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
float fStart = floor(fMsBase*fRcpStep) * fStep;
for(float f = fStart; f < fMsEnd; )
{
float fStart = f;
float fNext = f + fStep;
MicroProfileDrawBox(((fStart-fMsBase) * fMsToScreen), nBaseY, (fNext-fMsBase) * fMsToScreen+1, nBaseY + nHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[nColorIndex++ & 1]);
MicroProfileDrawBox(((f-fMsBase) * fMsToScreen), nBaseY, (fNext-fMsBase) * fMsToScreen+1, nBaseY + nHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[nColorIndex++ & 1]);
f = fNext;
}
}
@@ -1116,9 +1115,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
nMaxStackDepth = MicroProfileMax(nMaxStackDepth, nStackPos);
float fMsStart = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickStart);
float fMsEnd = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickEnd);
float fMsEnd2 = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickEnd);
float fXStart = fMsStart * fMsToScreen;
float fXEnd = fMsEnd * fMsToScreen;
float fXEnd = fMsEnd2 * fMsToScreen;
float fYStart = (float)(nY + nStackPos * nYDelta);
float fYEnd = fYStart + (MICROPROFILE_DETAILED_BAR_HEIGHT);
float fXDist = MicroProfileMax(fXStart - fMouseX, fMouseX - fXEnd);
@@ -1269,22 +1268,22 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
if(UI.nRangeBegin != UI.nRangeEnd)
{
float fMsStart = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeBegin);
float fMsEnd = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeEnd);
float fMsEnd3 = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeEnd);
float fXStart = fMsStart * fMsToScreen;
float fXEnd = fMsEnd * fMsToScreen;
float fXEnd = fMsEnd3 * fMsToScreen;
MicroProfileDrawBox(fXStart, nBaseY, fXEnd, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT, MicroProfileBoxTypeFlat);
MicroProfileDrawLineVertical(fXStart, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT | 0x44000000);
MicroProfileDrawLineVertical(fXEnd, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT | 0x44000000);
fMsStart += fDetailedOffset;
fMsEnd += fDetailedOffset;
fMsEnd3 += fDetailedOffset;
char sBuffer[32];
uint32_t nLenStart = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsStart);
float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
float fStartTextX = fXStart - fStartTextWidth - 2;
MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd3);
MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
@@ -1297,9 +1296,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
if(UI.nRangeBeginGpu != UI.nRangeEndGpu)
{
float fMsStart = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeBeginGpu);
float fMsEnd = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeEndGpu);
float fMsEnd4 = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeEndGpu);
float fXStart = fMsStart * fMsToScreen;
float fXEnd = fMsEnd * fMsToScreen;
float fXEnd = fMsEnd4 * fMsToScreen;
MicroProfileDrawBox(fXStart, nBaseY, fXEnd, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU, MicroProfileBoxTypeFlat);
MicroProfileDrawLineVertical(fXStart, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU | 0x44000000);
MicroProfileDrawLineVertical(fXEnd, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU | 0x44000000);
@@ -1307,14 +1306,14 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
nBaseY += MICROPROFILE_TEXT_HEIGHT+1;
fMsStart += fDetailedOffset;
fMsEnd += fDetailedOffset;
fMsEnd4 += fDetailedOffset;
char sBuffer[32];
uint32_t nLenStart = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsStart);
float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
float fStartTextX = fXStart - fStartTextWidth - 2;
MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd4);
MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
}
@@ -1716,8 +1715,8 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
uint32_t nTextCount = 0;
uint32_t nGraphIndex = (S.nGraphPut + MICROPROFILE_GRAPH_HISTORY - int(MICROPROFILE_GRAPH_HISTORY*(1.f - fMouseXPrc))) % MICROPROFILE_GRAPH_HISTORY;
uint32_t nX = UI.nMouseX;
uint32_t nY = UI.nMouseY + 20;
uint32_t nMouseX = UI.nMouseX;
uint32_t nMouseY = UI.nMouseY + 20;
for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
{
@@ -1736,7 +1735,7 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
}
if(nTextCount)
{
MicroProfileDrawFloatWindow(nX, nY, Strings.ppStrings, Strings.nNumStrings, 0, pColors);
MicroProfileDrawFloatWindow(nMouseX, nMouseY, Strings.ppStrings, Strings.nNumStrings, 0, pColors);
}
if(UI.nMouseRight)
@@ -2321,8 +2320,8 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
uint32_t nMenuX[MICROPROFILE_MENU_MAX] = {0};
uint32_t nNumMenuItems = 0;
int nLen = snprintf(buffer, 127, "MicroProfile");
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
int nMPTextLen = snprintf(buffer, 127, "MicroProfile");
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nMPTextLen);
nX += (sizeof("MicroProfile")+2) * (MICROPROFILE_TEXT_WIDTH+1);
pMenuText[nNumMenuItems++] = "Mode";
pMenuText[nNumMenuItems++] = "Groups";
@@ -2438,16 +2437,16 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
int nNumLines = 0;
bool bSelected = false;
const char* pString = CB(nNumLines, &bSelected);
uint32_t nWidth = 0, nHeight = 0;
uint32_t nTextWidth = 0, nTextHeight = 0;
while(pString)
{
nWidth = MicroProfileMax<int>(nWidth, (int)strlen(pString));
nTextWidth = MicroProfileMax<int>(nTextWidth, (int)strlen(pString));
nNumLines++;
pString = CB(nNumLines, &bSelected);
}
nWidth = (2+nWidth) * (MICROPROFILE_TEXT_WIDTH+1);
nHeight = nNumLines * (MICROPROFILE_TEXT_HEIGHT+1);
if(UI.nMouseY <= nY + nHeight+0 && UI.nMouseY >= nY-0 && UI.nMouseX <= nX + nWidth + 0 && UI.nMouseX >= nX - 0)
nTextWidth = (2+nTextWidth) * (MICROPROFILE_TEXT_WIDTH+1);
nTextHeight = nNumLines * (MICROPROFILE_TEXT_HEIGHT+1);
if(UI.nMouseY <= nY + nTextHeight+0 && UI.nMouseY >= nY-0 && UI.nMouseX <= nX + nTextWidth + 0 && UI.nMouseX >= nX - 0)
{
UI.nActiveMenu = nMenu;
}
@@ -2455,21 +2454,21 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
{
UI.nActiveMenu = UINT32_MAX;
}
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000|g_nMicroProfileBackColors[1]);
MicroProfileDrawBox(nX, nY, nX + nTextWidth, nY + nTextHeight, 0xff000000|g_nMicroProfileBackColors[1]);
for(int i = 0; i < nNumLines; ++i)
{
bool bSelected = false;
const char* pString = CB(i, &bSelected);
bool bSelected2 = false;
const char* pString2 = CB(i, &bSelected2);
if(UI.nMouseY >= nY && UI.nMouseY < nY + MICROPROFILE_TEXT_HEIGHT + 1)
{
if(UI.nMouseLeft || UI.nMouseRight)
{
CBClick[nMenu](i);
}
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888);
MicroProfileDrawBox(nX, nY, nX + nTextWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888);
}
int nLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected ? '*' : ' ' ,pString);
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
int nTextLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected2 ? '*' : ' ' ,pString2);
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nTextLen);
nY += MICROPROFILE_TEXT_HEIGHT+1;
}
}
@@ -2605,7 +2604,7 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
for(uint32_t i = 0; i < nCount; ++i)
{
nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
uint32_t nWidth = MicroProfileMin(nMaxWidth, (uint32_t)(nMaxWidth * pMs[i] * fRcpReference));
nWidth = MicroProfileMin(nMaxWidth, (uint32_t)(nMaxWidth * pMs[i] * fRcpReference));
MicroProfileDrawBox(nMaxOffsetX, nOffsetY, nMaxOffsetX+nWidth, nOffsetY+MICROPROFILE_TEXT_HEIGHT, pColors[i]|0xff000000);
}
}

View File

@@ -65,6 +65,10 @@ if (MSVC)
/we4305 # 'context': truncation from 'type1' to 'type2'
/we4388 # 'expression': signed/unsigned mismatch
/we4389 # 'operator': signed/unsigned mismatch
/we4456 # Declaration of 'identifier' hides previous local declaration
/we4457 # Declaration of 'identifier' hides function parameter
/we4458 # Declaration of 'identifier' hides class member
/we4459 # Declaration of 'identifier' hides global declaration
/we4505 # 'function': unreferenced local function has been removed
/we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
@@ -92,6 +96,7 @@ else()
-Werror=missing-declarations
-Werror=missing-field-initializers
-Werror=reorder
-Werror=shadow
-Werror=sign-compare
-Werror=switch
-Werror=uninitialized

View File

@@ -49,9 +49,6 @@ if (NOT MSVC)
target_compile_options(audio_core PRIVATE
-Werror=conversion
-Werror=ignored-qualifiers
-Werror=shadow
-Werror=unused-parameter
-Werror=unused-variable
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>

View File

@@ -429,7 +429,7 @@ void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, Vo
in_params.node_id);
break;
default:
UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
}
}
}
@@ -1312,7 +1312,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::s
samples_to_read - samples_read, channel, temp_mix_offset);
break;
default:
UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
}
temp_mix_offset += samples_decoded;

View File

@@ -50,7 +50,7 @@ EffectBase* EffectContext::RetargetEffect(std::size_t i, EffectType effect) {
effects[i] = std::make_unique<EffectBiquadFilter>();
break;
default:
UNREACHABLE_MSG("Unimplemented effect {}", effect);
ASSERT_MSG(false, "Unimplemented effect {}", effect);
effects[i] = std::make_unique<EffectStubbed>();
}
return GetInfo(i);
@@ -104,7 +104,7 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
auto& params = GetParams();
const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
ASSERT_MSG(false, "Invalid reverb max channel count {}", reverb_params->max_channels);
return;
}

View File

@@ -483,7 +483,7 @@ bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
// Add more work
index_stack.push(j);
} else if (node_state == NodeStates::State::InFound) {
UNREACHABLE_MSG("Node start marked as found");
ASSERT_MSG(false, "Node start marked as found");
ResetState();
return false;
}

View File

@@ -114,7 +114,7 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
in_params.current_playstate = ServerPlayState::Play;
break;
default:
UNREACHABLE_MSG("Unknown playstate {}", voice_in.play_state);
ASSERT_MSG(false, "Unknown playstate {}", voice_in.play_state);
break;
}
@@ -410,7 +410,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
return in_params.should_depop;
}
default:
UNREACHABLE_MSG("Invalid playstate {}", in_params.current_playstate);
ASSERT_MSG(false, "Invalid playstate {}", in_params.current_playstate);
}
return false;

View File

@@ -6,8 +6,13 @@
#include "common/settings.h"
void assert_handle_failure() {
void assert_fail_impl() {
if (Settings::values.use_debug_asserts) {
Crash();
}
}
[[noreturn]] void unreachable_impl() {
Crash();
throw std::runtime_error("Unreachable code");
}

View File

@@ -9,44 +9,43 @@
// Sometimes we want to try to continue even after hitting an assert.
// However touching this file yields a global recompilation as this header is included almost
// everywhere. So let's just move the handling of the failed assert to a single cpp file.
void assert_handle_failure();
// For asserts we'd like to keep all the junk executed when an assert happens away from the
// important code in the function. One way of doing this is to put all the relevant code inside a
// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to
// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper
// template that calls the lambda. This seems to generate an extra instruction at the call-site
// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good
// enough for our purposes.
template <typename Fn>
#if defined(_MSC_VER)
[[msvc::noinline]]
#elif defined(__GNUC__)
[[gnu::cold, gnu::noinline]]
void assert_fail_impl();
[[noreturn]] void unreachable_impl();
#ifdef _MSC_VER
#define YUZU_NO_INLINE __declspec(noinline)
#else
#define YUZU_NO_INLINE __attribute__((noinline))
#endif
static void
assert_noinline_call(const Fn& fn) {
fn();
assert_handle_failure();
}
#define ASSERT(_a_) \
do \
if (!(_a_)) { \
assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
([&]() YUZU_NO_INLINE { \
if (!(_a_)) [[unlikely]] { \
LOG_CRITICAL(Debug, "Assertion Failed!"); \
assert_fail_impl(); \
} \
while (0)
}())
#define ASSERT_MSG(_a_, ...) \
do \
if (!(_a_)) { \
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
([&]() YUZU_NO_INLINE { \
if (!(_a_)) [[unlikely]] { \
LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
assert_fail_impl(); \
} \
while (0)
}())
#define UNREACHABLE() \
do { \
LOG_CRITICAL(Debug, "Unreachable code!"); \
unreachable_impl(); \
} while (0)
#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
#define UNREACHABLE_MSG(...) \
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); })
do { \
LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); \
unreachable_impl(); \
} while (0)
#ifdef _DEBUG
#define DEBUG_ASSERT(_a_) ASSERT(_a_)

View File

@@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
// SPDX-License-Identifier: MIT
#pragma once
#include <atomic>
#include <bit>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <new>
#include <stop_token>
#include <type_traits>
#include <utility>
namespace Common {
#if defined(__cpp_lib_hardware_interference_size)
constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size;
#else
constexpr size_t hardware_interference_size = 64;
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4324)
#endif
template <typename T, size_t capacity = 0x400>
class MPSCQueue {
public:
explicit MPSCQueue() : allocator{std::allocator<Slot<T>>()} {
// Allocate one extra slot to prevent false sharing on the last slot
slots = allocator.allocate(capacity + 1);
// Allocators are not required to honor alignment for over-aligned types
// (see http://eel.is/c++draft/allocator.requirements#10) so we verify
// alignment here
if (reinterpret_cast<uintptr_t>(slots) % alignof(Slot<T>) != 0) {
allocator.deallocate(slots, capacity + 1);
throw std::bad_alloc();
}
for (size_t i = 0; i < capacity; ++i) {
std::construct_at(&slots[i]);
}
static_assert(std::has_single_bit(capacity), "capacity must be an integer power of 2");
static_assert(alignof(Slot<T>) == hardware_interference_size,
"Slot must be aligned to cache line boundary to prevent false sharing");
static_assert(sizeof(Slot<T>) % hardware_interference_size == 0,
"Slot size must be a multiple of cache line size to prevent "
"false sharing between adjacent slots");
static_assert(sizeof(MPSCQueue) % hardware_interference_size == 0,
"Queue size must be a multiple of cache line size to "
"prevent false sharing between adjacent queues");
}
~MPSCQueue() noexcept {
for (size_t i = 0; i < capacity; ++i) {
std::destroy_at(&slots[i]);
}
allocator.deallocate(slots, capacity + 1);
}
// The queue must be both non-copyable and non-movable
MPSCQueue(const MPSCQueue&) = delete;
MPSCQueue& operator=(const MPSCQueue&) = delete;
MPSCQueue(MPSCQueue&&) = delete;
MPSCQueue& operator=(MPSCQueue&&) = delete;
void Push(const T& v) noexcept {
static_assert(std::is_nothrow_copy_constructible_v<T>,
"T must be nothrow copy constructible");
emplace(v);
}
template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>>
void Push(P&& v) noexcept {
emplace(std::forward<P>(v));
}
void Pop(T& v, std::stop_token stop) noexcept {
auto const tail = tail_.fetch_add(1);
auto& slot = slots[idx(tail)];
if (!slot.turn.test()) {
std::unique_lock lock{cv_mutex};
cv.wait(lock, stop, [&slot] { return slot.turn.test(); });
}
v = slot.move();
slot.destroy();
slot.turn.clear();
slot.turn.notify_one();
}
private:
template <typename U = T>
struct Slot {
~Slot() noexcept {
if (turn.test()) {
destroy();
}
}
template <typename... Args>
void construct(Args&&... args) noexcept {
static_assert(std::is_nothrow_constructible_v<U, Args&&...>,
"T must be nothrow constructible with Args&&...");
std::construct_at(reinterpret_cast<U*>(&storage), std::forward<Args>(args)...);
}
void destroy() noexcept {
static_assert(std::is_nothrow_destructible_v<U>, "T must be nothrow destructible");
std::destroy_at(reinterpret_cast<U*>(&storage));
}
U&& move() noexcept {
return reinterpret_cast<U&&>(storage);
}
// Align to avoid false sharing between adjacent slots
alignas(hardware_interference_size) std::atomic_flag turn{};
struct aligned_store {
struct type {
alignas(U) unsigned char data[sizeof(U)];
};
};
typename aligned_store::type storage;
};
template <typename... Args>
void emplace(Args&&... args) noexcept {
static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
"T must be nothrow constructible with Args&&...");
auto const head = head_.fetch_add(1);
auto& slot = slots[idx(head)];
slot.turn.wait(true);
slot.construct(std::forward<Args>(args)...);
slot.turn.test_and_set();
cv.notify_one();
}
constexpr size_t idx(size_t i) const noexcept {
return i & mask;
}
static constexpr size_t mask = capacity - 1;
// Align to avoid false sharing between head_ and tail_
alignas(hardware_interference_size) std::atomic<size_t> head_{0};
alignas(hardware_interference_size) std::atomic<size_t> tail_{0};
std::mutex cv_mutex;
std::condition_variable_any cv;
Slot<T>* slots;
[[no_unique_address]] std::allocator<Slot<T>> allocator;
static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>,
"T must be nothrow copy or move assignable");
static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
} // namespace Common

View File

@@ -33,9 +33,9 @@ void DetachedTasks::AddTask(std::function<void()> task) {
++instance->count;
std::thread([task{std::move(task)}]() {
task();
std::unique_lock lock{instance->mutex};
std::unique_lock thread_lock{instance->mutex};
--instance->count;
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
std::notify_all_at_thread_exit(instance->cv, std::move(thread_lock));
}).detach();
}

View File

@@ -15,6 +15,9 @@ enum class PageType : u8 {
Unmapped,
/// Page is mapped to regular memory. This is the only type you can get pointers to.
Memory,
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
/// the callbacks.
DebugMemory,
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
/// invalidation
RasterizerCachedMemory,

View File

@@ -76,7 +76,7 @@ std::string ParamPackage::Serialize() const {
std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const {
auto pair = data.find(key);
if (pair == data.end()) {
LOG_DEBUG(Common, "key '{}' not found", key);
LOG_TRACE(Common, "key '{}' not found", key);
return default_value;
}
@@ -86,7 +86,7 @@ std::string ParamPackage::Get(const std::string& key, const std::string& default
int ParamPackage::Get(const std::string& key, int default_value) const {
auto pair = data.find(key);
if (pair == data.end()) {
LOG_DEBUG(Common, "key '{}' not found", key);
LOG_TRACE(Common, "key '{}' not found", key);
return default_value;
}
@@ -101,7 +101,7 @@ int ParamPackage::Get(const std::string& key, int default_value) const {
float ParamPackage::Get(const std::string& key, float default_value) const {
auto pair = data.find(key);
if (pair == data.end()) {
LOG_DEBUG(Common, "key {} not found", key);
LOG_TRACE(Common, "key {} not found", key);
return default_value;
}

View File

@@ -147,7 +147,7 @@ void UpdateRescalingInfo() {
info.down_shift = 0;
break;
default:
UNREACHABLE();
ASSERT(false);
info.up_scale = 1;
info.down_shift = 0;
}

View File

@@ -496,7 +496,7 @@ struct Values {
// Renderer
RangedSetting<RendererBackend> renderer_backend{
RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
BasicSetting<bool> renderer_debug{false, "debug"};
BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};

View File

@@ -743,16 +743,11 @@ if (MSVC)
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
/we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
/we4456 # Declaration of 'identifier' hides previous local declaration
/we4457 # Declaration of 'identifier' hides function parameter
/we4458 # Declaration of 'identifier' hides class member
/we4459 # Declaration of 'identifier' hides global declaration
)
else()
target_compile_options(core PRIVATE
-Werror=conversion
-Werror=ignored-qualifiers
-Werror=shadow
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>

View File

@@ -95,7 +95,7 @@ void ARM_Interface::Run() {
using Kernel::SuspendType;
while (true) {
Kernel::KThread* current_thread{system.Kernel().CurrentScheduler()->GetCurrentThread()};
Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
Dynarmic::HaltReason hr{};
// Notify the debugger and go to sleep if a step was performed
@@ -107,6 +107,7 @@ void ARM_Interface::Run() {
}
// Otherwise, run the thread.
system.EnterDynarmicProfile();
if (current_thread->GetStepState() == StepState::StepPending) {
hr = StepJit();
@@ -116,11 +117,19 @@ void ARM_Interface::Run() {
} else {
hr = RunJit();
}
system.ExitDynarmicProfile();
// Notify the debugger and go to sleep if a breakpoint was hit.
if (Has(hr, breakpoint)) {
RewindBreakpointInstruction();
system.GetDebugger().NotifyThreadStopped(current_thread);
current_thread->RequestSuspend(Kernel::SuspendType::Debug);
current_thread->RequestSuspend(SuspendType::Debug);
break;
}
if (Has(hr, watchpoint)) {
RewindBreakpointInstruction();
system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
current_thread->RequestSuspend(SuspendType::Debug);
break;
}
@@ -134,4 +143,36 @@ void ARM_Interface::Run() {
}
}
void ARM_Interface::LoadWatchpointArray(const WatchpointArray& wp) {
watchpoints = &wp;
}
const Kernel::DebugWatchpoint* ARM_Interface::MatchingWatchpoint(
VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const {
if (!watchpoints) {
return nullptr;
}
const VAddr start_address{addr};
const VAddr end_address{addr + size};
for (size_t i = 0; i < Core::Hardware::NUM_WATCHPOINTS; i++) {
const auto& watch{(*watchpoints)[i]};
if (end_address <= watch.start_address) {
continue;
}
if (start_address >= watch.end_address) {
continue;
}
if ((access_type & watch.type) == Kernel::DebugWatchpointType::None) {
continue;
}
return &watch;
}
return nullptr;
}
} // namespace Core

View File

@@ -5,6 +5,7 @@
#pragma once
#include <array>
#include <span>
#include <vector>
#include <dynarmic/interface/halt_reason.h>
@@ -19,13 +20,16 @@ struct PageTable;
namespace Kernel {
enum class VMAPermission : u8;
}
enum class DebugWatchpointType : u8;
struct DebugWatchpoint;
} // namespace Kernel
namespace Core {
class System;
class CPUInterruptHandler;
using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
/// Generic ARMv8 CPU interface
class ARM_Interface {
@@ -170,6 +174,7 @@ public:
virtual void SaveContext(ThreadContext64& ctx) = 0;
virtual void LoadContext(const ThreadContext32& ctx) = 0;
virtual void LoadContext(const ThreadContext64& ctx) = 0;
void LoadWatchpointArray(const WatchpointArray& wp);
/// Clears the exclusive monitor's state.
virtual void ClearExclusiveState() = 0;
@@ -198,18 +203,24 @@ public:
static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5;
protected:
/// System context that this ARM interface is running under.
System& system;
CPUInterrupts& interrupt_handlers;
const WatchpointArray* watchpoints;
bool uses_wall_clock;
static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
const Kernel::DebugWatchpoint* MatchingWatchpoint(
VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const;
virtual Dynarmic::HaltReason RunJit() = 0;
virtual Dynarmic::HaltReason StepJit() = 0;
virtual u32 GetSvcNumber() const = 0;
virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
virtual void RewindBreakpointInstruction() = 0;
};
} // namespace Core

View File

@@ -29,45 +29,62 @@ using namespace Common::Literals;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
: parent{parent_}, memory(parent.system.Memory()) {}
: parent{parent_},
memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
u8 MemoryRead8(u32 vaddr) override {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return memory.Read8(vaddr);
}
u16 MemoryRead16(u32 vaddr) override {
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return memory.Read16(vaddr);
}
u32 MemoryRead32(u32 vaddr) override {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return memory.Read32(vaddr);
}
u64 MemoryRead64(u32 vaddr) override {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return memory.Read64(vaddr);
}
void MemoryWrite8(u32 vaddr, u8 value) override {
memory.Write8(vaddr, value);
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
memory.Write8(vaddr, value);
}
}
void MemoryWrite16(u32 vaddr, u16 value) override {
memory.Write16(vaddr, value);
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
memory.Write16(vaddr, value);
}
}
void MemoryWrite32(u32 vaddr, u32 value) override {
memory.Write32(vaddr, value);
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
memory.Write32(vaddr, value);
}
}
void MemoryWrite64(u32 vaddr, u64 value) override {
memory.Write64(vaddr, value);
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
memory.Write64(vaddr, value);
}
}
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override {
return memory.WriteExclusive8(vaddr, value, expected);
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
memory.WriteExclusive8(vaddr, value, expected);
}
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override {
return memory.WriteExclusive16(vaddr, value, expected);
return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
memory.WriteExclusive16(vaddr, value, expected);
}
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override {
return memory.WriteExclusive32(vaddr, value, expected);
return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
memory.WriteExclusive32(vaddr, value, expected);
}
bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override {
return memory.WriteExclusive64(vaddr, value, expected);
return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
memory.WriteExclusive64(vaddr, value, expected);
}
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
@@ -77,8 +94,8 @@ public:
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
if (parent.system.DebuggerEnabled()) {
parent.jit.load()->Regs()[15] = pc;
if (debugger_enabled) {
parent.SaveContext(parent.breakpoint_context);
parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
return;
}
@@ -117,9 +134,26 @@ public:
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
if (!debugger_enabled) {
return true;
}
const auto match{parent.MatchingWatchpoint(addr, size, type)};
if (match) {
parent.SaveContext(parent.breakpoint_context);
parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
parent.halted_watchpoint = match;
return false;
}
return true;
}
ARM_Dynarmic_32& parent;
Core::Memory::Memory& memory;
std::size_t num_interpreted_instructions{};
bool debugger_enabled{};
static constexpr u64 minimum_run_cycles = 1000U;
};
@@ -154,6 +188,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
config.code_cache_size = 512_MiB;
config.far_code_offset = 400_MiB;
// Allow memory fault handling to work
if (system.DebuggerEnabled()) {
config.check_halt_on_memory_access = true;
}
// null_jit
if (!page_table) {
// Don't waste too much memory on null_jit
@@ -248,6 +287,14 @@ u32 ARM_Dynarmic_32::GetSvcNumber() const {
return svc_swi;
}
const Kernel::DebugWatchpoint* ARM_Dynarmic_32::HaltedWatchpoint() const {
return halted_watchpoint;
}
void ARM_Dynarmic_32::RewindBreakpointInstruction() {
LoadContext(breakpoint_context);
}
ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
std::size_t core_index_)

View File

@@ -72,6 +72,8 @@ protected:
Dynarmic::HaltReason RunJit() override;
Dynarmic::HaltReason StepJit() override;
u32 GetSvcNumber() const override;
const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
void RewindBreakpointInstruction() override;
private:
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
@@ -98,6 +100,10 @@ private:
// SVC callback
u32 svc_swi{};
// Watchpoint info
const Kernel::DebugWatchpoint* halted_watchpoint;
ThreadContext32 breakpoint_context;
};
} // namespace Core

View File

@@ -29,55 +29,76 @@ using namespace Common::Literals;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
: parent{parent_}, memory(parent.system.Memory()) {}
: parent{parent_},
memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
u8 MemoryRead8(u64 vaddr) override {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return memory.Read8(vaddr);
}
u16 MemoryRead16(u64 vaddr) override {
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return memory.Read16(vaddr);
}
u32 MemoryRead32(u64 vaddr) override {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return memory.Read32(vaddr);
}
u64 MemoryRead64(u64 vaddr) override {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return memory.Read64(vaddr);
}
Vector MemoryRead128(u64 vaddr) override {
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
}
void MemoryWrite8(u64 vaddr, u8 value) override {
memory.Write8(vaddr, value);
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
memory.Write8(vaddr, value);
}
}
void MemoryWrite16(u64 vaddr, u16 value) override {
memory.Write16(vaddr, value);
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
memory.Write16(vaddr, value);
}
}
void MemoryWrite32(u64 vaddr, u32 value) override {
memory.Write32(vaddr, value);
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
memory.Write32(vaddr, value);
}
}
void MemoryWrite64(u64 vaddr, u64 value) override {
memory.Write64(vaddr, value);
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
memory.Write64(vaddr, value);
}
}
void MemoryWrite128(u64 vaddr, Vector value) override {
memory.Write64(vaddr, value[0]);
memory.Write64(vaddr + 8, value[1]);
if (CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write)) {
memory.Write64(vaddr, value[0]);
memory.Write64(vaddr + 8, value[1]);
}
}
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override {
return memory.WriteExclusive8(vaddr, value, expected);
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
memory.WriteExclusive8(vaddr, value, expected);
}
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override {
return memory.WriteExclusive16(vaddr, value, expected);
return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
memory.WriteExclusive16(vaddr, value, expected);
}
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override {
return memory.WriteExclusive32(vaddr, value, expected);
return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
memory.WriteExclusive32(vaddr, value, expected);
}
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override {
return memory.WriteExclusive64(vaddr, value, expected);
return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
memory.WriteExclusive64(vaddr, value, expected);
}
bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override {
return memory.WriteExclusive128(vaddr, value, expected);
return CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write) &&
memory.WriteExclusive128(vaddr, value, expected);
}
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
@@ -118,8 +139,8 @@ public:
case Dynarmic::A64::Exception::Yield:
return;
default:
if (parent.system.DebuggerEnabled()) {
parent.jit.load()->SetPC(pc);
if (debugger_enabled) {
parent.SaveContext(parent.breakpoint_context);
parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
return;
}
@@ -160,10 +181,27 @@ public:
return parent.system.CoreTiming().GetClockTicks();
}
bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
if (!debugger_enabled) {
return true;
}
const auto match{parent.MatchingWatchpoint(addr, size, type)};
if (match) {
parent.SaveContext(parent.breakpoint_context);
parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
parent.halted_watchpoint = match;
return false;
}
return true;
}
ARM_Dynarmic_64& parent;
Core::Memory::Memory& memory;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
bool debugger_enabled{};
static constexpr u64 minimum_run_cycles = 1000U;
};
@@ -214,6 +252,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
config.code_cache_size = 512_MiB;
config.far_code_offset = 400_MiB;
// Allow memory fault handling to work
if (system.DebuggerEnabled()) {
config.check_halt_on_memory_access = true;
}
// null_jit
if (!page_table) {
// Don't waste too much memory on null_jit
@@ -308,6 +351,14 @@ u32 ARM_Dynarmic_64::GetSvcNumber() const {
return svc_swi;
}
const Kernel::DebugWatchpoint* ARM_Dynarmic_64::HaltedWatchpoint() const {
return halted_watchpoint;
}
void ARM_Dynarmic_64::RewindBreakpointInstruction() {
LoadContext(breakpoint_context);
}
ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
std::size_t core_index_)

View File

@@ -66,6 +66,8 @@ protected:
Dynarmic::HaltReason RunJit() override;
Dynarmic::HaltReason StepJit() override;
u32 GetSvcNumber() const override;
const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
void RewindBreakpointInstruction() override;
private:
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
@@ -91,6 +93,10 @@ private:
// SVC callback
u32 svc_swi{};
// Breakpoint info
const Kernel::DebugWatchpoint* halted_watchpoint;
ThreadContext64 breakpoint_context;
};
} // namespace Core

View File

@@ -138,7 +138,6 @@ struct System::Impl {
kernel.Suspend(false);
core_timing.SyncPause(false);
cpu_manager.Pause(false);
is_paused = false;
return status;
@@ -150,25 +149,22 @@ struct System::Impl {
core_timing.SyncPause(true);
kernel.Suspend(true);
cpu_manager.Pause(true);
is_paused = true;
return status;
}
std::unique_lock<std::mutex> StallCPU() {
std::unique_lock<std::mutex> StallProcesses() {
std::unique_lock<std::mutex> lk(suspend_guard);
kernel.Suspend(true);
core_timing.SyncPause(true);
cpu_manager.Pause(true);
return lk;
}
void UnstallCPU() {
void UnstallProcesses() {
if (!is_paused) {
core_timing.SyncPause(false);
kernel.Suspend(false);
cpu_manager.Pause(false);
}
}
@@ -334,6 +330,8 @@ struct System::Impl {
gpu_core->NotifyShutdown();
}
kernel.ShutdownCores();
cpu_manager.Shutdown();
debugger.reset();
services.reset();
service_manager.reset();
@@ -493,12 +491,18 @@ void System::Shutdown() {
impl->Shutdown();
}
std::unique_lock<std::mutex> System::StallCPU() {
return impl->StallCPU();
void System::DetachDebugger() {
if (impl->debugger) {
impl->debugger->NotifyShutdown();
}
}
void System::UnstallCPU() {
impl->UnstallCPU();
std::unique_lock<std::mutex> System::StallProcesses() {
return impl->StallProcesses();
}
void System::UnstallProcesses() {
impl->UnstallProcesses();
}
void System::InitializeDebugger() {

View File

@@ -160,8 +160,11 @@ public:
/// Shutdown the emulated system.
void Shutdown();
std::unique_lock<std::mutex> StallCPU();
void UnstallCPU();
/// Forcibly detach the debugger if it is running.
void DetachDebugger();
std::unique_lock<std::mutex> StallProcesses();
void UnstallProcesses();
/**
* Initialize the debugger.

View File

@@ -21,23 +21,24 @@ CpuManager::~CpuManager() = default;
void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
std::size_t core) {
cpu_manager.RunThread(stop_token, core);
cpu_manager.RunThread(core);
}
void CpuManager::Initialize() {
running_mode = true;
if (is_multicore) {
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
}
} else {
core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1);
for (std::size_t core = 0; core < num_cores; core++) {
core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
}
}
void CpuManager::Shutdown() {
running_mode = false;
Pause(false);
for (std::size_t core = 0; core < num_cores; core++) {
if (core_data[core].host_thread.joinable()) {
core_data[core].host_thread.join();
}
}
}
std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
@@ -48,8 +49,8 @@ std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
return IdleThreadFunction;
}
std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
return SuspendThreadFunction;
std::function<void(void*)> CpuManager::GetShutdownThreadStartFunc() {
return ShutdownThreadFunction;
}
void CpuManager::GuestThreadFunction(void* cpu_manager_) {
@@ -79,17 +80,12 @@ void CpuManager::IdleThreadFunction(void* cpu_manager_) {
}
}
void CpuManager::SuspendThreadFunction(void* cpu_manager_) {
CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
if (cpu_manager->is_multicore) {
cpu_manager->MultiCoreRunSuspendThread();
} else {
cpu_manager->SingleCoreRunSuspendThread();
}
void CpuManager::ShutdownThreadFunction(void* cpu_manager) {
static_cast<CpuManager*>(cpu_manager)->ShutdownThread();
}
void* CpuManager::GetStartFuncParamater() {
return static_cast<void*>(this);
void* CpuManager::GetStartFuncParameter() {
return this;
}
///////////////////////////////////////////////////////////////////////////////
@@ -99,7 +95,7 @@ void* CpuManager::GetStartFuncParamater() {
void CpuManager::MultiCoreRunGuestThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
auto& host_context = thread->GetHostContext();
host_context->SetRewindPoint(GuestRewindFunction, this);
MultiCoreRunGuestLoop();
@@ -110,12 +106,10 @@ void CpuManager::MultiCoreRunGuestLoop() {
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
system.EnterDynarmicProfile();
while (!physical_core->IsInterrupted()) {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
system.ExitDynarmicProfile();
{
Kernel::KScopedDisableDispatch dd(kernel);
physical_core->ArmInterface().ClearExclusiveState();
@@ -131,58 +125,6 @@ void CpuManager::MultiCoreRunIdleThread() {
}
}
void CpuManager::MultiCoreRunSuspendThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
while (true) {
auto core = kernel.CurrentPhysicalCoreIndex();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.CurrentPhysicalCoreIndex());
scheduler.RescheduleCurrentCore();
}
}
void CpuManager::MultiCorePause(bool paused) {
if (!paused) {
bool all_not_barrier = false;
while (!all_not_barrier) {
all_not_barrier = true;
for (const auto& data : core_data) {
all_not_barrier &= !data.is_running.load() && data.initialized.load();
}
}
for (auto& data : core_data) {
data.enter_barrier->Set();
}
if (paused_state.load()) {
bool all_barrier = false;
while (!all_barrier) {
all_barrier = true;
for (const auto& data : core_data) {
all_barrier &= data.is_paused.load() && data.initialized.load();
}
}
for (auto& data : core_data) {
data.exit_barrier->Set();
}
}
} else {
/// Wait until all cores are paused.
bool all_barrier = false;
while (!all_barrier) {
all_barrier = true;
for (const auto& data : core_data) {
all_barrier &= data.is_paused.load() && data.initialized.load();
}
}
/// Don't release the barrier
}
paused_state = paused;
}
///////////////////////////////////////////////////////////////////////////////
/// SingleCore ///
///////////////////////////////////////////////////////////////////////////////
@@ -190,7 +132,7 @@ void CpuManager::MultiCorePause(bool paused) {
void CpuManager::SingleCoreRunGuestThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
auto& host_context = thread->GetHostContext();
host_context->SetRewindPoint(GuestRewindFunction, this);
SingleCoreRunGuestLoop();
@@ -200,12 +142,10 @@ void CpuManager::SingleCoreRunGuestLoop() {
auto& kernel = system.Kernel();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
system.EnterDynarmicProfile();
if (!physical_core->IsInterrupted()) {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
system.ExitDynarmicProfile();
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
kernel.SetIsPhantomModeForSingleCore(false);
@@ -228,25 +168,11 @@ void CpuManager::SingleCoreRunIdleThread() {
}
}
void CpuManager::SingleCoreRunSuspendThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
while (true) {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
scheduler.RescheduleCurrentCore();
}
}
void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
{
auto& kernel = system.Kernel();
auto& scheduler = kernel.Scheduler(current_core);
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Kernel::KThread* current_thread = scheduler.GetSchedulerCurrentThread();
if (idle_count >= 4 || from_running_enviroment) {
if (!from_running_enviroment) {
system.CoreTiming().Idle();
@@ -258,7 +184,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
}
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
system.CoreTiming().ResetTicks();
scheduler.Unload(scheduler.GetCurrentThread());
scheduler.Unload(scheduler.GetSchedulerCurrentThread());
auto& next_scheduler = kernel.Scheduler(current_core);
Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
@@ -267,47 +193,21 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
// May have changed scheduler
{
auto& scheduler = system.Kernel().Scheduler(current_core);
scheduler.Reload(scheduler.GetCurrentThread());
if (!scheduler.IsIdle()) {
idle_count = 0;
}
scheduler.Reload(scheduler.GetSchedulerCurrentThread());
idle_count = 0;
}
}
void CpuManager::SingleCorePause(bool paused) {
if (!paused) {
bool all_not_barrier = false;
while (!all_not_barrier) {
all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load();
}
core_data[0].enter_barrier->Set();
if (paused_state.load()) {
bool all_barrier = false;
while (!all_barrier) {
all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
}
core_data[0].exit_barrier->Set();
}
} else {
/// Wait until all cores are paused.
bool all_barrier = false;
while (!all_barrier) {
all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
}
/// Don't release the barrier
}
paused_state = paused;
void CpuManager::ShutdownThread() {
auto& kernel = system.Kernel();
auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0;
auto* current_thread = kernel.GetCurrentEmuThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
UNREACHABLE();
}
void CpuManager::Pause(bool paused) {
if (is_multicore) {
MultiCorePause(paused);
} else {
SingleCorePause(paused);
}
}
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
void CpuManager::RunThread(std::size_t core) {
/// Initialization
system.RegisterCoreThread(core);
std::string name;
@@ -320,45 +220,24 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
auto& data = core_data[core];
data.enter_barrier = std::make_unique<Common::Event>();
data.exit_barrier = std::make_unique<Common::Event>();
data.host_context = Common::Fiber::ThreadToFiber();
data.is_running = false;
data.initialized = true;
const bool sc_sync = !is_async_gpu && !is_multicore;
bool sc_sync_first_use = sc_sync;
// Cleanup
SCOPE_EXIT({
data.host_context->Exit();
data.enter_barrier.reset();
data.exit_barrier.reset();
data.initialized = false;
MicroProfileOnThreadExit();
});
/// Running
while (running_mode) {
data.is_running = false;
data.enter_barrier->Wait();
if (sc_sync_first_use) {
system.GPU().ObtainContext();
sc_sync_first_use = false;
}
// Running
gpu_barrier->Sync();
// Emulation was stopped
if (stop_token.stop_requested()) {
return;
}
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
data.is_running = true;
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
data.is_running = false;
data.is_paused = true;
data.exit_barrier->Wait();
data.is_paused = false;
if (!is_async_gpu && !is_multicore) {
system.GPU().ObtainContext();
}
auto* current_thread = system.Kernel().CurrentScheduler()->GetIdleThread();
Kernel::SetCurrentThread(system.Kernel(), current_thread);
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
}
} // namespace Core

View File

@@ -43,15 +43,17 @@ public:
is_async_gpu = is_async;
}
void OnGpuReady() {
gpu_barrier->Sync();
}
void Initialize();
void Shutdown();
void Pause(bool paused);
static std::function<void(void*)> GetGuestThreadStartFunc();
static std::function<void(void*)> GetIdleThreadStartFunc();
static std::function<void(void*)> GetSuspendThreadStartFunc();
void* GetStartFuncParamater();
static std::function<void(void*)> GetShutdownThreadStartFunc();
void* GetStartFuncParameter();
void PreemptSingleCore(bool from_running_enviroment = true);
@@ -63,43 +65,34 @@ private:
static void GuestThreadFunction(void* cpu_manager);
static void GuestRewindFunction(void* cpu_manager);
static void IdleThreadFunction(void* cpu_manager);
static void SuspendThreadFunction(void* cpu_manager);
static void ShutdownThreadFunction(void* cpu_manager);
void MultiCoreRunGuestThread();
void MultiCoreRunGuestLoop();
void MultiCoreRunIdleThread();
void MultiCoreRunSuspendThread();
void MultiCorePause(bool paused);
void SingleCoreRunGuestThread();
void SingleCoreRunGuestLoop();
void SingleCoreRunIdleThread();
void SingleCoreRunSuspendThread();
void SingleCorePause(bool paused);
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
void RunThread(std::stop_token stop_token, std::size_t core);
void ShutdownThread();
void RunThread(std::size_t core);
struct CoreData {
std::shared_ptr<Common::Fiber> host_context;
std::unique_ptr<Common::Event> enter_barrier;
std::unique_ptr<Common::Event> exit_barrier;
std::atomic<bool> is_running;
std::atomic<bool> is_paused;
std::atomic<bool> initialized;
std::jthread host_thread;
};
std::atomic<bool> running_mode{};
std::atomic<bool> paused_state{};
std::unique_ptr<Common::Barrier> gpu_barrier{};
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
bool is_async_gpu{};
bool is_multicore{};
std::atomic<std::size_t> current_core{};
std::size_t idle_count{};
std::size_t num_cores{};
static constexpr std::size_t max_cycle_runs = 5;
System& system;

View File

@@ -140,7 +140,6 @@ u64 GetSignatureTypeDataSize(SignatureType type) {
return 0x3C;
}
UNREACHABLE();
return 0;
}
u64 GetSignatureTypePaddingSize(SignatureType type) {
@@ -155,7 +154,6 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
return 0x40;
}
UNREACHABLE();
return 0;
}
SignatureType Ticket::GetSignatureType() const {

View File

@@ -42,6 +42,18 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
return received_data;
}
enum class SignalType {
Stopped,
Watchpoint,
ShuttingDown,
};
struct SignalInfo {
SignalType type;
Kernel::KThread* thread;
const Kernel::DebugWatchpoint* watchpoint;
};
namespace Core {
class DebuggerImpl : public DebuggerBackend {
@@ -56,17 +68,23 @@ public:
ShutdownServer();
}
bool NotifyThreadStopped(Kernel::KThread* thread) {
std::scoped_lock lk{connection_lock};
bool SignalDebugger(SignalInfo signal_info) {
{
std::scoped_lock lk{connection_lock};
if (stopped) {
// Do not notify the debugger about another event.
// It should be ignored.
return false;
if (stopped) {
// Do not notify the debugger about another event.
// It should be ignored.
return false;
}
// Set up the state.
stopped = true;
info = signal_info;
}
stopped = true;
boost::asio::write(signal_pipe, boost::asio::buffer(&thread, sizeof(thread)));
// Write a single byte into the pipe to wake up the debug interface.
boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
return true;
}
@@ -96,7 +114,7 @@ private:
connection_thread = std::jthread([&, port](std::stop_token stop_token) {
try {
// Initialize the listening socket and accept a new client.
tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port};
tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
tcp::acceptor acceptor{io_context, endpoint};
acceptor.async_accept(client_socket, [](const auto&) {});
@@ -124,12 +142,9 @@ private:
Common::SetCurrentThreadName("yuzu:Debugger");
// Set up the client signals for new data.
AsyncReceiveInto(signal_pipe, active_thread, [&](auto d) { PipeData(d); });
AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
// Stop the emulated CPU.
AllCoreStop();
// Set the active thread.
UpdateActiveThread();
@@ -142,9 +157,33 @@ private:
}
void PipeData(std::span<const u8> data) {
AllCoreStop();
UpdateActiveThread();
frontend->Stopped(active_thread);
switch (info.type) {
case SignalType::Stopped:
case SignalType::Watchpoint:
// Stop emulation.
PauseEmulation();
// Notify the client.
active_thread = info.thread;
UpdateActiveThread();
if (info.type == SignalType::Watchpoint) {
frontend->Watchpoint(active_thread, *info.watchpoint);
} else {
frontend->Stopped(active_thread);
}
break;
case SignalType::ShuttingDown:
frontend->ShuttingDown();
// Wait for emulation to shut down gracefully now.
signal_pipe.close();
client_socket.shutdown(boost::asio::socket_base::shutdown_both);
LOG_INFO(Debug_GDBStub, "Shut down server");
break;
}
}
void ClientData(std::span<const u8> data) {
@@ -156,32 +195,29 @@ private:
std::scoped_lock lk{connection_lock};
stopped = true;
}
AllCoreStop();
PauseEmulation();
UpdateActiveThread();
frontend->Stopped(active_thread);
break;
}
case DebuggerAction::Continue:
active_thread->SetStepState(Kernel::StepState::NotStepping);
ResumeInactiveThreads();
AllCoreResume();
MarkResumed([&] { ResumeEmulation(); });
break;
case DebuggerAction::StepThreadUnlocked:
active_thread->SetStepState(Kernel::StepState::StepPending);
ResumeInactiveThreads();
AllCoreResume();
MarkResumed([&] {
active_thread->SetStepState(Kernel::StepState::StepPending);
active_thread->Resume(Kernel::SuspendType::Debug);
ResumeEmulation(active_thread);
});
break;
case DebuggerAction::StepThreadLocked:
active_thread->SetStepState(Kernel::StepState::StepPending);
SuspendInactiveThreads();
AllCoreResume();
case DebuggerAction::StepThreadLocked: {
MarkResumed([&] {
active_thread->SetStepState(Kernel::StepState::StepPending);
active_thread->Resume(Kernel::SuspendType::Debug);
});
break;
}
case DebuggerAction::ShutdownEmulation: {
// Suspend all threads and release any locks held
active_thread->RequestSuspend(Kernel::SuspendType::Debug);
SuspendInactiveThreads();
AllCoreResume();
// Spawn another thread that will exit after shutdown,
// to avoid a deadlock
Core::System* system_ref{&system};
@@ -193,33 +229,33 @@ private:
}
}
void AllCoreStop() {
if (!suspend) {
suspend = system.StallCPU();
void PauseEmulation() {
// Put all threads to sleep on next scheduler round.
for (auto* thread : ThreadList()) {
thread->RequestSuspend(Kernel::SuspendType::Debug);
}
// Signal an interrupt so that scheduler will fire.
system.Kernel().InterruptAllPhysicalCores();
}
void ResumeEmulation(Kernel::KThread* except = nullptr) {
// Wake up all threads.
for (auto* thread : ThreadList()) {
if (thread == except) {
continue;
}
thread->SetStepState(Kernel::StepState::NotStepping);
thread->Resume(Kernel::SuspendType::Debug);
}
}
void AllCoreResume() {
template <typename Callback>
void MarkResumed(Callback&& cb) {
std::scoped_lock lk{connection_lock};
stopped = false;
system.UnstallCPU();
suspend.reset();
}
void SuspendInactiveThreads() {
for (auto* thread : ThreadList()) {
if (thread != active_thread) {
thread->RequestSuspend(Kernel::SuspendType::Debug);
}
}
}
void ResumeInactiveThreads() {
for (auto* thread : ThreadList()) {
if (thread != active_thread) {
thread->Resume(Kernel::SuspendType::Debug);
thread->SetStepState(Kernel::StepState::NotStepping);
}
}
cb();
}
void UpdateActiveThread() {
@@ -227,8 +263,6 @@ private:
if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
active_thread = threads[0];
}
active_thread->Resume(Kernel::SuspendType::Debug);
active_thread->SetStepState(Kernel::StepState::NotStepping);
}
const std::vector<Kernel::KThread*>& ThreadList() {
@@ -244,9 +278,10 @@ private:
boost::asio::io_context io_context;
boost::process::async_pipe signal_pipe;
boost::asio::ip::tcp::socket client_socket;
std::optional<std::unique_lock<std::mutex>> suspend;
SignalInfo info;
Kernel::KThread* active_thread;
bool pipe_data;
bool stopped;
std::array<u8, 4096> client_data;
@@ -263,7 +298,18 @@ Debugger::Debugger(Core::System& system, u16 port) {
Debugger::~Debugger() = default;
bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
return impl && impl->NotifyThreadStopped(thread);
return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread, nullptr});
}
bool Debugger::NotifyThreadWatchpoint(Kernel::KThread* thread,
const Kernel::DebugWatchpoint& watch) {
return impl && impl->SignalDebugger(SignalInfo{SignalType::Watchpoint, thread, &watch});
}
void Debugger::NotifyShutdown() {
if (impl) {
impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr, nullptr});
}
}
} // namespace Core

View File

@@ -9,7 +9,8 @@
namespace Kernel {
class KThread;
}
struct DebugWatchpoint;
} // namespace Kernel
namespace Core {
class System;
@@ -35,6 +36,16 @@ public:
*/
bool NotifyThreadStopped(Kernel::KThread* thread);
/**
* Notify the debugger that a shutdown is being performed now and disconnect.
*/
void NotifyShutdown();
/*
* Notify the debugger that the given thread has stopped due to hitting a watchpoint.
*/
bool NotifyThreadWatchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch);
private:
std::unique_ptr<DebuggerImpl> impl;
};

View File

@@ -11,7 +11,8 @@
namespace Kernel {
class KThread;
}
struct DebugWatchpoint;
} // namespace Kernel
namespace Core {
@@ -66,6 +67,16 @@ public:
*/
virtual void Stopped(Kernel::KThread* thread) = 0;
/**
* Called when emulation is shutting down.
*/
virtual void ShuttingDown() = 0;
/*
* Called when emulation has stopped on a watchpoint.
*/
virtual void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) = 0;
/**
* Called when new data is asynchronously received on the client socket.
* A list of actions to perform is returned.

View File

@@ -106,10 +106,29 @@ GDBStub::~GDBStub() = default;
void GDBStub::Connected() {}
void GDBStub::ShuttingDown() {}
void GDBStub::Stopped(Kernel::KThread* thread) {
SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
}
void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) {
const auto status{arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)};
switch (watch.type) {
case Kernel::DebugWatchpointType::Read:
SendReply(fmt::format("{}rwatch:{:x};", status, watch.start_address));
break;
case Kernel::DebugWatchpointType::Write:
SendReply(fmt::format("{}watch:{:x};", status, watch.start_address));
break;
case Kernel::DebugWatchpointType::ReadOrWrite:
default:
SendReply(fmt::format("{}awatch:{:x};", status, watch.start_address));
break;
}
}
std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
std::vector<DebuggerAction> actions;
current_command.insert(current_command.end(), data.begin(), data.end());
@@ -276,44 +295,124 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
case 'c':
actions.push_back(DebuggerAction::Continue);
break;
case 'Z': {
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
if (system.Memory().IsValidVirtualAddress(addr)) {
replaced_instructions[addr] = system.Memory().Read32(addr);
system.Memory().Write32(addr, arch->BreakpointInstruction());
system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_ERR);
}
case 'Z':
HandleBreakpointInsert(command);
break;
}
case 'z': {
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
const auto orig_insn{replaced_instructions.find(addr)};
if (system.Memory().IsValidVirtualAddress(addr) &&
orig_insn != replaced_instructions.end()) {
system.Memory().Write32(addr, orig_insn->second);
system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
replaced_instructions.erase(addr);
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_ERR);
}
case 'z':
HandleBreakpointRemove(command);
break;
}
default:
SendReply(GDB_STUB_REPLY_EMPTY);
break;
}
}
enum class BreakpointType {
Software = 0,
Hardware = 1,
WriteWatch = 2,
ReadWatch = 3,
AccessWatch = 4,
};
void GDBStub::HandleBreakpointInsert(std::string_view command) {
const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
bool success{};
switch (type) {
case BreakpointType::Software:
replaced_instructions[addr] = system.Memory().Read32(addr);
system.Memory().Write32(addr, arch->BreakpointInstruction());
system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
success = true;
break;
case BreakpointType::WriteWatch:
success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
Kernel::DebugWatchpointType::Write);
break;
case BreakpointType::ReadWatch:
success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
Kernel::DebugWatchpointType::Read);
break;
case BreakpointType::AccessWatch:
success = system.CurrentProcess()->InsertWatchpoint(
system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break;
case BreakpointType::Hardware:
default:
SendReply(GDB_STUB_REPLY_EMPTY);
return;
}
if (success) {
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_ERR);
}
}
void GDBStub::HandleBreakpointRemove(std::string_view command) {
const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
bool success{};
switch (type) {
case BreakpointType::Software: {
const auto orig_insn{replaced_instructions.find(addr)};
if (orig_insn != replaced_instructions.end()) {
system.Memory().Write32(addr, orig_insn->second);
system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
replaced_instructions.erase(addr);
success = true;
}
break;
}
case BreakpointType::WriteWatch:
success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
Kernel::DebugWatchpointType::Write);
break;
case BreakpointType::ReadWatch:
success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
Kernel::DebugWatchpointType::Read);
break;
case BreakpointType::AccessWatch:
success = system.CurrentProcess()->RemoveWatchpoint(
system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break;
case BreakpointType::Hardware:
default:
SendReply(GDB_STUB_REPLY_EMPTY);
return;
}
if (success) {
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_ERR);
}
}
// Structure offsets are from Atmosphere
// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp

View File

@@ -23,6 +23,8 @@ public:
void Connected() override;
void Stopped(Kernel::KThread* thread) override;
void ShuttingDown() override;
void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) override;
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
private:
@@ -30,6 +32,8 @@ private:
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
void HandleQuery(std::string_view command);
void HandleBreakpointInsert(std::string_view command);
void HandleBreakpointRemove(std::string_view command);
std::vector<char>::const_iterator CommandEnd() const;
std::optional<std::string> DetachCommand();
Kernel::KThread* GetThreadByID(u64 thread_id);

View File

@@ -15,6 +15,7 @@ namespace Core {
class GDBStubArch {
public:
virtual ~GDBStubArch() = default;
virtual std::string GetTargetXML() const = 0;
virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0;
virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0;

View File

@@ -419,7 +419,7 @@ std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type
Core::Crypto::Mode::ECB);
cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
Core::Crypto::Key128 out;
Core::Crypto::Key128 out{};
if (type == NCASectionCryptoType::XTS) {
std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
} else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR) {

View File

@@ -50,7 +50,7 @@ std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockTyp
low = mid + 1;
}
}
UNREACHABLE_MSG("Offset could not be found in BKTR block.");
ASSERT_MSG(false, "Offset could not be found in BKTR block.");
return {0, 0};
}
} // Anonymous namespace

View File

@@ -108,7 +108,7 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
return ContentRecordType::HtmlDocument;
default:
UNREACHABLE_MSG("Invalid NCAContentType={:02X}", type);
ASSERT_MSG(false, "Invalid NCAContentType={:02X}", type);
return ContentRecordType{};
}
}

View File

@@ -144,7 +144,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
}
} else {
UNREACHABLE();
ASSERT(false);
return nullptr;
}

View File

@@ -65,7 +65,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
controller->Connect(true);
} else {
UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
ASSERT_MSG(false, "Unable to add a new controller based on the given parameters!");
}
}

View File

@@ -25,6 +25,9 @@ constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
};
// Cortex-A57 supports 4 memory watchpoints
constexpr u64 NUM_WATCHPOINTS = 4;
} // namespace Hardware
} // namespace Core

View File

@@ -48,7 +48,7 @@ EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
return handheld.get();
case NpadIdType::Invalid:
default:
UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
@@ -77,7 +77,7 @@ const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type
return handheld.get();
case NpadIdType::Invalid:
default:
UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}

View File

@@ -141,7 +141,7 @@ public:
if (index < DomainHandlerCount()) {
domain_handlers[index] = nullptr;
} else {
UNREACHABLE_MSG("Unexpected handler index {}", index);
ASSERT_MSG(false, "Unexpected handler index {}", index);
}
}

View File

@@ -244,7 +244,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
// If we somehow get an invalid type, abort.
default:
UNREACHABLE_MSG("Unknown slab type: {}", slab_types[i]);
ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
}
// If we've hit the end of a gap, free it.

View File

@@ -234,7 +234,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
@@ -287,7 +287,7 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{

View File

@@ -35,7 +35,7 @@ public:
case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
}
UNREACHABLE();
ASSERT(false);
return ResultUnknown;
}
@@ -49,7 +49,7 @@ public:
case Svc::ArbitrationType::WaitIfEqual:
return WaitIfEqual(addr, value, timeout);
}
UNREACHABLE();
ASSERT(false);
return ResultUnknown;
}

View File

@@ -84,7 +84,7 @@ u64 KAddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
}
UNREACHABLE();
ASSERT(false);
return 0;
}
@@ -101,7 +101,7 @@ std::size_t KAddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type)
ASSERT(IsAllowed39BitType(type));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
}
UNREACHABLE();
ASSERT(false);
return 0;
}

View File

@@ -18,7 +18,7 @@ namespace Kernel {
class KernelCore;
class KProcess;
#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
#define KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, ATTRIBUTE) \
\
private: \
friend class ::Kernel::KClassTokenGenerator; \
@@ -40,16 +40,19 @@ public:
static constexpr const char* GetStaticTypeName() { \
return TypeName; \
} \
virtual TypeObj GetTypeObj() const { \
virtual TypeObj GetTypeObj() ATTRIBUTE { \
return GetStaticTypeObj(); \
} \
virtual const char* GetTypeName() const { \
virtual const char* GetTypeName() ATTRIBUTE { \
return GetStaticTypeName(); \
} \
\
private: \
constexpr bool operator!=(const TypeObj& rhs)
#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, const override)
class KAutoObject {
protected:
class TypeObj {
@@ -82,7 +85,7 @@ protected:
};
private:
KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject);
KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const);
public:
explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {

View File

@@ -49,6 +49,7 @@ private:
}
}
}
UNREACHABLE();
}();
template <typename T>

View File

@@ -27,23 +27,18 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
auto& page_table = m_owner->PageTable();
// Construct the page group.
m_page_group =
KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
m_page_group = {};
// Lock the memory.
R_TRY(page_table.LockForCodeMemory(addr, size))
R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size))
// Clear the memory.
//
// FIXME: this ends up clobbering address ranges outside the scope of the mapping within
// guest memory, and is not specifically required if the guest program is correctly
// written, so disable until this is further investigated.
//
// for (const auto& block : m_page_group.Nodes()) {
// std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
// }
for (const auto& block : m_page_group.Nodes()) {
std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
}
// Set remaining tracking members.
m_owner->Open();
m_address = addr;
m_is_initialized = true;
m_is_owner_mapped = false;
@@ -57,8 +52,14 @@ void KCodeMemory::Finalize() {
// Unlock.
if (!m_is_mapped && !m_is_owner_mapped) {
const size_t size = m_page_group.GetNumPages() * PageSize;
m_owner->PageTable().UnlockForCodeMemory(m_address, size);
m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group);
}
// Close the page group.
m_page_group = {};
// Close our reference to our owner.
m_owner->Close();
}
ResultCode KCodeMemory::Map(VAddr address, size_t size) {
@@ -118,7 +119,8 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis
k_perm = KMemoryPermission::UserReadExecute;
break;
default:
break;
// Already validated by ControlCodeMemory svc
UNREACHABLE();
}
// Map the memory.

View File

@@ -106,7 +106,7 @@ KConditionVariable::KConditionVariable(Core::System& system_)
KConditionVariable::~KConditionVariable() = default;
ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
KThread* owner_thread = GetCurrentThreadPointer(kernel);
// Signal the address.
{
@@ -147,7 +147,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
}
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
// Wait for the address.

View File

@@ -15,8 +15,7 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
return;
}
auto& scheduler = kernel.Scheduler(core_id);
auto& current_thread = *scheduler.GetCurrentThread();
auto& current_thread = GetCurrentThread(kernel);
// If the user disable count is set, we may need to pin the current thread.
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
@@ -26,7 +25,7 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
process->PinCurrentThread(core_id);
// Set the interrupt flag for the thread.
scheduler.GetCurrentThread()->SetInterruptFlag();
GetCurrentThread(kernel).SetInterruptFlag();
}
}

View File

@@ -29,7 +29,7 @@ constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
} else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
return KMemoryManager::Pool::SystemNonSecure;
} else {
UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool");
ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool");
return {};
}
}

View File

@@ -35,7 +35,7 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT
case FileSys::ProgramAddressSpaceType::Is39Bit:
return 39;
default:
UNREACHABLE();
ASSERT(false);
return {};
}
}
@@ -65,7 +65,6 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
std::size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)};
std::size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)};
ASSERT(start <= code_addr);
ASSERT(code_addr < code_addr + code_size);
ASSERT(code_addr + code_size - 1 <= end - 1);
@@ -128,7 +127,7 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
const std::size_t needed_size{
(alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)};
if (alloc_size < needed_size) {
UNREACHABLE();
ASSERT(false);
return ResultOutOfMemory;
}
@@ -542,6 +541,95 @@ ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num
return ResultSuccess;
}
bool KPageTable::IsValidPageGroup(const KPageLinkedList& pg_ll, VAddr addr, size_t num_pages) {
ASSERT(this->IsLockedByCurrentThread());
const size_t size = num_pages * PageSize;
const auto& pg = pg_ll.Nodes();
const auto& memory_layout = system.Kernel().MemoryLayout();
// Empty groups are necessarily invalid.
if (pg.empty()) {
return false;
}
// We're going to validate that the group we'd expect is the group we see.
auto cur_it = pg.begin();
PAddr cur_block_address = cur_it->GetAddress();
size_t cur_block_pages = cur_it->GetNumPages();
auto UpdateCurrentIterator = [&]() {
if (cur_block_pages == 0) {
if ((++cur_it) == pg.end()) {
return false;
}
cur_block_address = cur_it->GetAddress();
cur_block_pages = cur_it->GetNumPages();
}
return true;
};
// Begin traversal.
Common::PageTable::TraversalContext context;
Common::PageTable::TraversalEntry next_entry;
if (!page_table_impl.BeginTraversal(next_entry, context, addr)) {
return false;
}
// Prepare tracking variables.
PAddr cur_addr = next_entry.phys_addr;
size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
size_t tot_size = cur_size;
// Iterate, comparing expected to actual.
while (tot_size < size) {
if (!page_table_impl.ContinueTraversal(next_entry, context)) {
return false;
}
if (next_entry.phys_addr != (cur_addr + cur_size)) {
const size_t cur_pages = cur_size / PageSize;
if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
return false;
}
if (!UpdateCurrentIterator()) {
return false;
}
if (cur_block_address != cur_addr || cur_block_pages < cur_pages) {
return false;
}
cur_block_address += cur_size;
cur_block_pages -= cur_pages;
cur_addr = next_entry.phys_addr;
cur_size = next_entry.block_size;
} else {
cur_size += next_entry.block_size;
}
tot_size += next_entry.block_size;
}
// Ensure we compare the right amount for the last block.
if (tot_size > size) {
cur_size -= (tot_size - size);
}
if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
return false;
}
if (!UpdateCurrentIterator()) {
return false;
}
return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
}
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
KPageTable& src_page_table, VAddr src_addr) {
KScopedLightLock lk(general_lock);
@@ -1341,7 +1429,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
new_state = KMemoryState::AliasCodeData;
break;
default:
UNREACHABLE();
ASSERT(false);
}
}
@@ -1687,22 +1775,22 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
return ResultSuccess;
}
ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
ResultCode KPageTable::LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size) {
return this->LockMemoryAndOpen(
nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
KMemoryAttribute::All, KMemoryAttribute::None,
out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
KMemoryAttribute::None,
static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
KMemoryPermission::KernelReadWrite),
KMemoryAttribute::Locked);
}
ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::All,
KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
KMemoryAttribute::Locked, nullptr);
ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size,
const KPageLinkedList& pg) {
return this->UnlockMemory(
addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg);
}
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1734,9 +1822,7 @@ void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages,
VAddr addr{start};
while (addr < start + (num_pages * PageSize)) {
const PAddr paddr{GetPhysicalAddr(addr)};
if (!paddr) {
UNREACHABLE();
}
ASSERT(paddr != 0);
page_linked_list.AddBlock(paddr, 1);
addr += PageSize;
}
@@ -1767,7 +1853,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin
system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress());
break;
default:
UNREACHABLE();
ASSERT(false);
}
addr += size;
@@ -1798,7 +1884,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss
case OperationType::ChangePermissionsAndRefresh:
break;
default:
UNREACHABLE();
ASSERT(false);
}
return ResultSuccess;
}
@@ -1835,7 +1921,6 @@ VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
return code_region_start;
default:
UNREACHABLE();
return {};
}
}
@@ -1871,7 +1956,6 @@ std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
return code_region_end - code_region_start;
default:
UNREACHABLE();
return {};
}
}
@@ -2125,7 +2209,7 @@ ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_
// Check the page group.
if (pg != nullptr) {
UNIMPLEMENTED_MSG("PageGroup support is unimplemented!");
R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion);
}
// Decide on new perm and attr.

View File

@@ -72,8 +72,8 @@ public:
KMemoryPermission perm, PAddr map_addr = 0);
ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
ResultCode LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size);
ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageLinkedList& pg);
ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
@@ -178,6 +178,7 @@ private:
const KPageLinkedList* pg);
ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
bool IsValidPageGroup(const KPageLinkedList& pg, VAddr addr, size_t num_pages);
bool IsLockedByCurrentThread() const {
return general_lock.IsLockedByCurrentThread();

View File

@@ -60,7 +60,7 @@ ResultCode KPort::EnqueueSession(KServerSession* session) {
if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
session_ptr->ClientConnected(server.AcceptSession());
} else {
UNREACHABLE();
ASSERT(false);
}
return ResultSuccess;

View File

@@ -57,18 +57,13 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
thread->GetContext64().cpu_registers[0] = 0;
thread->GetContext32().cpu_registers[1] = thread_handle;
thread->GetContext64().cpu_registers[1] = thread_handle;
thread->DisableDispatch();
auto& kernel = system.Kernel();
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
{
KScopedSchedulerLock lock{kernel};
thread->SetState(ThreadState::Runnable);
if (system.DebuggerEnabled()) {
thread->RequestSuspend(SuspendType::Debug);
}
if (system.DebuggerEnabled()) {
thread->RequestSuspend(SuspendType::Debug);
}
// Run our thread.
void(thread->Run());
}
} // Anonymous namespace
@@ -181,7 +176,8 @@ void KProcess::PinCurrentThread(s32 core_id) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
KThread* cur_thread =
kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread();
// If the thread isn't terminated, pin it.
if (!cur_thread->IsTerminationRequested()) {
@@ -198,7 +194,8 @@ void KProcess::UnpinCurrentThread(s32 core_id) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
KThread* cur_thread =
kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread();
// Unpin it.
cur_thread->Unpin();
@@ -275,11 +272,15 @@ void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr a
shmem->Close();
}
void KProcess::RegisterThread(const KThread* thread) {
void KProcess::RegisterThread(KThread* thread) {
KScopedLightLock lk{list_lock};
thread_list.push_back(thread);
}
void KProcess::UnregisterThread(const KThread* thread) {
void KProcess::UnregisterThread(KThread* thread) {
KScopedLightLock lk{list_lock};
thread_list.remove(thread);
}
@@ -297,6 +298,50 @@ ResultCode KProcess::Reset() {
return ResultSuccess;
}
ResultCode KProcess::SetActivity(ProcessActivity activity) {
// Lock ourselves and the scheduler.
KScopedLightLock lk{state_lock};
KScopedLightLock list_lk{list_lock};
KScopedSchedulerLock sl{kernel};
// Validate our state.
R_UNLESS(status != ProcessStatus::Exiting, ResultInvalidState);
R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState);
// Either pause or resume.
if (activity == ProcessActivity::Paused) {
// Verify that we're not suspended.
if (is_suspended) {
return ResultInvalidState;
}
// Suspend all threads.
for (auto* thread : GetThreadList()) {
thread->RequestSuspend(SuspendType::Process);
}
// Set ourselves as suspended.
SetSuspended(true);
} else {
ASSERT(activity == ProcessActivity::Runnable);
// Verify that we're suspended.
if (!is_suspended) {
return ResultInvalidState;
}
// Resume all threads.
for (auto* thread : GetThreadList()) {
thread->Resume(SuspendType::Process);
}
// Set ourselves as resumed.
SetSuspended(false);
}
return ResultSuccess;
}
ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
std::size_t code_size) {
program_id = metadata.GetTitleID();
@@ -350,7 +395,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
break;
default:
UNREACHABLE();
ASSERT(false);
}
// Create TLS region
@@ -377,11 +422,11 @@ void KProcess::PrepareForTermination() {
ChangeStatus(ProcessStatus::Exiting);
const auto stop_threads = [this](const std::vector<KThread*>& in_thread_list) {
for (auto& thread : in_thread_list) {
for (auto* thread : in_thread_list) {
if (thread->GetOwnerProcess() != this)
continue;
if (thread == kernel.CurrentScheduler()->GetCurrentThread())
if (thread == GetCurrentThreadPointer(kernel))
continue;
// TODO(Subv): When are the other running/ready threads terminated?
@@ -536,6 +581,52 @@ ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) {
return ResultSuccess;
}
bool KProcess::InsertWatchpoint(Core::System& system, VAddr addr, u64 size,
DebugWatchpointType type) {
const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) {
return wp.type == DebugWatchpointType::None;
})};
if (watch == watchpoints.end()) {
return false;
}
watch->start_address = addr;
watch->end_address = addr + size;
watch->type = type;
for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) {
debug_page_refcounts[page]++;
system.Memory().MarkRegionDebug(page, PageSize, true);
}
return true;
}
bool KProcess::RemoveWatchpoint(Core::System& system, VAddr addr, u64 size,
DebugWatchpointType type) {
const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) {
return wp.start_address == addr && wp.end_address == addr + size && wp.type == type;
})};
if (watch == watchpoints.end()) {
return false;
}
watch->start_address = 0;
watch->end_address = 0;
watch->type = DebugWatchpointType::None;
for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) {
debug_page_refcounts[page]--;
if (!debug_page_refcounts[page]) {
system.Memory().MarkRegionDebug(page, PageSize, false);
}
}
return true;
}
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
Svc::MemoryPermission permission) {
@@ -556,9 +647,10 @@ bool KProcess::IsSignaled() const {
}
KProcess::KProcess(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_},
page_table{std::make_unique<KPageTable>(kernel_.System())}, handle_table{kernel_},
address_arbiter{kernel_.System()}, condition_var{kernel_.System()}, state_lock{kernel_} {}
: KAutoObjectWithSlabHeapAndContainer{kernel_}, page_table{std::make_unique<KPageTable>(
kernel_.System())},
handle_table{kernel_}, address_arbiter{kernel_.System()}, condition_var{kernel_.System()},
state_lock{kernel_}, list_lock{kernel_} {}
KProcess::~KProcess() = default;

View File

@@ -7,6 +7,7 @@
#include <array>
#include <cstddef>
#include <list>
#include <map>
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/k_address_arbiter.h"
@@ -63,6 +64,25 @@ enum class ProcessStatus {
DebugBreak,
};
enum class ProcessActivity : u32 {
Runnable,
Paused,
};
enum class DebugWatchpointType : u8 {
None = 0,
Read = 1 << 0,
Write = 1 << 1,
ReadOrWrite = Read | Write,
};
DECLARE_ENUM_FLAG_OPERATORS(DebugWatchpointType);
struct DebugWatchpoint {
VAddr start_address;
VAddr end_address;
DebugWatchpointType type;
};
class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> {
KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
@@ -282,17 +302,17 @@ public:
u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
/// Gets the list of all threads created with this process as their owner.
const std::list<const KThread*>& GetThreadList() const {
std::list<KThread*>& GetThreadList() {
return thread_list;
}
/// Registers a thread as being created under this process,
/// adding it to this process' thread list.
void RegisterThread(const KThread* thread);
void RegisterThread(KThread* thread);
/// Unregisters a thread from this process, removing it
/// from this process' thread list.
void UnregisterThread(const KThread* thread);
void UnregisterThread(KThread* thread);
/// Clears the signaled state of the process if and only if it's signaled.
///
@@ -347,6 +367,8 @@ public:
void DoWorkerTaskImpl();
ResultCode SetActivity(ProcessActivity activity);
void PinCurrentThread(s32 core_id);
void UnpinCurrentThread(s32 core_id);
void UnpinThread(KThread* thread);
@@ -367,6 +389,19 @@ public:
// Frees a used TLS slot identified by the given address
ResultCode DeleteThreadLocalRegion(VAddr addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Debug watchpoint management
// Attempts to insert a watchpoint into a free slot. Returns false if none are available.
bool InsertWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type);
// Attempts to remove the watchpoint specified by the given parameters.
bool RemoveWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type);
const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const {
return watchpoints;
}
private:
void PinThread(s32 core_id, KThread* thread) {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
@@ -442,7 +477,7 @@ private:
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
/// List of threads that are running with this process as their owner.
std::list<const KThread*> thread_list;
std::list<KThread*> thread_list;
/// List of shared memory that are running with this process as their owner.
std::list<KSharedMemoryInfo*> shared_memory_list;
@@ -471,10 +506,13 @@ private:
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{};
std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> watchpoints{};
std::map<VAddr, u64> debug_page_refcounts;
KThread* exception_thread{};
KLightLock state_lock;
KLightLock list_lock;
using TLPTree =
Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;

View File

@@ -317,7 +317,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
{
KThread* best_thread = priority_queue.GetScheduledFront(cpu_core_id);
if (best_thread == GetCurrentThread()) {
if (best_thread == GetCurrentThreadPointer(kernel)) {
best_thread = priority_queue.GetScheduledNext(cpu_core_id, best_thread);
}
@@ -424,7 +424,7 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
KThread& cur_thread = GetCurrentThread(kernel);
KProcess& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -463,7 +463,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
KThread& cur_thread = GetCurrentThread(kernel);
KProcess& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -551,7 +551,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
KThread& cur_thread = GetCurrentThread(kernel);
KProcess& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -642,7 +642,7 @@ KScheduler::~KScheduler() {
ASSERT(!idle_thread);
}
KThread* KScheduler::GetCurrentThread() const {
KThread* KScheduler::GetSchedulerCurrentThread() const {
if (auto result = current_thread.load(); result) {
return result;
}
@@ -654,7 +654,7 @@ u64 KScheduler::GetLastContextSwitchTicks() const {
}
void KScheduler::RescheduleCurrentCore() {
ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1);
ASSERT(GetCurrentThread(system.Kernel()).GetDisableDispatchCount() == 1);
auto& phys_core = system.Kernel().PhysicalCore(core_id);
if (phys_core.IsInterrupted()) {
@@ -665,7 +665,7 @@ void KScheduler::RescheduleCurrentCore() {
if (state.needs_scheduling.load()) {
Schedule();
} else {
GetCurrentThread()->EnableDispatch();
GetCurrentThread(system.Kernel()).EnableDispatch();
guard.Unlock();
}
}
@@ -710,6 +710,7 @@ void KScheduler::Reload(KThread* thread) {
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64());
cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints());
cpu_core.SetTlsAddress(thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
@@ -717,13 +718,18 @@ void KScheduler::Reload(KThread* thread) {
void KScheduler::SwitchContextStep2() {
// Load context of new thread
Reload(GetCurrentThread());
Reload(GetCurrentThreadPointer(system.Kernel()));
RescheduleCurrentCore();
}
void KScheduler::Schedule() {
ASSERT(GetCurrentThread(system.Kernel()).GetDisableDispatchCount() == 1);
this->ScheduleImpl();
}
void KScheduler::ScheduleImpl() {
KThread* previous_thread = GetCurrentThread();
KThread* previous_thread = GetCurrentThreadPointer(system.Kernel());
KThread* next_thread = state.highest_priority_thread;
state.needs_scheduling.store(false);
@@ -761,6 +767,7 @@ void KScheduler::ScheduleImpl() {
old_context = &previous_thread->GetHostContext();
// Set the new thread.
SetCurrentThread(system.Kernel(), next_thread);
current_thread.store(next_thread);
guard.Unlock();
@@ -804,6 +811,7 @@ void KScheduler::SwitchToCurrent() {
}
}
auto thread = next_thread ? next_thread : idle_thread;
SetCurrentThread(system.Kernel(), thread);
Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
} while (!is_switch_pending());
}
@@ -829,6 +837,7 @@ void KScheduler::Initialize() {
idle_thread = KThread::Create(system.Kernel());
ASSERT(KThread::InitializeIdleThread(system, idle_thread, core_id).IsSuccess());
idle_thread->SetName(fmt::format("IdleThread:{}", core_id));
idle_thread->EnableDispatch();
}
KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)

View File

@@ -48,18 +48,13 @@ public:
void Reload(KThread* thread);
/// Gets the current running thread
[[nodiscard]] KThread* GetCurrentThread() const;
[[nodiscard]] KThread* GetSchedulerCurrentThread() const;
/// Gets the idle thread
[[nodiscard]] KThread* GetIdleThread() const {
return idle_thread;
}
/// Returns true if the scheduler is idle
[[nodiscard]] bool IsIdle() const {
return GetCurrentThread() == idle_thread;
}
/// Gets the timestamp for the last context switch in ticks.
[[nodiscard]] u64 GetLastContextSwitchTicks() const;
@@ -149,10 +144,7 @@ private:
void RotateScheduledQueue(s32 cpu_core_id, s32 priority);
void Schedule() {
ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1);
this->ScheduleImpl();
}
void Schedule();
/// Switches the CPU's active thread context to that of the specified thread
void ScheduleImpl();

View File

@@ -97,13 +97,13 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
"object_id {} is too big! This probably means a recent service call "
"to {} needed to return a new interface!",
object_id, name);
UNREACHABLE();
ASSERT(false);
return ResultSuccess; // Ignore error if asserts are off
}
if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
return strong_ptr->HandleSyncRequest(*this, context);
} else {
UNREACHABLE();
ASSERT(false);
return ResultSuccess;
}

View File

@@ -133,7 +133,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
UNIMPLEMENTED();
break;
default:
UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
break;
}
thread_type = type;
@@ -225,7 +225,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
// Setup the stack parameters.
StackParameters& sp = GetStackParameters();
sp.cur_thread = this;
sp.disable_count = 0;
sp.disable_count = 1;
SetInExceptionHandler();
// Set thread ID.
@@ -267,15 +267,15 @@ ResultCode KThread::InitializeDummyThread(KThread* thread) {
ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
Core::CpuManager::GetIdleThreadStartFunc(),
system.GetCpuManager().GetStartFuncParamater());
system.GetCpuManager().GetStartFuncParameter());
}
ResultCode KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread,
KThreadFunction func, uintptr_t arg,
s32 virt_core) {
return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority,
Core::CpuManager::GetSuspendThreadStartFunc(),
system.GetCpuManager().GetStartFuncParamater());
Core::CpuManager::GetShutdownThreadStartFunc(),
system.GetCpuManager().GetStartFuncParameter());
}
ResultCode KThread::InitializeUserThread(Core::System& system, KThread* thread,
@@ -284,7 +284,7 @@ ResultCode KThread::InitializeUserThread(Core::System& system, KThread* thread,
system.Kernel().GlobalSchedulerContext().AddThread(thread);
return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner,
ThreadType::User, Core::CpuManager::GetGuestThreadStartFunc(),
system.GetCpuManager().GetStartFuncParamater());
system.GetCpuManager().GetStartFuncParameter());
}
void KThread::PostDestroy(uintptr_t arg) {
@@ -382,7 +382,7 @@ void KThread::FinishTermination() {
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
KThread* core_thread{};
do {
core_thread = kernel.Scheduler(i).GetCurrentThread();
core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
} while (core_thread == this);
}
}
@@ -631,7 +631,7 @@ ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
s32 thread_core;
for (thread_core = 0; thread_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES);
++thread_core) {
if (kernel.Scheduler(thread_core).GetCurrentThread() == this) {
if (kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) {
thread_is_current = true;
break;
}
@@ -748,6 +748,19 @@ void KThread::Continue() {
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
void KThread::WaitUntilSuspended() {
// Make sure we have a suspend requested.
ASSERT(IsSuspendRequested());
// Loop until the thread is not executing on any core.
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
KThread* core_thread{};
do {
core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
} while (core_thread == this);
}
}
ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
// Lock ourselves.
KScopedLightLock lk(activity_pause_lock);
@@ -809,7 +822,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
// Check if the thread is currently running.
// If it is, we'll need to retry.
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
if (kernel.Scheduler(i).GetCurrentThread() == this) {
if (kernel.Scheduler(i).GetSchedulerCurrentThread() == this) {
thread_is_current = true;
break;
}
@@ -1014,8 +1027,6 @@ ResultCode KThread::Run() {
// Set our state and finish.
SetState(ThreadState::Runnable);
DisableDispatch();
return ResultSuccess;
}
}
@@ -1164,6 +1175,10 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
return host_context;
}
void SetCurrentThread(KernelCore& kernel, KThread* thread) {
kernel.SetCurrentEmuThread(thread);
}
KThread* GetCurrentThreadPointer(KernelCore& kernel) {
return kernel.GetCurrentEmuThread();
}

View File

@@ -106,6 +106,7 @@ enum class StepState : u32 {
StepPerformed, ///< Thread has stepped, waiting to be scheduled again
};
void SetCurrentThread(KernelCore& kernel, KThread* thread);
[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
@@ -207,6 +208,8 @@ public:
void Continue();
void WaitUntilSuspended();
constexpr void SetSyncedIndex(s32 index) {
synced_index = index;
}

View File

@@ -76,7 +76,7 @@ struct KernelCore::Impl {
InitializeMemoryLayout();
Init::InitializeKPageBufferSlabHeap(system);
InitializeSchedulers();
InitializeSuspendThreads();
InitializeShutdownThreads();
InitializePreemption(kernel);
RegisterHostThread();
@@ -143,9 +143,9 @@ struct KernelCore::Impl {
CleanupObject(system_resource_limit);
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
if (suspend_threads[core_id]) {
suspend_threads[core_id]->Close();
suspend_threads[core_id] = nullptr;
if (shutdown_threads[core_id]) {
shutdown_threads[core_id]->Close();
shutdown_threads[core_id] = nullptr;
}
schedulers[core_id]->Finalize();
@@ -212,7 +212,9 @@ struct KernelCore::Impl {
system_resource_limit = KResourceLimit::Create(system.Kernel());
system_resource_limit->Initialize(&core_timing);
const auto [total_size, kernel_size] = memory_layout->GetTotalAndKernelMemorySizes();
const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()};
const auto total_size{sizes.first};
const auto kernel_size{sizes.second};
// If setting the default system values fails, then something seriously wrong has occurred.
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size)
@@ -245,13 +247,13 @@ struct KernelCore::Impl {
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
}
void InitializeSuspendThreads() {
void InitializeShutdownThreads() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
suspend_threads[core_id] = KThread::Create(system.Kernel());
ASSERT(KThread::InitializeHighPriorityThread(system, suspend_threads[core_id], {}, {},
shutdown_threads[core_id] = KThread::Create(system.Kernel());
ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {},
core_id)
.IsSuccess());
suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
shutdown_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
}
}
@@ -329,6 +331,8 @@ struct KernelCore::Impl {
return is_shutting_down.load(std::memory_order_relaxed);
}
static inline thread_local KThread* current_thread{nullptr};
KThread* GetCurrentEmuThread() {
// If we are shutting down the kernel, none of this is relevant anymore.
if (IsShuttingDown()) {
@@ -339,7 +343,12 @@ struct KernelCore::Impl {
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
return GetHostDummyThread();
}
return schedulers[thread_id]->GetCurrentThread();
return current_thread;
}
void SetCurrentEmuThread(KThread* thread) {
current_thread = thread;
}
void DeriveInitialMemoryLayout() {
@@ -766,7 +775,7 @@ struct KernelCore::Impl {
std::weak_ptr<ServiceThread> default_service_thread;
Common::ThreadWorker service_threads_manager;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads;
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
@@ -917,6 +926,12 @@ const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
return *impl->global_object_list_container;
}
void KernelCore::InterruptAllPhysicalCores() {
for (auto& physical_core : impl->cores) {
physical_core.Interrupt();
}
}
void KernelCore::InvalidateAllInstructionCaches() {
for (auto& physical_core : impl->cores) {
physical_core.ArmInterface().ClearInstructionCache();
@@ -1016,6 +1031,10 @@ KThread* KernelCore::GetCurrentEmuThread() const {
return impl->GetCurrentEmuThread();
}
void KernelCore::SetCurrentEmuThread(KThread* thread) {
impl->SetCurrentEmuThread(thread);
}
KMemoryManager& KernelCore::MemoryManager() {
return *impl->memory_manager;
}
@@ -1064,22 +1083,29 @@ const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
return *impl->hidbus_shared_mem;
}
void KernelCore::Suspend(bool in_suspention) {
const bool should_suspend = exception_exited || in_suspention;
{
KScopedSchedulerLock lock(*this);
const auto state = should_suspend ? ThreadState::Runnable : ThreadState::Waiting;
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
impl->suspend_threads[core_id]->SetState(state);
impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
ThreadWaitReasonForDebugging::Suspended);
if (!should_suspend) {
impl->suspend_threads[core_id]->DisableDispatch();
void KernelCore::Suspend(bool suspended) {
const bool should_suspend{exception_exited || suspended};
const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
for (auto* process : GetProcessList()) {
process->SetActivity(activity);
if (should_suspend) {
// Wait for execution to stop
for (auto* thread : process->GetThreadList()) {
thread->WaitUntilSuspended();
}
}
}
}
void KernelCore::ShutdownCores() {
for (auto* thread : impl->shutdown_threads) {
void(thread->Run());
}
InterruptAllPhysicalCores();
}
bool KernelCore::IsMulticore() const {
return impl->is_multicore;
}

View File

@@ -184,6 +184,8 @@ public:
const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const;
void InterruptAllPhysicalCores();
void InvalidateAllInstructionCaches();
void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
@@ -224,6 +226,9 @@ public:
/// Gets the current host_thread/guest_thread pointer.
KThread* GetCurrentEmuThread() const;
/// Sets the current guest_thread pointer.
void SetCurrentEmuThread(KThread* thread);
/// Gets the current host_thread handle.
u32 GetCurrentHostThreadID() const;
@@ -269,12 +274,15 @@ public:
/// Gets the shared memory object for HIDBus services.
const Kernel::KSharedMemory& GetHidBusSharedMem() const;
/// Suspend/unsuspend the OS.
void Suspend(bool in_suspention);
/// Suspend/unsuspend all processes.
void Suspend(bool suspend);
/// Exceptional exit the OS.
/// Exceptional exit all processes.
void ExceptionalExit();
/// Notify emulated CPU cores to shut down.
void ShutdownCores();
bool IsMulticore() const;
bool IsShuttingDown() const;

View File

@@ -15,6 +15,7 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/debugger/debugger.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_code_memory.h"
@@ -326,7 +327,6 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
auto thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLock lock(kernel);
@@ -336,7 +336,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
}
return thread->GetWaitResult();
return GetCurrentThread(kernel).GetWaitResult();
}
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
@@ -623,10 +623,16 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
handle_debug_buffer(info1, info2);
auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
const auto thread_processor_id = current_thread->GetActiveCore();
system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
}
if (system.DebuggerEnabled()) {
auto* thread = system.Kernel().GetCurrentEmuThread();
system.GetDebugger().NotifyThreadStopped(thread);
thread->RequestSuspend(Kernel::SuspendType::Debug);
}
}
static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
@@ -877,7 +883,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
const auto& core_timing = system.CoreTiming();
const auto& scheduler = *system.Kernel().CurrentScheduler();
const auto* const current_thread = scheduler.GetCurrentThread();
const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
const bool same_thread = current_thread == thread.GetPointerUnsafe();
const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
@@ -1096,7 +1102,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
if (thread->GetRawState() != ThreadState::Runnable) {
bool current = false;
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) {
if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
current = true;
break;
}
@@ -1719,11 +1725,12 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
/// Exits the current process
static void ExitProcess(Core::System& system) {
auto* current_process = system.Kernel().CurrentProcess();
UNIMPLEMENTED();
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
"Process has already exited");
system.Exit();
}
static void ExitProcess32(Core::System& system) {
@@ -1843,7 +1850,7 @@ static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
static void ExitThread(Core::System& system) {
LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
system.GlobalSchedulerContext().RemoveThread(current_thread);
current_thread->Exit();
system.Kernel().UnregisterInUseObject(current_thread);
@@ -1876,7 +1883,7 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
KScheduler::YieldToAnyThread(kernel);
} else {
// Nintendo does nothing at all if an otherwise invalid value is passed.
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
}
@@ -2530,7 +2537,7 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
return ResultOutOfRange;
}
const auto* const current_process = system.Kernel().CurrentProcess();
auto* const current_process = system.Kernel().CurrentProcess();
const auto total_copy_size = out_thread_ids_size * sizeof(u64);
if (out_thread_ids_size > 0 &&
@@ -2982,11 +2989,10 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) {
}
void Call(Core::System& system, u32 immediate) {
system.ExitDynarmicProfile();
auto& kernel = system.Kernel();
kernel.EnterSVCProfile();
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
auto* thread = GetCurrentThreadPointer(kernel);
thread->SetIsCallingSvc();
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
@@ -3007,8 +3013,6 @@ void Call(Core::System& system, u32 immediate) {
auto* host_context = thread->GetHostContext().get();
host_context->Rewind();
}
system.EnterDynarmicProfile();
}
} // namespace Kernel::Svc

View File

@@ -686,7 +686,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
{66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"},
{67, nullptr, "CancelCpuBoostMode"},
{68, nullptr, "GetBuiltInDisplayType"},
{80, nullptr, "PerformSystemButtonPressingIfInFocus"},
{80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"},
{90, nullptr, "SetPerformanceConfigurationChangedNotification"},
{91, nullptr, "GetCurrentPerformanceConfiguration"},
{100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
@@ -826,6 +826,16 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) {
apm_sys->SetCpuBoostMode(ctx);
}
void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto system_button{rp.PopEnum<SystemButtonType>()};
LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(
Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");

View File

@@ -220,6 +220,18 @@ private:
Docked = 1,
};
// This is nn::am::service::SystemButtonType
enum class SystemButtonType {
None,
HomeButtonShortPressing,
HomeButtonLongPressing,
PowerButtonShortPressing,
PowerButtonLongPressing,
ShutdownSystem,
CaptureButtonShortPressing,
CaptureButtonLongPressing,
};
void GetEventHandle(Kernel::HLERequestContext& ctx);
void ReceiveMessage(Kernel::HLERequestContext& ctx);
void GetCurrentFocusState(Kernel::HLERequestContext& ctx);
@@ -234,6 +246,7 @@ private:
void EndVrModeEx(Kernel::HLERequestContext& ctx);
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
void PerformSystemButtonPressingIfInFocus(Kernel::HLERequestContext& ctx);
void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(Kernel::HLERequestContext& ctx);
std::shared_ptr<AppletMessageQueue> msg_queue;

View File

@@ -178,7 +178,7 @@ ResultCode Controller::GetStatus() const {
}
void Controller::ExecuteInteractive() {
UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}
void Controller::Execute() {

View File

@@ -156,7 +156,7 @@ ResultCode Error::GetStatus() const {
}
void Error::ExecuteInteractive() {
UNREACHABLE_MSG("Unexpected interactive applet data!");
ASSERT_MSG(false, "Unexpected interactive applet data!");
}
void Error::Execute() {

View File

@@ -76,7 +76,7 @@ ResultCode Auth::GetStatus() const {
}
void Auth::ExecuteInteractive() {
UNREACHABLE_MSG("Unexpected interactive applet data.");
ASSERT_MSG(false, "Unexpected interactive applet data.");
}
void Auth::Execute() {
@@ -175,7 +175,7 @@ ResultCode PhotoViewer::GetStatus() const {
}
void PhotoViewer::ExecuteInteractive() {
UNREACHABLE_MSG("Unexpected interactive applet data.");
ASSERT_MSG(false, "Unexpected interactive applet data.");
}
void PhotoViewer::Execute() {

View File

@@ -67,7 +67,7 @@ ResultCode MiiEdit::GetStatus() const {
}
void MiiEdit::ExecuteInteractive() {
UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}
void MiiEdit::Execute() {

View File

@@ -44,7 +44,7 @@ ResultCode ProfileSelect::GetStatus() const {
}
void ProfileSelect::ExecuteInteractive() {
UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}
void ProfileSelect::Execute() {

View File

@@ -71,7 +71,7 @@ void SoftwareKeyboard::Initialize() {
InitializeBackground(applet_mode);
break;
default:
UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode);
ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
break;
}
}

View File

@@ -279,7 +279,7 @@ void WebBrowser::Initialize() {
InitializeLobby();
break;
default:
UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind);
ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
break;
}
}
@@ -320,7 +320,7 @@ void WebBrowser::Execute() {
ExecuteLobby();
break;
default:
UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind);
ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
WebBrowserExit(WebExitReason::EndButtonPressed);
break;
}

View File

@@ -899,7 +899,7 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
case FileSys::SaveDataSpaceId::TemporaryStorage:
case FileSys::SaveDataSpaceId::ProperSystem:
case FileSys::SaveDataSpaceId::SafeMode:
UNREACHABLE();
ASSERT(false);
}
auto filesystem = std::make_shared<IFileSystem>(system, std::move(dir.Unwrap()),

View File

@@ -1,6 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/glue/notif.h"
@@ -9,11 +14,11 @@ namespace Service::Glue {
NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} {
// clang-format off
static const FunctionInfo functions[] = {
{500, nullptr, "RegisterAlarmSetting"},
{510, nullptr, "UpdateAlarmSetting"},
{500, &NOTIF_A::RegisterAlarmSetting, "RegisterAlarmSetting"},
{510, &NOTIF_A::UpdateAlarmSetting, "UpdateAlarmSetting"},
{520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"},
{530, nullptr, "LoadApplicationParameter"},
{540, nullptr, "DeleteAlarmSetting"},
{530, &NOTIF_A::LoadApplicationParameter, "LoadApplicationParameter"},
{540, &NOTIF_A::DeleteAlarmSetting, "DeleteAlarmSetting"},
{1000, &NOTIF_A::Initialize, "Initialize"},
};
// clang-format on
@@ -23,21 +28,132 @@ NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} {
NOTIF_A::~NOTIF_A() = default;
void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) {
// Returns an array of AlarmSetting
constexpr s32 alarm_count = 0;
void NOTIF_A::RegisterAlarmSetting(Kernel::HLERequestContext& ctx) {
const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0);
const auto application_parameter_size = ctx.GetReadBufferSize(1);
LOG_WARNING(Service_NOTIF, "(STUBBED) called");
ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting),
"alarm_setting_buffer_size is not 0x40 bytes");
ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter),
"application_parameter_size is bigger than 0x400 bytes");
AlarmSetting new_alarm{};
memcpy(&new_alarm, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting));
// TODO: Count alarms per game id
if (alarms.size() >= max_alarms) {
LOG_ERROR(Service_NOTIF, "Alarm limit reached");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
new_alarm.alarm_setting_id = last_alarm_setting_id++;
alarms.push_back(new_alarm);
// TODO: Save application parameter data
LOG_WARNING(Service_NOTIF,
"(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}",
application_parameter_size, new_alarm.alarm_setting_id, new_alarm.kind,
new_alarm.muted);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(new_alarm.alarm_setting_id);
}
void NOTIF_A::UpdateAlarmSetting(Kernel::HLERequestContext& ctx) {
const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0);
const auto application_parameter_size = ctx.GetReadBufferSize(1);
ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting),
"alarm_setting_buffer_size is not 0x40 bytes");
ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter),
"application_parameter_size is bigger than 0x400 bytes");
AlarmSetting alarm_setting{};
memcpy(&alarm_setting, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting));
const auto alarm_it = GetAlarmFromId(alarm_setting.alarm_setting_id);
if (alarm_it != alarms.end()) {
LOG_DEBUG(Service_NOTIF, "Alarm updated");
*alarm_it = alarm_setting;
// TODO: Save application parameter data
}
LOG_WARNING(Service_NOTIF,
"(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}",
application_parameter_size, alarm_setting.alarm_setting_id, alarm_setting.kind,
alarm_setting.muted);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_NOTIF, "called, alarm_count={}", alarms.size());
// TODO: Only return alarms of this game id
ctx.WriteBuffer(alarms);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(alarm_count);
rb.Push(static_cast<u32>(alarms.size()));
}
void NOTIF_A::LoadApplicationParameter(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto alarm_setting_id{rp.Pop<AlarmSettingId>()};
const auto alarm_it = GetAlarmFromId(alarm_setting_id);
if (alarm_it == alarms.end()) {
LOG_ERROR(Service_NOTIF, "Invalid alarm setting id={}", alarm_setting_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
// TODO: Read application parameter related to this setting id
ApplicationParameter application_parameter{};
LOG_WARNING(Service_NOTIF, "(STUBBED) called, alarm_setting_id={}", alarm_setting_id);
ctx.WriteBuffer(application_parameter);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(application_parameter.size()));
}
void NOTIF_A::DeleteAlarmSetting(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto alarm_setting_id{rp.Pop<AlarmSettingId>()};
std::erase_if(alarms, [alarm_setting_id](const AlarmSetting& alarm) {
return alarm.alarm_setting_id == alarm_setting_id;
});
LOG_INFO(Service_NOTIF, "called, alarm_setting_id={}", alarm_setting_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void NOTIF_A::Initialize(Kernel::HLERequestContext& ctx) {
// TODO: Load previous alarms from config
LOG_WARNING(Service_NOTIF, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
std::vector<NOTIF_A::AlarmSetting>::iterator NOTIF_A::GetAlarmFromId(
AlarmSettingId alarm_setting_id) {
return std::find_if(alarms.begin(), alarms.end(),
[alarm_setting_id](const AlarmSetting& alarm) {
return alarm.alarm_setting_id == alarm_setting_id;
});
}
} // namespace Service::Glue

View File

@@ -3,6 +3,10 @@
#pragma once
#include <array>
#include <vector>
#include "common/uuid.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -17,8 +21,52 @@ public:
~NOTIF_A() override;
private:
static constexpr std::size_t max_alarms = 8;
// This is nn::notification::AlarmSettingId
using AlarmSettingId = u16;
static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size");
using ApplicationParameter = std::array<u8, 0x400>;
static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size");
struct DailyAlarmSetting {
s8 hour;
s8 minute;
};
static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size");
struct WeeklyScheduleAlarmSetting {
INSERT_PADDING_BYTES(0xA);
std::array<DailyAlarmSetting, 0x7> day_of_week;
};
static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18,
"WeeklyScheduleAlarmSetting is an invalid size");
// This is nn::notification::AlarmSetting
struct AlarmSetting {
AlarmSettingId alarm_setting_id;
u8 kind;
u8 muted;
INSERT_PADDING_BYTES(0x4);
Common::UUID account_id;
u64 application_id;
INSERT_PADDING_BYTES(0x8);
WeeklyScheduleAlarmSetting schedule;
};
static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size");
void RegisterAlarmSetting(Kernel::HLERequestContext& ctx);
void UpdateAlarmSetting(Kernel::HLERequestContext& ctx);
void ListAlarmSettings(Kernel::HLERequestContext& ctx);
void LoadApplicationParameter(Kernel::HLERequestContext& ctx);
void DeleteAlarmSetting(Kernel::HLERequestContext& ctx);
void Initialize(Kernel::HLERequestContext& ctx);
std::vector<AlarmSetting>::iterator GetAlarmFromId(AlarmSettingId alarm_setting_id);
std::vector<AlarmSetting> alarms{};
AlarmSettingId last_alarm_setting_id{};
};
} // namespace Service::Glue

View File

@@ -61,6 +61,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
}
last_update_timestamp = shared_memory->gesture_lifo.timestamp;
UpdateGestureSharedMemory(gesture, time_difference);
}
void Controller_Gesture::ReadTouchInput() {
@@ -94,8 +95,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
return false;
}
void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
GestureProperties& gesture,
void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
f32 time_difference) {
GestureType type = GestureType::Idle;
GestureAttribute attributes{};

View File

@@ -107,8 +107,7 @@ private:
bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
// Updates the shared memory to the next state
void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture,
f32 time_difference);
void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
// Initializes new gesture
void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);

View File

@@ -160,7 +160,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->system_properties.raw = 0;
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
UNREACHABLE();
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
shared_memory->style_tag.fullkey.Assign(1);
@@ -422,7 +422,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
libnx_state.connection_status.is_connected.Assign(1);
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
UNREACHABLE();
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::NES:
@@ -597,7 +597,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
UNREACHABLE();
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
@@ -856,7 +856,7 @@ void Controller_NPad::VibrateController(
}
if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
UNREACHABLE_MSG("DeviceIndex should never be None!");
ASSERT_MSG(false, "DeviceIndex should never be None!");
return;
}

View File

@@ -1441,7 +1441,7 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
break;
case Core::HID::DeviceIndex::None:
default:
UNREACHABLE_MSG("DeviceIndex should never be None!");
ASSERT_MSG(false, "DeviceIndex should never be None!");
vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
break;
}

View File

@@ -347,7 +347,7 @@ public:
}
if (!succeeded) {
UNREACHABLE_MSG("Out of address space!");
ASSERT_MSG(false, "Out of address space!");
return Kernel::ResultOutOfMemory;
}

View File

@@ -290,7 +290,7 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo
u8 glasses_type{};
while (glasses_type_start < glasses_type_info.values[glasses_type]) {
if (++glasses_type >= glasses_type_info.values_count) {
UNREACHABLE();
ASSERT(false);
break;
}
}

View File

@@ -150,9 +150,9 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
event.event->GetWritableEvent().Clear();
if (events_interface.failed[event_id]) {
{
auto lk = system.StallCPU();
auto lk = system.StallProcesses();
gpu.WaitFence(params.syncpt_id, target_value);
system.UnstallCPU();
system.UnstallProcesses();
}
std::memcpy(output.data(), &params, sizeof(params));
events_interface.failed[event_id] = false;

View File

@@ -23,7 +23,7 @@ u32 SyncpointManager::AllocateSyncpoint() {
return syncpoint_id;
}
}
UNREACHABLE_MSG("No more available syncpoints!");
ASSERT_MSG(false, "No more available syncpoints!");
return {};
}

View File

@@ -659,7 +659,7 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
value = core->consumer_usage_bit;
break;
default:
UNREACHABLE();
ASSERT(false);
return Status::BadValue;
}

View File

@@ -48,12 +48,12 @@ ResultCode StandardUserSystemClockCore::GetClockContext(Core::System& system,
}
ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext&) {
UNREACHABLE();
UNIMPLEMENTED();
return ERROR_NOT_IMPLEMENTED;
}
ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) {
UNREACHABLE();
UNIMPLEMENTED();
return ERROR_NOT_IMPLEMENTED;
}

View File

@@ -111,7 +111,7 @@ struct TimeManager::Impl final {
FileSys::VirtualFile& vfs_file) {
if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
location_name, vfs_file) != ResultSuccess) {
UNREACHABLE();
ASSERT(false);
return;
}
@@ -155,7 +155,7 @@ struct TimeManager::Impl final {
} else {
if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) !=
ResultSuccess) {
UNREACHABLE();
ASSERT(false);
return;
}
}
@@ -170,7 +170,7 @@ struct TimeManager::Impl final {
if (standard_network_system_clock_core.SetSystemClockContext(clock_context) !=
ResultSuccess) {
UNREACHABLE();
ASSERT(false);
return;
}
@@ -183,7 +183,7 @@ struct TimeManager::Impl final {
Clock::SteadyClockTimePoint steady_clock_time_point) {
if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
system_, is_automatic_correction_enabled) != ResultSuccess) {
UNREACHABLE();
ASSERT(false);
return;
}
@@ -203,7 +203,7 @@ struct TimeManager::Impl final {
if (GetStandardLocalSystemClockCore()
.SetCurrentTime(system_, timespan.ToSeconds())
.IsError()) {
UNREACHABLE();
ASSERT(false);
return;
}
}

View File

@@ -279,7 +279,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) {
break;
}
default:
UNREACHABLE();
ASSERT(false);
}
return value + rule.transition_time + offset;
}

View File

@@ -128,11 +128,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
// Apply patches if necessary
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
std::vector<u8> pi_header;
pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.data(),
program_image.data() + program_image.size());
std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader));
std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(),
program_image.size());
pi_header = pm->PatchNSO(pi_header, nso_file.GetName());

View File

@@ -67,6 +67,16 @@ struct Memory::Impl {
return system.DeviceMemory().GetPointer(paddr) + vaddr;
}
[[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const {
const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]};
if (paddr == 0) {
return {};
}
return system.DeviceMemory().GetPointer(paddr) + vaddr;
}
u8 Read8(const VAddr addr) {
return Read<u8>(addr);
}
@@ -187,6 +197,12 @@ struct Memory::Impl {
on_memory(copy_amount, mem_ptr);
break;
}
case Common::PageType::DebugMemory: {
DEBUG_ASSERT(pointer);
u8* const mem_ptr{GetPointerFromDebugMemory(current_vaddr)};
on_memory(copy_amount, mem_ptr);
break;
}
case Common::PageType::RasterizerCachedMemory: {
u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
on_rasterizer(current_vaddr, copy_amount, host_ptr);
@@ -316,6 +332,58 @@ struct Memory::Impl {
});
}
void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
if (vaddr == 0) {
return;
}
// Iterate over a contiguous CPU address space, marking/unmarking the region.
// The region is at a granularity of CPU pages.
const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
const Common::PageType page_type{
current_page_table->pointers[vaddr >> PAGE_BITS].Type()};
if (debug) {
// Switch page type to debug if now debug
switch (page_type) {
case Common::PageType::Unmapped:
ASSERT_MSG(false, "Attempted to mark unmapped pages as debug");
break;
case Common::PageType::RasterizerCachedMemory:
case Common::PageType::DebugMemory:
// Page is already marked.
break;
case Common::PageType::Memory:
current_page_table->pointers[vaddr >> PAGE_BITS].Store(
nullptr, Common::PageType::DebugMemory);
break;
default:
UNREACHABLE();
}
} else {
// Switch page type to non-debug if now non-debug
switch (page_type) {
case Common::PageType::Unmapped:
ASSERT_MSG(false, "Attempted to mark unmapped pages as non-debug");
break;
case Common::PageType::RasterizerCachedMemory:
case Common::PageType::Memory:
// Don't mess with already non-debug or rasterizer memory.
break;
case Common::PageType::DebugMemory: {
u8* const pointer{GetPointerFromDebugMemory(vaddr & ~PAGE_MASK)};
current_page_table->pointers[vaddr >> PAGE_BITS].Store(
pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);
break;
}
default:
UNREACHABLE();
}
}
}
}
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
if (vaddr == 0) {
return;
@@ -342,6 +410,7 @@ struct Memory::Impl {
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
case Common::PageType::DebugMemory:
case Common::PageType::Memory:
current_page_table->pointers[vaddr >> PAGE_BITS].Store(
nullptr, Common::PageType::RasterizerCachedMemory);
@@ -360,6 +429,7 @@ struct Memory::Impl {
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
case Common::PageType::DebugMemory:
case Common::PageType::Memory:
// There can be more than one GPU region mapped per CPU region, so it's common
// that this area is already unmarked as cached.
@@ -460,6 +530,8 @@ struct Memory::Impl {
case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr);
return nullptr;
case Common::PageType::DebugMemory:
return GetPointerFromDebugMemory(vaddr);
case Common::PageType::RasterizerCachedMemory: {
u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
on_rasterizer();
@@ -591,7 +663,8 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
return false;
}
const auto [pointer, type] = page_table.pointers[page].PointerType();
return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory ||
type == Common::PageType::DebugMemory;
}
bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
@@ -707,4 +780,8 @@ void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
impl->RasterizerMarkRegionCached(vaddr, size, cached);
}
void Memory::MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
impl->MarkRegionDebug(vaddr, size, debug);
}
} // namespace Core::Memory

View File

@@ -446,6 +446,17 @@ public:
*/
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
/**
* Marks each page within the specified address range as debug or non-debug.
* Debug addresses are not accessible from fastmem pointers.
*
* @param vaddr The virtual address indicating the start of the address range.
* @param size The size of the address range in bytes.
* @param debug Whether or not any pages within the address range should be
* marked as debug or non-debug.
*/
void MarkRegionDebug(VAddr vaddr, u64 size, bool debug);
private:
Core::System& system;

Some files were not shown because too many files have changed in this diff Show More