Compare commits

..

209 Commits

Author SHA1 Message Date
Liam
adec4d0da7 cmake: update inclusion of system FFmpeg 2022-11-30 18:25:07 -05:00
Fernando S
4e89979c87 Merge pull request #9320 from yuzu-emu/fix-audio-suspend
AudioCore: Take suspend lock when stalling the running process.
2022-11-30 16:41:32 +01:00
bunnei
8fd4e44014 audio_core: sink_stream: Hold the suspend lock when process is stalled.
- Prevents us from clashing with other callers trying to un/stall.
2022-11-29 20:32:06 -08:00
Morph
49219b8a86 Merge pull request #9349 from lat9nq/cmake-322
CMakeLists: Bump minimum required CMake version to 3.22
2022-11-29 19:41:47 -05:00
liamwhite
d6b63239ae Merge pull request #9308 from lat9nq/from-scratch
startup_checks: Use Windows flow for *nix
2022-11-29 18:45:39 -05:00
liamwhite
cafca891ea Merge pull request #9322 from german77/pump_events
input_common: Pump SDL events from main thread
2022-11-29 18:45:25 -05:00
liamwhite
c845d8a9e8 Merge pull request #9352 from lioncash/vidcast
engines: Remove unnecessary casts
2022-11-29 18:45:16 -05:00
liamwhite
1a6785d296 Merge pull request #9354 from lioncash/const-param
host1x/syncpoint_manager: Pass DeregisterAction() handle as const-ref
2022-11-29 18:45:05 -05:00
Lioncash
b6d93b2c77 host1x/syncpoint_manager: Eliminate unnecessary std::function construction
We can just pass the function object through, and if it's a valid
function, then it will automatically be converted.
2022-11-29 08:58:50 -05:00
Lioncash
c4af7b3f5c host1x/syncpoint_manager: Pass DeregisterAction() handle as const-ref
The handle is only compared against and not modified in any way, so we
can pass it by const reference.

This also allows us to mark the respective parameters for
DeregisterGuestAction() and DeregisterHostAction() as const references
as well.
2022-11-29 08:55:33 -05:00
Lioncash
96ffc174aa maxwell_3d: Mark shifted value as unsigned
Otherwise this is technically creating a signed int result that gets
converted. Just a consistency change.

While we're in the area, we can mark Samples() as const.
2022-11-29 08:39:33 -05:00
Lioncash
d7ec031419 engines: Remove unnecessary casts
In a few cases we have some casts that can be trivially removed.
2022-11-29 08:38:46 -05:00
liamwhite
55a3cbfa0d Merge pull request #9340 from lioncash/nvdrv
nvdrv: Simplify builder declarations
2022-11-29 08:27:13 -05:00
liamwhite
55b546a110 Merge pull request #9347 from lioncash/vcast
video_core/surface: Eliminate casts in GetFormatType()
2022-11-29 08:26:39 -05:00
liamwhite
6b8ab9ed8f Merge pull request #9346 from lioncash/vtable
producer_listener: Add virtual destructor to IProducerListener
2022-11-29 08:26:32 -05:00
liamwhite
a7f1fa7bfc Merge pull request #9345 from lioncash/fence
consumer_base: Pass std::shared_ptr by const reference
2022-11-29 08:26:25 -05:00
liamwhite
b6373c5ea5 Merge pull request #9343 from lioncash/bounds
syncpoint_manager: Reduce redundant bounds checks
2022-11-29 08:26:16 -05:00
lat9nq
0941ae0b61 CMake: Directly link to SDL2-static when appropriate
Trying to be lazy and alias SDL2 to SDL2-static causes issues in later
versions of CMake. Just use the same condition to tell which one to use.
2022-11-28 23:21:14 -05:00
lat9nq
47b133c0b8 CMakeLists: Bump minimum required CMake version to 3.22 2022-11-28 22:48:49 -05:00
Lioncash
a9efea8ae9 video_core/surface: Eliminate casts in GetFormatType()
We can just compare directly and get rid of verbose casting.
2022-11-28 20:25:44 -05:00
Lioncash
25dda06f49 producer_listener: Add virtual destructor to IProducerListener
Several member variables are shared_ptr's to this base class. Even
though producer listeners are still unimplemented, this ensures we
always have consistent deletion behavior once this ends up being used
polymorphically.
2022-11-28 19:39:13 -05:00
Lioncash
ae74f46e78 buffer_item_consumer: Pass fence by const-ref in ReleaseBuffer()
This isn't directly modified.

Also allows rvalues to be used with it.
2022-11-28 19:16:20 -05:00
Lioncash
3020f72b0c buffer_queue_consumer: std::move std::shared_ptr in Connect()
Avoids an unnecessary reference count increment and decrement
2022-11-28 19:12:26 -05:00
Lioncash
c0c4f6dfa6 consumer_base: Pass shared_ptr by const reference
Avoids churning atomic reference count increments and decrements.
2022-11-28 19:09:44 -05:00
Lioncash
260a495a23 consumer_base: Remove redundant virtual
override already serves this purpose
2022-11-28 19:06:34 -05:00
Lioncash
7a329ae56c syncpoint_manager: Mark IsSyncpointAllocated() as const
This doesn't modify class state at all.
2022-11-28 16:08:21 -05:00
Lioncash
d0883027d6 syncpoint_manager: Reduce number of bounds checks
The only time we need to check bounds is on the first access.
2022-11-28 16:06:01 -05:00
Morph
bbb963a31a Merge pull request #9339 from lioncash/cacheheader
common/cache_management: Amend header includes
2022-11-28 13:12:20 -05:00
Morph
51abe35e05 Merge pull request #9338 from lioncash/properties
input_common/helpers: Mark analog property structs members as static constexpr
2022-11-28 13:12:14 -05:00
Morph
64ff79f919 Merge pull request #9337 from lioncash/pbr
common/input: Add helper functions for constructing input and output devices
2022-11-28 13:12:08 -05:00
Morph
e507c01a22 Merge pull request #9336 from lioncash/themepath
yuzu/main: Merge variable declaration into ifdef
2022-11-28 13:11:17 -05:00
Lioncash
ad787b20ca nvdrv: Simplify builder declarations
We can just use auto here. If one of these ever happens to not be
derived from nvdevice, then this will cause a compilation error.

We can also move the devices into the collection to get rid of an
unnecessary atomic reference count increment and decrement.
2022-11-28 10:43:48 -05:00
Lioncash
c2c9b44749 common/cache_management: Amend header includes
Narrows the include in the header to <cstddef>, since that's what houses
size_t's definition, meanwhile the <cstdint> include can be moved into
the cpp file.
2022-11-28 10:19:53 -05:00
Lioncash
8265c167d3 input_common/helpers: Mark analog property structs members as static constexpr
These are const with no dependency on any other data members, so we can
make these static constexpr to reduce the overall object size.
2022-11-28 10:10:37 -05:00
Lioncash
780ae92265 core/hid/emulated_controller: Use ranges version of transform
Makes the transform calls much nicer to read.
2022-11-28 10:00:42 -05:00
Lioncash
2ec7d0b5fd common/input: Add helpers functions for creating input and output devices
Avoids the redundancy of needing to explictly specify the common
namespace and the type.
2022-11-28 10:00:37 -05:00
Lioncash
7f42432f42 common/input: Pass ParamPackage by const reference in CreateDevice
This was previously being passed by value, which was unnecessary and
created more allocations than necessary.
2022-11-28 09:11:58 -05:00
Lioncash
e96f55b6e2 yuzu/main: Merge variable declaration into ifdef
This is only used in the non-Windows path.
2022-11-28 08:40:41 -05:00
liamwhite
6291eec700 Merge pull request #9325 from german77/default_by_default
yuzu-cmd: Fix default config value
2022-11-28 08:14:32 -05:00
german77
aaada241dc yuzu-cmd: Fix default config value 2022-11-27 20:43:34 -06:00
liamwhite
6b64557ad6 Merge pull request #8829 from Docteh/qt6_0002
CMake: rework for Qt6 support
2022-11-27 17:56:22 -05:00
liamwhite
a2fde04da9 Merge pull request #9317 from german77/input-crash
yuzu-cmd: Fix input callback crash on close
2022-11-27 15:39:24 -05:00
liamwhite
71f2b4ba8f Merge pull request #9323 from german77/intructions
yuzu-cmd: Update configuration file description
2022-11-27 12:54:01 -05:00
german77
7d8095d944 input_common: Pump sdl events from main thread 2022-11-27 11:09:40 -06:00
german77
a67e776af9 yuzu-cmd: Fix input callback crash on close 2022-11-27 10:53:22 -06:00
bunnei
3ab8d9ac7c Merge pull request #9276 from goldenx86/fsrSlider
FSR Sharpening Slider
2022-11-27 00:13:15 -08:00
Morph
ef6144bf48 Merge pull request #9324 from v1993/master
crypto: use user-provided keys whenever possible
2022-11-26 21:20:05 -05:00
Valeri
278c0e6e8d crypto: use user-provided keys whenever possible
Solves an issue where autogenerated title keys would take precedence over those provided by user.
2022-11-27 05:08:25 +03:00
german77
4a46da6fb5 yuzu-cmd: Update configuration file description 2022-11-26 19:48:22 -06:00
bunnei
eabe45346f Merge pull request #9318 from goldenx86/glsl-ftw
Replace GLSL as the default OpenGL shader backend
2022-11-26 15:57:37 -08:00
bunnei
8f6245be9a core: Use atomic instead of a lock to protect is_paused.
- This allows us to call IsPaused() elsewhere if we are holding the suspend lock.
2022-11-26 13:46:38 -08:00
Matías Locatti
701ca96827 Oops 2022-11-26 17:39:43 -03:00
Matías Locatti
26211ac339 Replace GLSL as the default OpenGL shader backend
GLASM is not very compatible with the latest games, and too many people have the special superpower to break their Vulkan support.
2022-11-26 17:27:04 -03:00
liamwhite
3e53d8138c Merge pull request #9288 from vonchenplus/deferred_draw
video_core: Fine tune maxwell drawing trigger mechanism
2022-11-26 09:35:45 -05:00
liamwhite
ddca512f3f Merge pull request #9307 from Morph1984/not-used-correctly
maxwell_to_vk: Fix format usage bits and add R16_SINT
2022-11-26 09:08:55 -05:00
Matías Locatti
972bd6cb54 Sharpness instead of Sharpening 2022-11-26 02:14:10 -03:00
Matías Locatti
c8d84cb6cb Merge pull request #1 from lat9nq/fsrSlider
configure_graphics: Implement custom setting for FSR Sharpening
2022-11-26 00:51:08 -03:00
lat9nq
19a640286c configure_graphics: Implement custom FSR Sharpening setting 2022-11-25 22:42:54 -05:00
lat9nq
b670c350e4 settings: Reset FSR sharpening global state with the others 2022-11-25 22:42:24 -05:00
liamwhite
e16d1b85f1 Merge pull request #9297 from Kelebek1/sink_oob
[audio_core] Fix an OoB with sample sinking
2022-11-25 12:53:29 -05:00
bunnei
2572b0a5ea Merge pull request #9302 from liamwhite/why-are-we-still-using-ado
externals: always use LibreSSL on Windows
2022-11-25 00:39:16 -08:00
bunnei
e8cbc3b4c5 Merge pull request #9304 from liamwhite/menu-roll
Qt: assign menuRole properties for actions
2022-11-25 00:38:50 -08:00
bunnei
64965cc658 Merge pull request #9305 from lioncash/request
hle_ipc: Add helper function for determining element counts
2022-11-25 00:38:17 -08:00
liamwhite
20b62dbd30 Merge pull request #9194 from FernandoS27/yfc-fermi2d
YFC - Fermi2D: Rework blit engine and add a software blitter.
2022-11-24 21:48:41 -05:00
Morph
9d081a8729 Merge pull request #9312 from FernandoS27/pokemomma
GPU: Fix buffer cache issue, engine upload not inlining memory in multiple lines, etc
2022-11-24 18:24:07 -05:00
Fernando Sahmkow
826e0785bf Fermi2D: Cleanup and address feedback. 2022-11-24 21:00:48 +01:00
Fernando Sahmkow
3b582d5fb2 GPU: Fix buffer cache issue, engine upload not inlining memory in multiline and pessismistic invalidation. 2022-11-24 20:57:16 +01:00
Fernando Sahmkow
7356ab1de6 GPU: Implement additional render target formats. 2022-11-24 20:35:44 +01:00
Fernando Sahmkow
daf2ef8f1c MaxwellDMA: Implement BlockLinear to BlockLinear copies. 2022-11-24 20:35:44 +01:00
Fernando Sahmkow
5fbd6954ef Fermi2D: Implement Bilinear software filtering and address feedback. 2022-11-24 20:35:44 +01:00
Fernando Sahmkow
957840be91 Fermi2D: Rework blit engine and add a software blitter. 2022-11-24 20:35:44 +01:00
Kyle Kienapfel
a75542ad2d CMake: rework for Qt6 support
This PR rearranges things in the CMake system to make compiling with Qt6 possible

1. Camera API has changed in Qt6, so the camera feature is disabled
2. A previous fix involving QLocale is now version gated.
3. QRegExp replaced with QRegularExpression, see #5343
4. Qt6_LOCATION option added to specify a location to search for Qt6
  (see examples below)
5. windeployqt is used to copy Qt6 files into the build directory on Windows

Notes for Arch Linux
Arch install happened to have qt6-base qt6-declarative qt6-translations installed

mkdir build && cd build
cmake .. -GNinja -DYUZU_USE_BUNDLED_VCPKG=ON -DYUZU_TESTS=OFF -DENABLE_QT6=YES -DYUZU_USE_BUNDLED_QT=NO

Windows (MSVC)
Qt wants users to download precompiled libraries via an online installer,
it is worth noting that the GPL/LGPL takes precendence over any ...

In the Qt Maintenance tool, under a version, such as 6.3.1
Select "MSVC 2019 64-bit"
Under Additional Libraries Qt Multimedia may be of use for Camera support

For the Web Applet I had to select the following:
PDF Positioning WebChannel WebEngine

mkdir build && cd build
cmake -G "Visual Studio 16 2019" -DQt6_LOCATION=C:/Qt/6.4.0/msvc2019_64/ \
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=YES -DYUZU_USE_BUNDLED_QT=NO \
-DENABLE_QT_TRANSLATION=YES -DENABLE_QT6=YES ..

Some numbers for reference (msvc2019_64)
Qt5 (slimmed down) 508 MB
Qt5.15.2 all in    929 MB
Qt6.3.1           1.71 GB
Qt6.3.2           1.73 GB
Qt6.4.0-beta3     1.83 GB
Qt6.4.0           1.67 GB
2022-11-24 06:28:42 -08:00
Matías Locatti
f209e976f4 FSR Sharpening Slider part 1 - only a global slider 2022-11-24 04:22:13 -05:00
lat9nq
35319ca3a5 startup_checks: Use fmt::print, fix exec error handling
Uses fmt::print opposed to std::fprintf for error printing.

Call exit instead of returning to caller to prevent a like issue the
previous commit was trying to solve.

Removes unneeded comment.

Co-authored-by: liamwhite <liamwhite@users.noreply.github.com>
Co-authored-by: Lioncash <mathew1800@gmail.com>
2022-11-23 21:59:24 -05:00
lat9nq
3e6c835a64 startup_checks: Use Windows flow for *nix
Spawns a child using fork and exec as opposed to fork alone. Workaround
for the macos file manager complaining about not supporting fork without
exec.

Control flow for *nix is now roughly the same as for Windows.
2022-11-23 21:35:47 -05:00
Morph
852de7a771 maxwell_to_vk: Add R16_SINT
This was somehow missed when the format was added to GL
2022-11-23 21:30:58 -05:00
Morph
ca154d466a maxwell_to_vk: Fix format usage bits
- VK_FORMAT_B8G8R8A8_UNORM supports the STORAGE_IMAGE_BIT
- VK_FORMAT_R4G4B4A4_UNORM_PACK16 does not support the COLOR_ATTACHMENT_BIT
2022-11-23 21:29:43 -05:00
Lioncash
97f273e94e service: Make use of buffer element count helpers 2022-11-23 13:43:20 -05:00
Lioncash
59335f6796 hle_ipc: Add helper functions for getting number of buffer elements 2022-11-23 13:15:19 -05:00
Lioncash
c31f19b6d1 hle_ipc: Mark relevant member functions as [[nodiscard]]
Will allow the compiler to complain about cases where ignoring the
return value would be a bug.
2022-11-23 13:08:52 -05:00
Liam
9abceaed61 Qt: assign menuRole properties for actions 2022-11-23 12:41:56 -05:00
Liam
cdb2e4eaff externals: always use LibreSSL on Windows 2022-11-23 10:24:25 -05:00
liamwhite
168c9ee341 Merge pull request #9299 from lioncash/cast
k_handle_table: Remove cast to void* in GetObjectForIpc
2022-11-22 17:47:53 -05:00
Lioncash
8d99aae45b k_handle_table: Remove cast to void* in GetObjectForIpc
This was used to get around the KProcess class being incomplete. We can
just move this to the cpp file and eliminate the cast entirely, letting
the compiler do its work.
2022-11-22 13:58:42 -05:00
bunnei
f047f376d4 Merge pull request #9219 from german77/nfc_impl
service: nfc: Implement NFC IUser service
2022-11-22 10:28:38 -08:00
Kelebek1
84d4da89a5 Use the maximum input index for samples buffer span size, not just the input count 2022-11-22 15:32:11 +00:00
FengChen
1d57851fc7 video_core: Optimize maxwell drawing trigger mechanism 2022-11-22 17:53:26 +08:00
Narr the Reg
3027917f39 Merge pull request #9292 from Morph1984/amiibo-web-service
qt_amiibo_settings: Use WebClient only if ENABLE_WEB_SERVICE is enabled
2022-11-21 17:24:59 -06:00
Morph
7e0f70e5a1 qt_amiibo_settings: Use WebClient only if ENABLE_WEB_SERVICE is enabled
Resolves compilation errors when ENABLE_WEB_SERVICE is disabled in CMake configuration
2022-11-21 15:22:17 -05:00
Morph
aab68674c0 Merge pull request #9279 from liamwhite/this-would-have-never-happened-in-rust
dmnt:cht: fix copy-paste error
2022-11-20 13:30:35 -05:00
liamwhite
7f1c6def1f Merge pull request #9216 from vonchenplus/reimp_inline_index_buffer
video_core: Reimplement inline index buffer binding
2022-11-20 12:08:08 -05:00
Narr the Reg
db7bcd51ae Merge branch 'master' into nfc_impl 2022-11-20 09:31:20 -06:00
Liam
eb0713f781 dmnt:cht: fix copy-paste error 2022-11-20 10:14:22 -05:00
bunnei
57a05b1653 Merge pull request #9238 from german77/cabinet_applet
service: am: Implement cabinet applet
2022-11-20 00:48:39 -08:00
Morph
69c92b8156 Merge pull request #9249 from goldenx86/available-vram
Add available Vulkan VRAM to log files
2022-11-20 00:21:29 -05:00
Morph
4acbf3a193 Merge pull request #9274 from Morph1984/issue-forms
github: Add new issue form templates
2022-11-20 00:21:24 -05:00
Morph
54d6273975 github: Add blank issue template
This is meant to only be used by developers.
2022-11-20 00:18:53 -05:00
Morph
98a6cd02c8 github: Remove old markdown form
Replaced by the newer issue forms template
2022-11-19 17:41:26 -05:00
Morph
52acdafa95 github: Add new feature request issue form 2022-11-19 17:41:26 -05:00
Morph
975e17aa13 github: Add new bug report issue form 2022-11-19 17:41:26 -05:00
Fernando S
72118935a1 Merge pull request #9271 from merryhime/dynarmic-mwe128-stack-misalignment
dynarmic: Fix stack misalignment in GenMemory128Accessors
2022-11-19 22:12:55 +01:00
bunnei
109c31c90f Merge pull request #9254 from FernandoS27/auto-cpu-fix
Dynarmic: Remove inaccurate NaN from Auto CPU settings.
2022-11-19 12:52:41 -08:00
Merry
344e171cc7 dynarmic: Fix stack misalignment in GenMemory128Accessors 2022-11-19 20:10:26 +00:00
liamwhite
bcbc25eeb3 Merge pull request #9191 from german77/touching_souls
core: hid: Implement true multitouch support
2022-11-19 13:21:01 -05:00
Fernando S
b0365a81c2 Merge pull request #9260 from liamwhite/youre-in-big-trouble-now
spirv_emit_context: add missing flat decoration
2022-11-19 16:40:14 +01:00
Narr the Reg
327d225c3e service: nfc: Implement nfc user 2022-11-19 08:51:59 -06:00
german77
aa075a0c08 service: hid: Only overclock npad controllers 2022-11-19 08:44:42 -06:00
Narr the Reg
38c48cf8d8 core: hid: Implement true multitouch support 2022-11-19 08:44:33 -06:00
bunnei
4975f60162 Merge pull request #9252 from liamwhite/radv-superiority
maxwell3d: HLE multi-layer clear macro
2022-11-19 01:46:48 -08:00
Liam
0d033e6b45 spirv_emit_context: add missing flat decoration 2022-11-18 22:05:28 -05:00
liamwhite
9c67334031 Merge pull request #9253 from vonchenplus/attr_layer
shader: Implement miss attribute layer
2022-11-18 22:04:18 -05:00
bunnei
1fb33bd1e1 Merge pull request #9234 from liamwhite/data-cash-money
kernel: implement data cache management operations
2022-11-18 13:18:36 -08:00
Kyle Kienapfel
d23a35dfbd qt: Add Qt version to LogRuntimes 2022-11-17 19:14:14 -08:00
Kyle Kienapfel
ad3ee5c52b Qt6: Disable IR Sensor when compiling with Qt6
Gating the IR Sensor code behind a macro like so
`#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA`

The YUZU_USE_QT_MULTIMEDIA flag is implemented in later commit

Also the locale fix in src/yuzu/main.cpp is now gated against Qt6,
as it causes compilation error
2022-11-17 19:14:14 -08:00
bunnei
405d685101 Merge pull request #9244 from liamwhite/lost-wakeup
nvnflinger: fix lost wakeup
2022-11-17 17:15:47 -08:00
Morph
e5a446a0df Merge pull request #9229 from Docteh/achy_breaky_heart
Add break for default cases
2022-11-17 19:20:18 -05:00
liamwhite
0e61d711e2 Merge pull request #9228 from HidroSaphire/patch-1
Add break statement in default case
2022-11-17 18:53:59 -05:00
Fernando Sahmkow
bc95753107 Dynarmic: Remove inaccurate NaN from Auto CPU settings. 2022-11-17 16:59:41 +01:00
FengChen
60e0d4a177 shader: Implement miss attribute layer 2022-11-17 22:45:14 +08:00
Liam
4c42655a2d maxwell3d: full HLE for multi-layer clears 2022-11-17 08:31:43 -05:00
Liam
ece0c1095d maxwell3d: HLE multi-layer clear macro 2022-11-16 22:28:58 -05:00
Mai
f426fd95fe Merge pull request #9250 from v1993/patch-10
externals: microprofileui: Remove unused variables
2022-11-16 21:03:59 +00:00
Valeri
fa660190ff externals: microprofileui: Remove unused variables
Allows yuzu to be built with Clang 15
2022-11-16 20:36:43 +03:00
Matías Locatti
7c50a916c7 Update renderer_vulkan.cpp 2022-11-16 05:53:42 -03:00
Mai
48b4eca28a Merge pull request #9247 from lat9nq/verbose-del-warn
configure_profile_manager: Use a custom dialog when deleting a profile
2022-11-16 02:26:20 +00:00
lat9nq
e94bcf03cb configure_profile_manager: Cleanup reference/pointer usage
Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com>
Co-authored-by: Mai M. <mathew1800@gmail.com>
2022-11-15 19:25:09 -05:00
lat9nq
8ca02794c5 configure_profile_manager: Remove profile picture border
The border adds its own width at least on Linux which causes the icon to
be offset by 1px, and cropped by 2px on the bottom and right sides.
2022-11-15 18:11:58 -05:00
lat9nq
ef5184cf1c configure_profile_manager: Use a custom dialog for deletion
A hopefully more informative dialog that most importantly notifies the
user that their saves will be deleted with the user profile.

cpm: Only keep track of UI elements that we need

cpm: Remove unused forward declarations

cpm: Add missing include
2022-11-15 18:11:56 -05:00
bunnei
9e27624a19 Merge pull request #9243 from german77/result
core: Update result module
2022-11-14 20:36:38 -08:00
Feng Chen
cb971ad654 video_core: Reimplement inline index buffer binding 2022-11-15 12:10:44 +08:00
Liam
cf202f3718 nvnflinger: fix lost wakeup 2022-11-14 21:18:52 -05:00
Narr the Reg
18fcc03b3c core: Update result module 2022-11-14 20:08:47 -06:00
Kyle Kienapfel
6fa3faec65 Add break for default cases
Visual Studio has an option to search all files in a solution, so I
did a search in there for "default:" looking for any missing break
statements.

I've left out default statements that return something, and that throw
something, even if via ThrowInvalidType. UNREACHABLE leads towards throw

R_THROW macro leads towards a return
2022-11-13 16:30:55 -08:00
german77
75e6ec85e1 general: Address review comments 2022-11-13 17:13:43 -06:00
german77
a253d1557d service: am: Fix cabinet applet result 2022-11-13 14:25:00 -06:00
german77
9afadca5dc yuzu: Implement cabinet applet frontend 2022-11-13 13:58:19 -06:00
german77
fb57cd26a1 service: am: Implement cabinet applet backend 2022-11-13 11:07:48 -06:00
german77
b193d40d22 input_common: Add amiibo applet functions 2022-11-13 10:56:54 -06:00
german77
6c045c9beb service: nfc: fix tagprotocol and implement GetApplicationAreaId 2022-11-13 10:52:48 -06:00
liamwhite
040a01a5dd Merge pull request #9225 from liamwhite/debugger-instance
Debugger improvements
2022-11-12 21:04:00 -05:00
Morph
8cc5ad8742 Merge pull request #9235 from goldenx86/ignorearm
Ignore ARM for core count
2022-11-12 14:46:17 -05:00
Matías Locatti
540c1696d1 Ignore ARM for core count 2022-11-12 15:31:54 -03:00
Liam
651f6598ac kernel: implement FlushProcessDataCache 2022-11-12 11:27:04 -05:00
Liam
70ea1c2000 common: add cache management functions 2022-11-12 11:26:56 -05:00
bunnei
08091ff3e3 Merge pull request #9226 from Kelebek1/regs_regression
[video_core] Fix a couple regs regressions
2022-11-12 02:27:06 -08:00
bunnei
b51c1544b9 Merge pull request #9224 from liamwhite/services-arent-processes
service_thread: remove explicit KProcess
2022-11-11 22:37:04 -08:00
Mai
7dfe35eca6 Merge pull request #9231 from goldenx86/corecount
Add CPU core count to log files
2022-11-12 03:19:26 +00:00
Matías Locatti
69768ec71e Add CPU core count to log files 2022-11-11 23:50:48 -03:00
liamwhite
e4d55e4ee4 Merge pull request #9204 from vonchenplus/dma_copy_1d_random_crash
video_core: Fix dma copy 1D random crash
2022-11-11 17:56:41 -05:00
liamwhite
c4bc7ce7e2 Merge pull request #9133 from FearlessTobi/compat-improvements
yuzu/compatdb: Improve compatibility submission system
2022-11-11 16:15:36 -05:00
Tobias
211da31b34 yuzu/main: Change to 8_GiB instead of magic number
Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com>
2022-11-11 19:15:52 +01:00
liamwhite
c973029374 Merge pull request #9167 from vonchenplus/tess
video_core: Fix few issues in Tess stage
2022-11-11 08:03:40 -05:00
Enrico Mancuso
b832942b6e Add break statement in default case
According to the contributing page (https://github.com/yuzu-emu/yuzu/wiki/Contributing) the default cases should have a break statement
2022-11-11 10:16:58 +01:00
bunnei
5eb30c7827 Merge pull request #9223 from goldenx86/threadcount
Add CPU thread count to log files
2022-11-10 23:12:39 -08:00
Kelebek1
33ea0fdfe8 Fix regs regression with OpenGL two-sided stencil, and re-add data invalidation reg 2022-11-11 04:04:36 +00:00
Morph
c9bb888adf ir/texture_pass: Use host_info instead of querying Settings::values (#9176) 2022-11-11 03:32:53 +01:00
bunnei
d05b183f21 Merge pull request #9198 from liamwhite/arm64
Initial ARM64 support
2022-11-10 17:11:27 -08:00
Mai
83eb9cf7da Merge pull request #9180 from Docteh/remove_stuff
UI: split up strings relating to content removal
2022-11-11 00:42:40 +00:00
Mai
0e84fd95e2 Merge pull request #9217 from HidroSaphire/patch-1
Add break statement in default cases
2022-11-11 00:42:04 +00:00
Liam
18123ff958 gdbstub: add ams monitor commands 2022-11-10 19:20:57 -05:00
Liam
ceb829cc33 debugger: allow more than one connection attempt per session 2022-11-10 17:39:04 -05:00
bunnei
bb55d2e701 Merge pull request #9192 from german77/i_had_to_copy_each_one_again
yuzu: Change QtKeyToSwitchKey switch case to array
2022-11-10 13:44:41 -08:00
Matías Locatti
0c176ce828 Me likes
Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com>
2022-11-10 18:36:39 -03:00
Liam
b34d3d5882 service_thread: remove explicit KProcess 2022-11-10 16:14:03 -05:00
FearlessTobi
26a1d4fc37 yuzu/compatdb: Rework compatibility submission system
Co-Authored-By: Narr the Reg <5944268+german77@users.noreply.github.com>
2022-11-10 21:36:22 +01:00
bunnei
ea41c53ab1 Merge pull request #9183 from liamwhite/svc-refresh
kernel/svc_types: refresh
2022-11-10 11:03:54 -08:00
Matías Locatti
766941f1a3 Add CPU thread count to log files 2022-11-10 15:46:08 -03:00
FengChen
d03afd6f4b video_core: Fix dma copy 1D random crash 2022-11-11 00:23:45 +08:00
Liam
4eece4d35d kernel/svc_types: refresh 2022-11-09 19:05:08 -05:00
bunnei
770f23db34 Merge pull request #9182 from liamwhite/services-are-processes
kernel: assign KProcess to service threads
2022-11-09 15:52:23 -08:00
Liam
cbaf642ffe Initial ARM64 support 2022-11-09 16:58:49 -05:00
Enrico Mancuso
d581a4a367 Add break statement in default cases
According to the contributing page (https://github.com/yuzu-emu/yuzu/wiki/Contributing) the default cases should have a break statement

default:
        // Yes, even break for the last case
        break;
2022-11-09 15:34:26 +01:00
Fernando S
3161b34ff6 Merge pull request #9215 from liamwhite/swordfight
Ensure correctness of atomic store ordering
2022-11-09 14:50:49 +01:00
Liam
71c0e20f95 Ensure correctness of atomic store ordering 2022-11-09 08:09:50 -05:00
Mai
ad9e5bc5b7 Merge pull request #9199 from liamwhite/service-oops
service_thread: fix deletion
2022-11-09 03:25:53 +00:00
FengChen
a4472b5526 video_core: Fix few issues in Tess stage 2022-11-07 15:42:42 +08:00
Liam
6a0d8b2aa1 service_thread: fix deletion 2022-11-06 19:50:51 -05:00
Fernando S
dc520a487d Merge pull request #9195 from vonchenplus/vmm_kinds_error
video_core:Fix vmm kinds size error
2022-11-06 15:34:24 +01:00
FengChen
aa97f39ba8 video_core:Fix vmm kinds size error 2022-11-06 22:31:22 +08:00
Fernando S
df38c03a09 Merge pull request #9163 from vonchenplus/draw_error
video_core: Fix drawing trigger mechanism regression
2022-11-06 01:13:59 +01:00
liamwhite
4c198bbf06 Merge pull request #9173 from bunnei/kern-update-15
Kernel: Various updates for FW 15.0.x
2022-11-05 13:25:29 -04:00
Liam
cf0f821565 core: hle: kernel: Address review comments. 2022-11-05 12:23:47 -04:00
Kyle Kienapfel
a5d8703235 UI: split up strings relating to content removal
Requested by Italian translator (Fs00 in Discord)

"Remove Installed Game %1?"
"Error Removing %1"

I didn't press for translated strings, so have a taste direct from deepl

Rimuovere il contenuto del gioco installato?
Rimuovere l'aggiornamento del gioco installato?
Rimuovere il DLC del gioco installato?
2022-11-04 20:25:38 -07:00
Liam
e6fe40428c service_thread: register service threads to the logical owner process 2022-11-04 09:18:57 -04:00
Liam
85527cc7c7 kernel: avoid racy behavior in global suspension 2022-11-04 09:18:57 -04:00
bunnei
05ae0cab0e core: hle: kernel: k_page_table: Remove unnecessary casts. 2022-11-03 21:17:08 -07:00
bunnei
119315af08 core: hle: kernel: k_page_table: Manually open/close pages for IPC methods. 2022-11-03 21:17:08 -07:00
bunnei
661fe06d9d core: hle: kernel: k_page_table: Implement IPC memory methods. 2022-11-03 21:17:07 -07:00
bunnei
ba21ba0c5c core: hle: kernel: k_memory_manager: Refresh. 2022-11-03 21:17:07 -07:00
bunnei
32d7faafa8 core: hle: kernel: Integrate system KSystemResource. 2022-11-03 21:17:07 -07:00
bunnei
b7b47f3099 core: hle: kernel: k_dynamic_page_manager: Refresh. 2022-11-03 21:17:07 -07:00
bunnei
6f941121e6 core: hle: kernel: Add KSystemResource. 2022-11-03 21:17:07 -07:00
bunnei
6636b81573 core: hle: kernel: k_handle_table: Refresh. 2022-11-03 21:17:07 -07:00
bunnei
1f21fa866d core: hle: kernel: k_memory_layout: Refresh. 2022-11-03 21:17:07 -07:00
bunnei
84d130f143 core: hle: kernel: k_memory_region_type: Refresh. 2022-11-03 21:17:07 -07:00
bunnei
d928ba8e40 core: hle: kernel: slab_helpers: Add KAutoObjectWithSlabHeap. 2022-11-03 21:17:06 -07:00
bunnei
3aab7d4473 core: hle: kernel: k_dynamic_resource_manager: Add KBlockInfoManager, KBlockInfoSlabHeap. 2022-11-03 21:17:06 -07:00
bunnei
6b6c02f541 core: hle: kernel: k_page_bitmap: Refresh. 2022-11-03 21:17:06 -07:00
bunnei
50bfacca88 core: hle: kernel: k_memory_block: Refresh. 2022-11-03 21:17:06 -07:00
bunnei
0cb9bc12fc core: hle: kernel: k_page_heap: Refresh. 2022-11-03 21:17:06 -07:00
bunnei
6257461684 core: hle: kernel: k_page_group: Add KPageBufferSlabHeap. 2022-11-03 21:17:06 -07:00
bunnei
d353c45f7d core: hle: kernel: k_system_control: Add SecureAppletMemorySize. 2022-11-03 21:17:06 -07:00
bunnei
f76b4417e6 core: hle: kernel: k_page_buffer: Add KPageBufferSlabHeap. 2022-11-03 21:17:06 -07:00
bunnei
0897f4f96c core: hle: kernel: Add KPageTableManager. 2022-11-03 21:17:06 -07:00
bunnei
6d4f411c08 core: hle: kernel: Add KPageTableSlabHeap. 2022-11-03 21:17:06 -07:00
bunnei
37b17252d1 core: hle: kernel: Add KEventInfo. 2022-11-03 21:17:06 -07:00
bunnei
ddd3f48736 core: hle: kernel: Add KDebug. 2022-11-03 21:17:06 -07:00
bunnei
46322be735 core: hle: result: Fix code for compilers. 2022-11-03 21:17:06 -07:00
FengChen
b42b894785 video_core: Fix drawing trigger mechanism regression 2022-10-31 21:57:38 +08:00
270 changed files with 10110 additions and 2208 deletions

View File

@@ -0,0 +1,10 @@
name: New Issue (Developers Only)
description: A blank issue template for developers only. If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.
body:
- type: markdown
attributes:
value: |
**If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.**
- type: textarea
attributes:
label: "Issue"

View File

@@ -1,39 +0,0 @@
---
name: Bug Report / Feature Request
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
title: ''
labels: ''
assignees: ''
---
<!---
Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
Please read the FAQ:
https://yuzu-emu.org/wiki/faq/
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
https://community.citra-emu.org/
If the FAQ does not answer your question, please go to:
https://community.citra-emu.org/
When submitting an issue, please check the following:
- You have read the above.
- You have provided the version (commit hash) of yuzu you are using.
- You have provided sufficient detail for the issue to be reproduced.
- You have provided system specs (if relevant).
- Please also provide:
- For any issues, a log file
- For crashes, a backtrace.
- For graphical issues, comparison screenshots with real hardware.
- For emulation inaccuracies, a test-case (if able).
-->

64
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Bug Report
description: File a bug report
body:
- type: markdown
attributes:
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues
required: true
- type: input
attributes:
label: Affected Build(s)
description: List the affected build(s) that this issue applies to.
placeholder: Mainline 1234 / Early Access 1234
validations:
required: true
- type: textarea
id: issue-desc
attributes:
label: Description of Issue
description: A brief description of the issue encountered along with any images and/or videos.
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected Behavior
description: A brief description of how it is expected to work along with any images and/or videos.
validations:
required: true
- type: textarea
id: reproduction-steps
attributes:
label: Reproduction Steps
description: A brief explanation of how to reproduce this issue. If possible, provide a save file to aid in reproducing the issue.
validations:
required: true
- type: textarea
id: log
attributes:
label: Log File
description: A log file will help our developers to better diagnose and fix the issue.
validations:
required: true
- type: textarea
id: system-config
attributes:
label: System Configuration
placeholder: |
CPU: Intel i5-10400 / AMD Ryzen 5 3600
GPU/Driver: NVIDIA GeForce GTX 1060 (Driver 512.95)
RAM: 16GB DDR4-3200
OS: Windows 11 22H2 (Build 22621.819)
value: |
CPU:
GPU/Driver:
RAM:
OS:
validations:
required: true

View File

@@ -0,0 +1,28 @@
name: Feature Request
description: File a feature request
labels: "request"
body:
- type: markdown
attributes:
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make yuzu better.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the feature you are requesting.
options:
- label: I have searched the existing issues
required: true
- type: textarea
id: what-feature
attributes:
label: What feature are you suggesting?
description: A brief description of the requested feature.
validations:
required: true
- type: textarea
id: why-feature
attributes:
label: Why would this feature be useful?
description: A brief description of why this feature would make yuzu better.
validations:
required: true

View File

@@ -132,10 +132,6 @@ Files: vcpkg.json
Copyright: 2022 yuzu Emulator Project
License: GPL-3.0-or-later
Files: .github/ISSUE_TEMPLATE/config.yml
Copyright: 2020 tgsm <doodrabbit@hotmail.com>
License: GPL-2.0-or-later
Files: .github/ISSUE_TEMPLATE/bug-report-feature-request.md
Copyright: 2016 MerryMage
Files: .github/ISSUE_TEMPLATE/*
Copyright: 2022 yuzu Emulator Project
License: GPL-2.0-or-later

View File

@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.22)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
@@ -19,6 +19,9 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF)
option(ENABLE_QT "Enable the Qt frontend" ON)
option(ENABLE_QT6 "Allow usage of Qt6 to be attempted" OFF)
set(QT6_LOCATION "" CACHE PATH "Additional Location to search for Qt6 libraries like C:/Qt/6.3.1/msvc2019_64/")
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
@@ -28,6 +31,8 @@ option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
@@ -133,13 +138,13 @@ if (NOT ENABLE_GENERIC)
if (MSVC)
detect_architecture("_M_AMD64" x86_64)
detect_architecture("_M_IX86" x86)
detect_architecture("_M_ARM" ARM)
detect_architecture("_M_ARM64" ARM64)
detect_architecture("_M_ARM" arm)
detect_architecture("_M_ARM64" arm64)
else()
detect_architecture("__x86_64__" x86_64)
detect_architecture("__i386__" x86)
detect_architecture("__arm__" ARM)
detect_architecture("__aarch64__" ARM64)
detect_architecture("__arm__" arm)
detect_architecture("__aarch64__" arm64)
endif()
endif()
@@ -213,128 +218,166 @@ if (MINGW)
find_library(MSWSOCK_LIBRARY mswsock REQUIRED)
endif()
# Please consider this as a stub
if(ENABLE_QT6 AND Qt6_LOCATION)
list(APPEND CMAKE_PREFIX_PATH "${Qt6_LOCATION}")
endif()
function(set_yuzu_qt_components)
# Best practice is to ask for all components at once, so they are from the same version
set(YUZU_QT_COMPONENTS2 Core Widgets Concurrent)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
list(APPEND YUZU_QT_COMPONENTS2 DBus)
endif()
if (YUZU_USE_QT_MULTIMEDIA)
list(APPEND YUZU_QT_COMPONENTS2 Multimedia)
endif()
if (YUZU_USE_QT_WEB_ENGINE)
list(APPEND YUZU_QT_COMPONENTS2 WebEngineCore WebEngineWidgets)
endif()
if (ENABLE_QT_TRANSLATION)
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
endif()
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
endfunction(set_yuzu_qt_components)
# Qt5 requires that we find components, so it doesn't fit our pretty little find package function
if(ENABLE_QT)
set(QT_VERSION 5.15)
# These are used to specify minimum versions
set(QT5_VERSION 5.15)
set(QT6_VERSION 6.3.1)
# Check for system Qt on Linux, fallback to bundled Qt
if (UNIX AND NOT APPLE)
if (NOT YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia)
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT))
# Check for dependencies, then enable bundled Qt download
# Check that the system GLIBCXX version is compatible
find_program(OBJDUMP objdump)
if ("${OBJDUMP}" STREQUAL "OBJDUMP-NOTFOUND")
message(FATAL_ERROR "Required program `objdump` not found.")
endif()
find_library(LIBSTDCXX libstdc++.so.6)
execute_process(
COMMAND
${OBJDUMP} -T ${LIBSTDCXX}
COMMAND
grep GLIBCXX_3.4.28
COMMAND
sed "s/[0-9a-f]*.* //"
COMMAND
sed "s/ .*//"
COMMAND
sort -u
OUTPUT_VARIABLE
GLIBCXX_MET
)
if (NOT GLIBCXX_MET)
message(FATAL_ERROR "Qt too old or not found, and bundled Qt package is not \
compatible with this system. Either install Qt ${QT_VERSION}, or provide the path \
to Qt by setting the variable Qt5_ROOT.")
endif()
# Check for headers
find_package(PkgConfig REQUIRED)
pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0)
if (NOT QT_DEP_GLU_FOUND)
message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \
Perhaps `libglu1-mesa-dev` needs to be installed?")
endif()
pkg_check_modules(QT_DEP_MESA QUIET dri>=20.0.8)
if (NOT QT_DEP_MESA_FOUND)
message(FATAL_ERROR "Qt bundled pacakge dependency `dri` not found. \
Perhaps `mesa-common-dev` needs to be installed?")
endif()
# Check for X libraries
set(BUNDLED_QT_REQUIREMENTS
libxcb-icccm.so.4
libxcb-image.so.0
libxcb-keysyms.so.1
libxcb-randr.so.0
libxcb-render-util.so.0
libxcb-render.so.0
libxcb-shape.so.0
libxcb-shm.so.0
libxcb-sync.so.1
libxcb-xfixes.so.0
libxcb-xinerama.so.0
libxcb-xkb.so.1
libxcb.so.1
libxkbcommon-x11.so.0
libxkbcommon.so.0
)
set(UNRESOLVED_QT_DEPS "")
foreach (REQUIREMENT ${BUNDLED_QT_REQUIREMENTS})
find_library(BUNDLED_QT_${REQUIREMENT} ${REQUIREMENT})
if ("${BUNDLED_QT_${REQUIREMENT}}" STREQUAL "BUNDLED_QT_${REQUIREMENT}-NOTFOUND")
set(UNRESOLVED_QT_DEPS ${UNRESOLVED_QT_DEPS} ${REQUIREMENT})
endif()
unset(BUNDLED_QT_${REQUIREMENT})
endforeach()
unset(BUNDLED_QT_REQUIREMENTS)
if (NOT "${UNRESOLVED_QT_DEPS}" STREQUAL "")
message(FATAL_ERROR "Bundled Qt package missing required dependencies: ${UNRESOLVED_QT_DEPS}")
endif()
set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE)
endif()
if (YUZU_USE_BUNDLED_QT)
# Binary package currently does not support Qt webengine, so make sure it's disabled
set(YUZU_USE_QT_WEB_ENGINE OFF CACHE BOOL "Use Qt Webengine" FORCE)
endif()
set_yuzu_qt_components()
if (ENABLE_QT6)
find_package(Qt6 ${QT6_VERSION} COMPONENTS ${YUZU_QT_COMPONENTS})
endif()
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH)
if(YUZU_USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
set(QT_BUILD qt-5.15.2-msvc2019_64)
elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND NOT MINGW AND ARCHITECTURE_x86_64)
set(QT_BUILD qt5_5_15_2)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
endif()
if (DEFINED QT_BUILD)
download_bundled_external("qt/" ${QT_BUILD} QT_PREFIX)
endif()
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
endif()
if (UNIX AND NOT APPLE AND YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
if (Qt6_FOUND)
message(STATUS "yuzu/CMakeLists.txt: Qt6Widgets_VERSION ${Qt6Widgets_VERSION}, setting QT_VERSION")
set(QT_VERSION ${Qt6Widgets_VERSION})
set(QT_MAJOR_VERSION 6)
# Qt6 sets cxx_std_17 and we need to undo that
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")
else()
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
endif()
if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
message(STATUS "yuzu/CMakeLists.txt: Qt6 not found/not selected, trying for Qt5")
# When Qt6 partially found, need this set to use Qt5 when not specifying version
set(QT_DEFAULT_MAJOR_VERSION 5)
set(QT_MAJOR_VERSION 5)
set(YUZU_USE_QT_MULTIMEDIA ON)
# Check for system Qt on Linux, fallback to bundled Qt
if (UNIX AND NOT APPLE)
if (NOT YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT5_VERSION} COMPONENTS Widgets DBus Multimedia)
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT))
# Check for dependencies, then enable bundled Qt download
# Check that the system GLIBCXX version is compatible
find_program(OBJDUMP objdump)
if (NOT OBJDUMP)
message(FATAL_ERROR "Required program `objdump` not found.")
endif()
find_library(LIBSTDCXX libstdc++.so.6)
execute_process(
COMMAND
${OBJDUMP} -T ${LIBSTDCXX}
COMMAND
grep GLIBCXX_3.4.28
COMMAND
sed "s/[0-9a-f]*.* //"
COMMAND
sed "s/ .*//"
COMMAND
sort -u
OUTPUT_VARIABLE
GLIBCXX_MET
)
if (NOT GLIBCXX_MET)
message(FATAL_ERROR "Qt too old or not found, and bundled Qt package is not \
compatible with this system. Either install Qt ${QT_VERSION}, or provide the path \
to Qt by setting the variable Qt5_ROOT.")
endif()
# Check for headers
find_package(PkgConfig REQUIRED)
pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0)
if (NOT QT_DEP_GLU_FOUND)
message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \
Perhaps `libglu1-mesa-dev` needs to be installed?")
endif()
pkg_check_modules(QT_DEP_MESA QUIET dri>=20.0.8)
if (NOT QT_DEP_MESA_FOUND)
message(FATAL_ERROR "Qt bundled pacakge dependency `dri` not found. \
Perhaps `mesa-common-dev` needs to be installed?")
endif()
# Check for X libraries
set(BUNDLED_QT_REQUIREMENTS
libxcb-icccm.so.4
libxcb-image.so.0
libxcb-keysyms.so.1
libxcb-randr.so.0
libxcb-render-util.so.0
libxcb-render.so.0
libxcb-shape.so.0
libxcb-shm.so.0
libxcb-sync.so.1
libxcb-xfixes.so.0
libxcb-xinerama.so.0
libxcb-xkb.so.1
libxcb.so.1
libxkbcommon-x11.so.0
libxkbcommon.so.0
)
set(UNRESOLVED_QT_DEPS "")
foreach (REQUIREMENT ${BUNDLED_QT_REQUIREMENTS})
find_library(BUNDLED_QT_${REQUIREMENT} ${REQUIREMENT})
if (NOT BUNDLED_QT_${REQUIREMENT})
set(UNRESOLVED_QT_DEPS ${UNRESOLVED_QT_DEPS} ${REQUIREMENT})
endif()
unset(BUNDLED_QT_${REQUIREMENT})
endforeach()
unset(BUNDLED_QT_REQUIREMENTS)
if (NOT "${UNRESOLVED_QT_DEPS}" STREQUAL "")
message(FATAL_ERROR "Bundled Qt package missing required dependencies: ${UNRESOLVED_QT_DEPS}")
endif()
set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE)
endif()
if (YUZU_USE_BUNDLED_QT)
# Binary package currently does not support Qt webengine, so make sure it's disabled
set(YUZU_USE_QT_WEB_ENGINE OFF CACHE BOOL "Use Qt Webengine" FORCE)
endif()
endif()
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH)
if(YUZU_USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
set(QT_BUILD qt-5.15.2-msvc2019_64)
elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND NOT MINGW AND ARCHITECTURE_x86_64)
set(QT_BUILD qt5_5_15_2)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
endif()
if (DEFINED QT_BUILD)
download_bundled_external("qt/" ${QT_BUILD} QT_PREFIX)
endif()
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
# Binary package for Qt5 has Qt Multimedia
set(YUZU_USE_QT_MULTIMEDIA ON CACHE BOOL "Use Qt Multimedia" FORCE)
endif()
set_yuzu_qt_components()
find_package(Qt5 ${QT5_VERSION} COMPONENTS ${YUZU_QT_COMPONENTS} ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
endif()
if (ENABLE_QT_TRANSLATION)
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
endif()
endif()
# find SDL2 exports a bunch of variables that are needed, so its easier to do this outside of the yuzu_find_package

View File

@@ -27,10 +27,13 @@ function(copy_yuzu_Qt5_deps target_dir)
Qt5Core$<$<CONFIG:Debug>:d>.*
Qt5Gui$<$<CONFIG:Debug>:d>.*
Qt5Widgets$<$<CONFIG:Debug>:d>.*
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
Qt5Network$<$<CONFIG:Debug>:d>.*
)
if (YUZU_USE_QT_MULTIMEDIA)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
)
endif()
if (YUZU_USE_QT_WEB_ENGINE)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
Qt5Network$<$<CONFIG:Debug>:d>.*

View File

@@ -7,15 +7,14 @@ include(DownloadExternals)
# xbyak
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
add_library(xbyak INTERFACE)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
add_subdirectory(xbyak)
endif()
# Dynarmic
if (ARCHITECTURE_x86_64)
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
if (ARCHITECTURE_arm64)
set(DYNARMIC_FRONTENDS "A32")
endif()
set(DYNARMIC_NO_BUNDLED_FMT ON)
set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE)
add_subdirectory(dynarmic)
@@ -70,7 +69,6 @@ if (YUZU_USE_EXTERNAL_SDL2)
set(SDL_SHARED OFF)
add_subdirectory(SDL EXCLUDE_FROM_ALL)
add_library(SDL2 ALIAS SDL2-static)
endif()
# ENet
@@ -93,10 +91,14 @@ endif()
add_subdirectory(sirit)
if (ENABLE_WEB_SERVICE)
find_package(OpenSSL 1.1)
if (OPENSSL_FOUND)
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
else()
if (NOT WIN32)
find_package(OpenSSL 1.1)
if (OPENSSL_FOUND)
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
endif()
endif()
if (WIN32 OR NOT OPENSSL_FOUND)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
set(OPENSSLDIR "/etc/ssl/")

View File

@@ -845,8 +845,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
MicroProfile& S = *MicroProfileGet();
MP_DEBUG_DUMP_RANGE();
int nY = nBaseY - UI.nOffsetY;
int64_t nNumBoxes = 0;
int64_t nNumLines = 0;
uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;
MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent];
@@ -1149,7 +1147,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
}
}
#endif
++nNumBoxes;
}
else
{
@@ -1165,7 +1162,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
}
nLinesDrawn[nStackPos] = nLineX;
MicroProfileDrawLineVertical(nLineX, fYStart + 0.5f, fYEnd + 0.5f, nColor|UI.nOpacityForeground);
++nNumLines;
}
}
nStackPos--;

View File

@@ -217,7 +217,7 @@ else()
endif()
target_link_libraries(audio_core PUBLIC common core)
if (ARCHITECTURE_x86_64)
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_link_libraries(audio_core PRIVATE dynarmic)
endif()
@@ -226,6 +226,10 @@ if(ENABLE_CUBEB)
target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
endif()
if(ENABLE_SDL2)
target_link_libraries(audio_core PRIVATE SDL2)
if (YUZU_USE_EXTERNAL_SDL2)
target_link_libraries(audio_core PRIVATE SDL2-static)
else()
target_link_libraries(audio_core PRIVATE SDL2)
endif()
target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
endif()

View File

@@ -460,21 +460,23 @@ void CommandBuffer::GenerateDeviceSinkCommand(const s32 node_id, const s16 buffe
cmd.session_id = session_id;
cmd.input_count = parameter.input_count;
s16 max_input{0};
for (u32 i = 0; i < parameter.input_count; i++) {
cmd.inputs[i] = buffer_offset + parameter.inputs[i];
max_input = std::max(max_input, cmd.inputs[i]);
}
if (state.upsampler_info != nullptr) {
const auto size_{state.upsampler_info->sample_count * parameter.input_count};
const auto size_bytes{size_ * sizeof(s32)};
const auto addr{memory_pool->Translate(state.upsampler_info->samples_pos, size_bytes)};
cmd.sample_buffer = {reinterpret_cast<s32*>(addr),
parameter.input_count * state.upsampler_info->sample_count};
(max_input + 1) * state.upsampler_info->sample_count};
} else {
cmd.sample_buffer = samples_buffer;
}
cmd.input_count = parameter.input_count;
for (u32 i = 0; i < parameter.input_count; i++) {
cmd.inputs[i] = buffer_offset + parameter.inputs[i];
}
GenerateEnd<DeviceSinkCommand>(cmd);
}

View File

@@ -26,6 +26,7 @@ void PerformanceManager::CreateImpl(const size_t version) {
impl = std::make_unique<
PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1,
PerformanceEntryVersion1, PerformanceDetailVersion1>>();
break;
}
}

View File

@@ -266,19 +266,20 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
}
void SinkStream::Stall() {
if (stalled) {
std::scoped_lock lk{stall_guard};
if (stalled_lock) {
return;
}
stalled = true;
system.StallProcesses();
stalled_lock = system.StallProcesses();
}
void SinkStream::Unstall() {
if (!stalled) {
std::scoped_lock lk{stall_guard};
if (!stalled_lock) {
return;
}
system.UnstallProcesses();
stalled = false;
stalled_lock.unlock();
}
} // namespace AudioCore::Sink

View File

@@ -6,6 +6,7 @@
#include <array>
#include <atomic>
#include <memory>
#include <mutex>
#include <span>
#include <vector>
@@ -240,8 +241,8 @@ private:
f32 system_volume{1.0f};
/// Set via IAudioDevice service calls
f32 device_volume{1.0f};
/// True if coretiming has been stalled
bool stalled{false};
std::mutex stall_guard;
std::unique_lock<std::mutex> stalled_lock;
};
using SinkStreamPtr = std::unique_ptr<SinkStream>;

View File

@@ -34,6 +34,8 @@ add_library(common STATIC
bit_util.h
cityhash.cpp
cityhash.h
cache_management.cpp
cache_management.h
common_funcs.h
common_types.h
concepts.h

View File

@@ -156,6 +156,7 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
break;
default:
assert(false);
break;
}
}

View File

@@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdint>
#include <cstring>
#include "common/cache_management.h"
namespace Common {
#if defined(ARCHITECTURE_x86_64)
// Most cache operations are no-ops on x86
void DataCacheLineCleanByVAToPoU(void* start, size_t size) {}
void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size) {}
void DataCacheLineCleanByVAToPoC(void* start, size_t size) {}
void DataCacheZeroByVA(void* start, size_t size) {
std::memset(start, 0, size);
}
#elif defined(ARCHITECTURE_arm64)
// BS/DminLine is log2(cache size in words), we want size in bytes
#define EXTRACT_DMINLINE(ctr_el0) (1 << ((((ctr_el0) >> 16) & 0xf) + 2))
#define EXTRACT_BS(dczid_el0) (1 << (((dczid_el0)&0xf) + 2))
#define DEFINE_DC_OP(op_name, function_name) \
void function_name(void* start, size_t size) { \
size_t ctr_el0; \
asm volatile("mrs %[ctr_el0], ctr_el0\n\t" : [ctr_el0] "=r"(ctr_el0)); \
size_t cacheline_size = EXTRACT_DMINLINE(ctr_el0); \
uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
uintptr_t va_end = va_start + size; \
for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
} \
}
#define DEFINE_DC_OP_DCZID(op_name, function_name) \
void function_name(void* start, size_t size) { \
size_t dczid_el0; \
asm volatile("mrs %[dczid_el0], dczid_el0\n\t" : [dczid_el0] "=r"(dczid_el0)); \
size_t cacheline_size = EXTRACT_BS(dczid_el0); \
uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
uintptr_t va_end = va_start + size; \
for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
} \
}
DEFINE_DC_OP(cvau, DataCacheLineCleanByVAToPoU);
DEFINE_DC_OP(civac, DataCacheLineCleanAndInvalidateByVAToPoC);
DEFINE_DC_OP(cvac, DataCacheLineCleanByVAToPoC);
DEFINE_DC_OP_DCZID(zva, DataCacheZeroByVA);
#endif
} // namespace Common

View File

@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstddef>
namespace Common {
// Data cache instructions enabled at EL0 by SCTLR_EL1.UCI.
// VA = virtual address
// PoC = point of coherency
// PoU = point of unification
// dc cvau
void DataCacheLineCleanByVAToPoU(void* start, size_t size);
// dc civac
void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size);
// dc cvac
void DataCacheLineCleanByVAToPoC(void* start, size_t size);
// dc zva
void DataCacheZeroByVA(void* start, size_t size);
} // namespace Common

View File

@@ -31,8 +31,10 @@
#ifndef _MSC_VER
#ifdef ARCHITECTURE_x86_64
#if defined(ARCHITECTURE_x86_64)
#define Crash() __asm__ __volatile__("int $3")
#elif defined(ARCHITECTURE_arm64)
#define Crash() __asm__ __volatile__("brk #0")
#else
#define Crash() exit(1)
#endif

View File

@@ -359,6 +359,12 @@ public:
}
});
long page_size = sysconf(_SC_PAGESIZE);
if (page_size != 0x1000) {
LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size);
throw std::bad_alloc{};
}
// Backing memory initialization
#if defined(__FreeBSD__) && __FreeBSD__ < 13
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30

View File

@@ -383,6 +383,16 @@ void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDevic
}
}
inline void RegisterInputFactory(const std::string& name,
std::shared_ptr<Factory<InputDevice>> factory) {
RegisterFactory<InputDevice>(name, std::move(factory));
}
inline void RegisterOutputFactory(const std::string& name,
std::shared_ptr<Factory<OutputDevice>> factory) {
RegisterFactory<OutputDevice>(name, std::move(factory));
}
/**
* Unregisters an input device factory.
* @tparam InputDeviceType the type of input devices the factory can create
@@ -395,6 +405,14 @@ void UnregisterFactory(const std::string& name) {
}
}
inline void UnregisterInputFactory(const std::string& name) {
UnregisterFactory<InputDevice>(name);
}
inline void UnregisterOutputFactory(const std::string& name) {
UnregisterFactory<OutputDevice>(name);
}
/**
* Create an input device from given paramters.
* @tparam InputDeviceType the type of input devices to create
@@ -416,13 +434,21 @@ std::unique_ptr<InputDeviceType> CreateDeviceFromString(const std::string& param
return pair->second->Create(package);
}
inline std::unique_ptr<InputDevice> CreateInputDeviceFromString(const std::string& params) {
return CreateDeviceFromString<InputDevice>(params);
}
inline std::unique_ptr<OutputDevice> CreateOutputDeviceFromString(const std::string& params) {
return CreateDeviceFromString<OutputDevice>(params);
}
/**
* Create an input device from given paramters.
* Create an input device from given parameters.
* @tparam InputDeviceType the type of input devices to create
* @param A ParamPackage that contains all parameters for creating the device
* @param package A ParamPackage that contains all parameters for creating the device
*/
template <typename InputDeviceType>
std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package) {
std::unique_ptr<InputDeviceType> CreateDevice(const ParamPackage& package) {
const std::string engine = package.Get("engine", "null");
const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
const auto pair = factory_list.find(engine);
@@ -435,4 +461,12 @@ std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package
return pair->second->Create(package);
}
inline std::unique_ptr<InputDevice> CreateInputDevice(const ParamPackage& package) {
return CreateDevice<InputDevice>(package);
}
inline std::unique_ptr<OutputDevice> CreateOutputDevice(const ParamPackage& package) {
return CreateDevice<OutputDevice>(package);
}
} // namespace Common::Input

View File

@@ -48,6 +48,7 @@ void LogSettings() {
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue());
log_setting("Renderer_FSRSlider", values.fsr_sharpening_slider.GetValue());
log_setting("Renderer_AntiAliasing", values.anti_aliasing.GetValue());
log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
@@ -151,6 +152,7 @@ void UpdateRescalingInfo() {
ASSERT(false);
info.up_scale = 1;
info.down_shift = 0;
break;
}
info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift);
info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale;
@@ -180,6 +182,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true);
// Renderer
values.fsr_sharpening_slider.SetGlobal(true);
values.renderer_backend.SetGlobal(true);
values.vulkan_device.SetGlobal(true);
values.aspect_ratio.SetGlobal(true);

View File

@@ -421,6 +421,7 @@ struct Values {
ResolutionScalingInfo resolution_info{};
SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"};
SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"};
SwitchableSetting<int, true> fsr_sharpening_slider{25, 0, 200, "fsr_sharpening_slider"};
SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
// *nix platforms may have issues with the borderless windowed fullscreen mode.
// Default to exclusive fullscreen on these platforms for now.
@@ -442,7 +443,7 @@ struct Values {
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
ShaderBackend::SPIRV, "shader_backend"};
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};

View File

@@ -4,14 +4,27 @@
#include <array>
#include <cstring>
#include <fstream>
#include <iterator>
#include <optional>
#include <string_view>
#include <thread>
#include <vector>
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/x64/cpu_detect.h"
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
static inline u64 xgetbv(u32 index) {
return _xgetbv(index);
}
#else
#if defined(__DragonFly__) || defined(__FreeBSD__)
@@ -39,12 +52,11 @@ static inline void __cpuid(int info[4], u32 function_id) {
}
#define _XCR_XFEATURE_ENABLED_MASK 0
static inline u64 _xgetbv(u32 index) {
static inline u64 xgetbv(u32 index) {
u32 eax, edx;
__asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
return ((u64)edx << 32) | eax;
}
#endif // _MSC_VER
namespace Common {
@@ -107,7 +119,7 @@ static CPUCaps Detect() {
// - Is the XSAVE bit set in CPUID?
// - XGETBV result has the XCR bit set.
if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
caps.avx = true;
if (Common::Bit<12>(cpu_id[2]))
caps.fma = true;
@@ -192,4 +204,45 @@ const CPUCaps& GetCPUCaps() {
return caps;
}
std::optional<int> GetProcessorCount() {
#if defined(_WIN32)
// Get the buffer length.
DWORD length = 0;
GetLogicalProcessorInformation(nullptr, &length);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
LOG_ERROR(Frontend, "Failed to query core count.");
return std::nullopt;
}
std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer(
length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
// Now query the core count.
if (!GetLogicalProcessorInformation(buffer.data(), &length)) {
LOG_ERROR(Frontend, "Failed to query core count.");
return std::nullopt;
}
return static_cast<int>(
std::count_if(buffer.cbegin(), buffer.cend(), [](const auto& proc_info) {
return proc_info.Relationship == RelationProcessorCore;
}));
#elif defined(__unix__)
const int thread_count = std::thread::hardware_concurrency();
std::ifstream smt("/sys/devices/system/cpu/smt/active");
char state = '0';
if (smt) {
smt.read(&state, sizeof(state));
}
switch (state) {
case '0':
return thread_count;
case '1':
return thread_count / 2;
default:
return std::nullopt;
}
#else
// Shame on you
return std::nullopt;
#endif
}
} // namespace Common

View File

@@ -4,6 +4,7 @@
#pragma once
#include <optional>
#include <string_view>
#include "common/common_types.h"
@@ -74,4 +75,7 @@ struct CPUCaps {
*/
const CPUCaps& GetCPUCaps();
/// Detects CPU core count
std::optional<int> GetProcessorCount();
} // namespace Common

View File

@@ -120,6 +120,8 @@ add_library(core STATIC
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
frontend/applets/cabinet.cpp
frontend/applets/cabinet.h
frontend/applets/controller.cpp
frontend/applets/controller.h
frontend/applets/error.cpp
@@ -190,11 +192,13 @@ add_library(core STATIC
hle/kernel/k_code_memory.h
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
hle/kernel/k_debug.h
hle/kernel/k_dynamic_page_manager.h
hle/kernel/k_dynamic_resource_manager.h
hle/kernel/k_dynamic_slab_heap.h
hle/kernel/k_event.cpp
hle/kernel/k_event.h
hle/kernel/k_event_info.h
hle/kernel/k_handle_table.cpp
hle/kernel/k_handle_table.h
hle/kernel/k_interrupt_manager.cpp
@@ -222,6 +226,8 @@ add_library(core STATIC
hle/kernel/k_page_group.h
hle/kernel/k_page_table.cpp
hle/kernel/k_page_table.h
hle/kernel/k_page_table_manager.h
hle/kernel/k_page_table_slab_heap.h
hle/kernel/k_port.cpp
hle/kernel/k_port.h
hle/kernel/k_priority_queue.h
@@ -254,6 +260,8 @@ add_library(core STATIC
hle/kernel/k_synchronization_object.cpp
hle/kernel/k_synchronization_object.h
hle/kernel/k_system_control.h
hle/kernel/k_system_resource.cpp
hle/kernel/k_system_resource.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
hle/kernel/k_thread_local_page.cpp
@@ -306,6 +314,8 @@ add_library(core STATIC
hle/service/am/applet_ae.h
hle/service/am/applet_oe.cpp
hle/service/am/applet_oe.h
hle/service/am/applets/applet_cabinet.cpp
hle/service/am/applets/applet_cabinet.h
hle/service/am/applets/applet_controller.cpp
hle/service/am/applets/applet_controller.h
hle/service/am/applets/applet_error.cpp
@@ -491,10 +501,6 @@ add_library(core STATIC
hle/service/hid/irsensor/processor_base.h
hle/service/hid/irsensor/tera_plugin_processor.cpp
hle/service/hid/irsensor/tera_plugin_processor.h
hle/service/jit/jit_context.cpp
hle/service/jit/jit_context.h
hle/service/jit/jit.cpp
hle/service/jit/jit.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/lan_discovery.cpp
@@ -524,6 +530,11 @@ add_library(core STATIC
hle/service/ncm/ncm.h
hle/service/nfc/nfc.cpp
hle/service/nfc/nfc.h
hle/service/nfc/nfc_device.cpp
hle/service/nfc/nfc_device.h
hle/service/nfc/nfc_result.h
hle/service/nfc/nfc_user.cpp
hle/service/nfc/nfc_user.h
hle/service/nfp/amiibo_crypto.cpp
hle/service/nfp/amiibo_crypto.h
hle/service/nfp/nfp.cpp
@@ -799,14 +810,18 @@ if (ENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service)
endif()
if (ARCHITECTURE_x86_64)
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic_32.cpp
arm/dynarmic/arm_dynarmic_32.h
arm/dynarmic/arm_dynarmic_64.cpp
arm/dynarmic/arm_dynarmic_64.h
arm/dynarmic/arm_dynarmic_32.cpp
arm/dynarmic/arm_dynarmic_32.h
arm/dynarmic/arm_dynarmic_cp15.cpp
arm/dynarmic/arm_dynarmic_cp15.h
hle/service/jit/jit_context.cpp
hle/service/jit/jit_context.h
hle/service/jit/jit.cpp
hle/service/jit/jit.h
)
target_link_libraries(core PRIVATE dynarmic)
endif()

View File

@@ -301,6 +301,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
}
}
#ifdef ARCHITECTURE_arm64
// TODO: remove when fixed in dynarmic
config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
#endif
return std::make_unique<Dynarmic::A32::Jit>(config);
}

View File

@@ -348,7 +348,6 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
config.fastmem_address_space_bits = 64;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}

View File

@@ -52,12 +52,16 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
case 4:
// CP15_DATA_SYNC_BARRIER
return Callback{
[](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
#ifdef _MSC_VER
[](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
_mm_mfence();
_mm_lfence();
#else
#elif defined(ARCHITECTURE_x86_64)
asm volatile("mfence\n\tlfence\n\t" : : : "memory");
#elif defined(ARCHITECTURE_arm64)
asm volatile("dsb sy\n\t" : : : "memory");
#else
#error Unsupported architecture
#endif
return 0;
},
@@ -66,11 +70,15 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
case 5:
// CP15_DATA_MEMORY_BARRIER
return Callback{
[](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
#ifdef _MSC_VER
[](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
_mm_mfence();
#else
#elif defined(ARCHITECTURE_x86_64)
asm volatile("mfence\n\t" : : : "memory");
#elif defined(ARCHITECTURE_arm64)
asm volatile("dmb sy\n\t" : : : "memory");
#else
#error Unsupported architecture
#endif
return 0;
},
@@ -115,7 +123,7 @@ CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1,
CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
if (!two && opc == 0 && CRm == CoprocReg::C14) {
// CNTPCT
const auto callback = [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 {
const auto callback = [](void* arg, u32, u32) -> u64 {
const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg);
return parent_arg.system.CoreTiming().GetClockTicks();
};

View File

@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef ARCHITECTURE_x86_64
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#endif
#include "core/arm/exclusive_monitor.h"
@@ -13,7 +13,7 @@ ExclusiveMonitor::~ExclusiveMonitor() = default;
std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
std::size_t num_cores) {
#ifdef ARCHITECTURE_x86_64
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores);
#else
// TODO(merry): Passthrough exclusive monitor

View File

@@ -189,7 +189,7 @@ struct System::Impl {
kernel.Suspend(false);
core_timing.SyncPause(false);
is_paused = false;
is_paused.store(false, std::memory_order_relaxed);
return status;
}
@@ -200,14 +200,13 @@ struct System::Impl {
core_timing.SyncPause(true);
kernel.Suspend(true);
is_paused = true;
is_paused.store(true, std::memory_order_relaxed);
return status;
}
bool IsPaused() const {
std::unique_lock lk(suspend_guard);
return is_paused;
return is_paused.load(std::memory_order_relaxed);
}
std::unique_lock<std::mutex> StallProcesses() {
@@ -218,7 +217,7 @@ struct System::Impl {
}
void UnstallProcesses() {
if (!is_paused) {
if (!IsPaused()) {
core_timing.SyncPause(false);
kernel.Suspend(false);
}
@@ -465,7 +464,7 @@ struct System::Impl {
}
mutable std::mutex suspend_guard;
bool is_paused{};
std::atomic_bool is_paused{};
std::atomic<bool> is_shutting_down{};
Timing::CoreTiming core_timing;

View File

@@ -578,18 +578,18 @@ KeyManager::KeyManager() {
if (Settings::values.use_dev_keys) {
dev_mode = true;
LoadFromFile(yuzu_keys_dir / "dev.keys", false);
LoadFromFile(yuzu_keys_dir / "dev.keys_autogenerated", false);
LoadFromFile(yuzu_keys_dir / "dev.keys", false);
} else {
dev_mode = false;
LoadFromFile(yuzu_keys_dir / "prod.keys", false);
LoadFromFile(yuzu_keys_dir / "prod.keys_autogenerated", false);
LoadFromFile(yuzu_keys_dir / "prod.keys", false);
}
LoadFromFile(yuzu_keys_dir / "title.keys", true);
LoadFromFile(yuzu_keys_dir / "title.keys_autogenerated", true);
LoadFromFile(yuzu_keys_dir / "console.keys", false);
LoadFromFile(yuzu_keys_dir / "title.keys", true);
LoadFromFile(yuzu_keys_dir / "console.keys_autogenerated", false);
LoadFromFile(yuzu_keys_dir / "console.keys", false);
}
static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {

View File

@@ -27,12 +27,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
c(received_data);
AsyncReceiveInto(r, buffer, c);
}
AsyncReceiveInto(r, buffer, c);
});
}
template <typename Callback>
static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) {
acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) {
if (!error.failed()) {
c(peer_socket);
AsyncAccept(acceptor, c);
}
});
}
template <typename Readable, typename Buffer>
static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
static_assert(std::is_trivial_v<Buffer>);
@@ -59,9 +68,7 @@ namespace Core {
class DebuggerImpl : public DebuggerBackend {
public:
explicit DebuggerImpl(Core::System& system_, u16 port)
: system{system_}, signal_pipe{io_context}, client_socket{io_context} {
frontend = std::make_unique<GDBStub>(*this, system);
explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {
InitializeServer(port);
}
@@ -70,39 +77,42 @@ public:
}
bool SignalDebugger(SignalInfo signal_info) {
{
std::scoped_lock lk{connection_lock};
std::scoped_lock lk{connection_lock};
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;
if (stopped || !state) {
// Do not notify the debugger about another event.
// It should be ignored.
return false;
}
// Set up the state.
stopped = true;
state->info = signal_info;
// Write a single byte into the pipe to wake up the debug interface.
boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
return true;
}
// These functions are callbacks from the frontend, and the lock will be held.
// There is no need to relock it.
std::span<const u8> ReadFromClient() override {
return ReceiveInto(client_socket, client_data);
return ReceiveInto(state->client_socket, state->client_data);
}
void WriteToClient(std::span<const u8> data) override {
boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
boost::asio::write(state->client_socket,
boost::asio::buffer(data.data(), data.size_bytes()));
}
void SetActiveThread(Kernel::KThread* thread) override {
active_thread = thread;
state->active_thread = thread;
}
Kernel::KThread* GetActiveThread() override {
return active_thread;
return state->active_thread;
}
private:
@@ -113,65 +123,78 @@ private:
// Run the connection thread.
connection_thread = std::jthread([&, port](std::stop_token stop_token) {
Common::SetCurrentThreadName("Debugger");
try {
// Initialize the listening socket and accept a new client.
tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
tcp::acceptor acceptor{io_context, endpoint};
acceptor.async_accept(client_socket, [](const auto&) {});
io_context.run_one();
io_context.restart();
AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); });
if (stop_token.stop_requested()) {
return;
while (!stop_token.stop_requested() && io_context.run()) {
}
ThreadLoop(stop_token);
} catch (const std::exception& ex) {
LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
}
});
}
void AcceptConnection(boost::asio::ip::tcp::socket&& peer) {
LOG_INFO(Debug_GDBStub, "Accepting new peer connection");
std::scoped_lock lk{connection_lock};
// Ensure everything is stopped.
PauseEmulation();
// Set up the new frontend.
frontend = std::make_unique<GDBStub>(*this, system);
// Set the new state. This will tear down any existing state.
state = ConnectionState{
.client_socket{std::move(peer)},
.signal_pipe{io_context},
.info{},
.active_thread{},
.client_data{},
.pipe_data{},
};
// Set up the client signals for new data.
AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); });
AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); });
// Set the active thread.
UpdateActiveThread();
// Set up the frontend.
frontend->Connected();
}
void ShutdownServer() {
connection_thread.request_stop();
io_context.stop();
connection_thread.join();
}
void ThreadLoop(std::stop_token stop_token) {
Common::SetCurrentThreadName("Debugger");
// Set up the client signals for new data.
AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
// Set the active thread.
UpdateActiveThread();
// Set up the frontend.
frontend->Connected();
// Main event loop.
while (!stop_token.stop_requested() && io_context.run()) {
}
}
void PipeData(std::span<const u8> data) {
switch (info.type) {
std::scoped_lock lk{connection_lock};
switch (state->info.type) {
case SignalType::Stopped:
case SignalType::Watchpoint:
// Stop emulation.
PauseEmulation();
// Notify the client.
active_thread = info.thread;
state->active_thread = state->info.thread;
UpdateActiveThread();
if (info.type == SignalType::Watchpoint) {
frontend->Watchpoint(active_thread, *info.watchpoint);
if (state->info.type == SignalType::Watchpoint) {
frontend->Watchpoint(state->active_thread, *state->info.watchpoint);
} else {
frontend->Stopped(active_thread);
frontend->Stopped(state->active_thread);
}
break;
@@ -179,8 +202,8 @@ private:
frontend->ShuttingDown();
// Wait for emulation to shut down gracefully now.
signal_pipe.close();
client_socket.shutdown(boost::asio::socket_base::shutdown_both);
state->signal_pipe.close();
state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
LOG_INFO(Debug_GDBStub, "Shut down server");
break;
@@ -188,17 +211,16 @@ private:
}
void ClientData(std::span<const u8> data) {
std::scoped_lock lk{connection_lock};
const auto actions{frontend->ClientData(data)};
for (const auto action : actions) {
switch (action) {
case DebuggerAction::Interrupt: {
{
std::scoped_lock lk{connection_lock};
stopped = true;
}
stopped = true;
PauseEmulation();
UpdateActiveThread();
frontend->Stopped(active_thread);
frontend->Stopped(state->active_thread);
break;
}
case DebuggerAction::Continue:
@@ -206,15 +228,15 @@ private:
break;
case DebuggerAction::StepThreadUnlocked:
MarkResumed([&] {
active_thread->SetStepState(Kernel::StepState::StepPending);
active_thread->Resume(Kernel::SuspendType::Debug);
ResumeEmulation(active_thread);
state->active_thread->SetStepState(Kernel::StepState::StepPending);
state->active_thread->Resume(Kernel::SuspendType::Debug);
ResumeEmulation(state->active_thread);
});
break;
case DebuggerAction::StepThreadLocked: {
MarkResumed([&] {
active_thread->SetStepState(Kernel::StepState::StepPending);
active_thread->Resume(Kernel::SuspendType::Debug);
state->active_thread->SetStepState(Kernel::StepState::StepPending);
state->active_thread->Resume(Kernel::SuspendType::Debug);
});
break;
}
@@ -254,15 +276,14 @@ private:
template <typename Callback>
void MarkResumed(Callback&& cb) {
Kernel::KScopedSchedulerLock sl{system.Kernel()};
std::scoped_lock cl{connection_lock};
stopped = false;
cb();
}
void UpdateActiveThread() {
const auto& threads{ThreadList()};
if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
active_thread = threads[0];
if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
state->active_thread = threads[0];
}
}
@@ -274,18 +295,22 @@ private:
System& system;
std::unique_ptr<DebuggerFrontend> frontend;
boost::asio::io_context io_context;
std::jthread connection_thread;
std::mutex connection_lock;
boost::asio::io_context io_context;
boost::process::async_pipe signal_pipe;
boost::asio::ip::tcp::socket client_socket;
SignalInfo info;
Kernel::KThread* active_thread;
bool pipe_data;
bool stopped;
struct ConnectionState {
boost::asio::ip::tcp::socket client_socket;
boost::process::async_pipe signal_pipe;
std::array<u8, 4096> client_data;
SignalInfo info;
Kernel::KThread* active_thread;
std::array<u8, 4096> client_data;
bool pipe_data;
};
std::optional<ConnectionState> state{};
bool stopped{};
};
Debugger::Debugger(Core::System& system, u16 port) {

View File

@@ -606,6 +606,8 @@ void GDBStub::HandleQuery(std::string_view command) {
} else if (command.starts_with("StartNoAckMode")) {
no_ack = true;
SendReply(GDB_STUB_REPLY_OK);
} else if (command.starts_with("Rcmd,")) {
HandleRcmd(Common::HexStringToVector(command.substr(5), false));
} else {
SendReply(GDB_STUB_REPLY_EMPTY);
}
@@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
}
}
constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
{"----- Free -----", Kernel::Svc::MemoryState::Free},
{"Io ", Kernel::Svc::MemoryState::Io},
{"Static ", Kernel::Svc::MemoryState::Static},
{"Code ", Kernel::Svc::MemoryState::Code},
{"CodeData ", Kernel::Svc::MemoryState::CodeData},
{"Normal ", Kernel::Svc::MemoryState::Normal},
{"Shared ", Kernel::Svc::MemoryState::Shared},
{"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
{"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
{"Ipc ", Kernel::Svc::MemoryState::Ipc},
{"Stack ", Kernel::Svc::MemoryState::Stack},
{"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
{"Transfered ", Kernel::Svc::MemoryState::Transfered},
{"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered},
{"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
{"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
{"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
{"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
{"Kernel ", Kernel::Svc::MemoryState::Kernel},
{"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
{"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
{"Coverage ", Kernel::Svc::MemoryState::Coverage},
}};
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
for (size_t i = 0; i < MemoryStateNames.size(); i++) {
if (std::get<1>(MemoryStateNames[i]) == state) {
return std::get<0>(MemoryStateNames[i]);
}
}
return "Unknown ";
}
static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) {
if (info.state == Kernel::Svc::MemoryState::Free) {
return " ";
}
switch (info.permission) {
case Kernel::Svc::MemoryPermission::ReadExecute:
return "r-x";
case Kernel::Svc::MemoryPermission::Read:
return "r--";
case Kernel::Svc::MemoryPermission::ReadWrite:
return "rw-";
default:
return "---";
}
}
static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
Kernel::Svc::MemoryInfo mem_info;
VAddr cur_addr{base};
// Expect: r-x Code (.text)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
cur_addr = mem_info.base_address + mem_info.size;
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
return cur_addr - 1;
}
// Expect: r-- Code (.rodata)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
cur_addr = mem_info.base_address + mem_info.size;
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
return cur_addr - 1;
}
// Expect: rw- CodeData (.data)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
cur_addr = mem_info.base_address + mem_info.size;
return cur_addr - 1;
}
void GDBStub::HandleRcmd(const std::vector<u8>& command) {
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
std::string reply;
auto* process = system.CurrentProcess();
auto& page_table = process->PageTable();
if (command_str == "get info") {
Loader::AppLoader::Modules modules;
system.GetAppLoader().ReadNSOModules(modules);
reply = fmt::format("Process: {:#x} ({})\n"
"Program Id: {:#018x}\n",
process->GetProcessID(), process->GetName(), process->GetProgramID());
reply +=
fmt::format("Layout:\n"
" Alias: {:#012x} - {:#012x}\n"
" Heap: {:#012x} - {:#012x}\n"
" Aslr: {:#012x} - {:#012x}\n"
" Stack: {:#012x} - {:#012x}\n"
"Modules:\n",
page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(),
page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(),
page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(),
page_table.GetStackRegionStart(), page_table.GetStackRegionEnd());
for (const auto& [vaddr, name] : modules) {
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
GetModuleEnd(page_table, vaddr), name);
}
} else if (command_str == "get mappings") {
reply = "Mappings:\n";
VAddr cur_addr = 0;
while (true) {
using MemoryAttribute = Kernel::Svc::MemoryAttribute;
auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
const char* state = GetMemoryStateName(mem_info.state);
const char* perm = GetMemoryPermissionString(mem_info);
const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
reply +=
fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
mem_info.base_address, mem_info.base_address + mem_info.size - 1,
perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
}
const uintptr_t next_address = mem_info.base_address + mem_info.size;
if (next_address <= cur_addr) {
break;
}
cur_addr = next_address;
}
} else if (command_str == "help") {
reply = "Commands:\n get info\n get mappings\n";
} else {
reply = "Unknown command.\nCommands:\n get info\n get mappings\n";
}
std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
SendReply(Common::HexToString(reply_span, false));
}
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
for (auto* thread : threads) {

View File

@@ -32,6 +32,7 @@ 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 HandleRcmd(const std::vector<u8>& command);
void HandleBreakpointInsert(std::string_view command);
void HandleBreakpointRemove(std::string_view command);
std::vector<char>::const_iterator CommandEnd() const;

View File

@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/frontend/applets/cabinet.h"
#include <thread>
namespace Core::Frontend {
CabinetApplet::~CabinetApplet() = default;
void DefaultCabinetApplet::ShowCabinetApplet(
const CabinetCallback& callback, const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
LOG_WARNING(Service_AM, "(STUBBED) called");
callback(false, {});
}
} // namespace Core::Frontend

View File

@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include "core/hle/service/nfp/nfp_types.h"
namespace Service::NFP {
class NfpDevice;
} // namespace Service::NFP
namespace Core::Frontend {
struct CabinetParameters {
Service::NFP::TagInfo tag_info;
Service::NFP::RegisterInfo register_info;
Service::NFP::CabinetMode mode;
};
using CabinetCallback = std::function<void(bool, const std::string&)>;
class CabinetApplet {
public:
virtual ~CabinetApplet();
virtual void ShowCabinetApplet(const CabinetCallback& callback,
const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0;
};
class DefaultCabinetApplet final : public CabinetApplet {
public:
void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
};
} // namespace Core::Frontend

View File

@@ -19,27 +19,26 @@ void EmulatedConsole::ReloadFromSettings() {
}
void EmulatedConsole::SetTouchParams() {
// TODO(german77): Support any number of fingers
std::size_t index = 0;
// Hardcode mouse, touchscreen and cemuhook parameters
// We can't use mouse as touch if native mouse is enabled
if (!Settings::values.mouse_enabled) {
// We can't use mouse as touch if native mouse is enabled
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
}
touch_params[index++] =
Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
touch_params[index++] =
Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
touch_params[index++] =
Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
touch_params[index++] =
Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
touch_params[index++] =
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
touch_params[index++] =
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) {
Common::ParamPackage touchscreen_param{};
touchscreen_param.Set("engine", "touch");
touchscreen_param.Set("axis_x", i * 2);
touchscreen_param.Set("axis_y", (i * 2) + 1);
touchscreen_param.Set("button", i);
touch_params[index++] = touchscreen_param;
}
const auto button_index =
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
@@ -47,7 +46,7 @@ void EmulatedConsole::SetTouchParams() {
// Map the rest of the fingers from touch from button configuration
for (const auto& config_entry : touch_buttons) {
if (index >= touch_params.size()) {
if (index >= MaxTouchDevices) {
continue;
}
Common::ParamPackage params{config_entry};
@@ -60,7 +59,6 @@ void EmulatedConsole::SetTouchParams() {
touch_button_params.Set("button", params.Serialize());
touch_button_params.Set("x", x);
touch_button_params.Set("y", y);
touch_button_params.Set("touch_id", static_cast<int>(index));
touch_params[index] = touch_button_params;
index++;
}
@@ -70,7 +68,7 @@ void EmulatedConsole::ReloadInput() {
// If you load any device here add the equivalent to the UnloadInput() function
SetTouchParams();
motion_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(motion_params);
motion_devices = Common::Input::CreateInputDevice(motion_params);
if (motion_devices) {
motion_devices->SetCallback({
.on_change =
@@ -81,7 +79,7 @@ void EmulatedConsole::ReloadInput() {
// Unique index for identifying touch device source
std::size_t index = 0;
for (auto& touch_device : touch_devices) {
touch_device = Common::Input::CreateDevice<Common::Input::InputDevice>(touch_params[index]);
touch_device = Common::Input::CreateInputDevice(touch_params[index]);
if (!touch_device) {
continue;
}
@@ -178,12 +176,38 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
}
void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
if (index >= console.touch_values.size()) {
if (index >= MaxTouchDevices) {
return;
}
std::unique_lock lock{mutex};
console.touch_values[index] = TransformToTouch(callback);
const auto touch_input = TransformToTouch(callback);
auto touch_index = GetIndexFromFingerId(index);
bool is_new_input = false;
if (!touch_index.has_value() && touch_input.pressed.value) {
touch_index = GetNextFreeIndex();
is_new_input = true;
}
// No free entries or invalid state. Ignore input
if (!touch_index.has_value()) {
return;
}
auto& touch_value = console.touch_values[touch_index.value()];
if (is_new_input) {
touch_value.pressed.value = true;
touch_value.id = static_cast<u32>(index);
}
touch_value.x = touch_input.x;
touch_value.y = touch_input.y;
if (!touch_input.pressed.value) {
touch_value.pressed.value = false;
}
if (is_configuring) {
lock.unlock();
@@ -191,11 +215,15 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
return;
}
// TODO(german77): Remap touch id in sequential order
console.touch_state[index] = {
.position = {console.touch_values[index].x.value, console.touch_values[index].y.value},
.id = static_cast<u32>(console.touch_values[index].id),
.pressed = console.touch_values[index].pressed.value,
// Touch outside allowed range. Ignore input
if (touch_index.value() >= MaxActiveTouchInputs) {
return;
}
console.touch_state[touch_index.value()] = {
.position = {touch_value.x.value, touch_value.y.value},
.id = static_cast<u32>(touch_index.value()),
.pressed = touch_input.pressed.value,
};
lock.unlock();
@@ -222,6 +250,28 @@ TouchFingerState EmulatedConsole::GetTouch() const {
return console.touch_state;
}
std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const {
for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
const auto& finger = console.touch_values[index];
if (!finger.pressed.value) {
continue;
}
if (finger.id == static_cast<int>(finger_id)) {
return index;
}
}
return std::nullopt;
}
std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const {
for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
if (!console.touch_values[index].pressed.value) {
return index;
}
}
return std::nullopt;
}
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {

View File

@@ -7,6 +7,7 @@
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <unordered_map>
#include "common/common_funcs.h"
@@ -20,6 +21,8 @@
#include "core/hid/motion_input.h"
namespace Core::HID {
static constexpr std::size_t MaxTouchDevices = 32;
static constexpr std::size_t MaxActiveTouchInputs = 16;
struct ConsoleMotionInfo {
Common::Input::MotionStatus raw_status{};
@@ -27,13 +30,13 @@ struct ConsoleMotionInfo {
};
using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>;
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>;
using ConsoleMotionParams = Common::ParamPackage;
using TouchParams = std::array<Common::ParamPackage, 16>;
using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
using ConsoleMotionValues = ConsoleMotionInfo;
using TouchValues = std::array<Common::Input::TouchStatus, 16>;
using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
struct TouchFinger {
u64 last_touch{};
@@ -55,7 +58,7 @@ struct ConsoleMotion {
bool is_at_rest{};
};
using TouchFingerState = std::array<TouchFinger, 16>;
using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>;
struct ConsoleStatus {
// Data from input_common
@@ -166,6 +169,10 @@ private:
*/
void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
std::optional<std::size_t> GetNextFreeIndex() const;
/**
* Triggers a callback that something has changed on the console status
* @param type Input type of the event to trigger

View File

@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include "common/thread.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/input_converter.h"
@@ -144,29 +146,23 @@ void EmulatedController::LoadDevices() {
LoadTASParams();
std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
button_params.begin() + Settings::NativeButton::BUTTON_NS_END,
button_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
stick_params.begin() + Settings::NativeAnalog::STICK_HID_END,
stick_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
motion_params.begin() + Settings::NativeMotion::MOTION_HID_END,
motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(),
Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
Common::Input::CreateDevice<Common::Input::InputDevice>);
camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
nfc_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(nfc_params);
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
Common::Input::CreateDevice<Common::Input::OutputDevice>);
std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice);
std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice);
std::ranges::transform(motion_params, motion_devices.begin(), Common::Input::CreateInputDevice);
std::ranges::transform(trigger_params, trigger_devices.begin(),
Common::Input::CreateInputDevice);
std::ranges::transform(battery_params, battery_devices.begin(),
Common::Input::CreateInputDevice);
camera_devices = Common::Input::CreateInputDevice(camera_params);
nfc_devices = Common::Input::CreateInputDevice(nfc_params);
std::ranges::transform(output_params, output_devices.begin(),
Common::Input::CreateOutputDevice);
// Initialize TAS devices
std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(),
Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(),
Common::Input::CreateDevice<Common::Input::InputDevice>);
std::ranges::transform(tas_button_params, tas_button_devices.begin(),
Common::Input::CreateInputDevice);
std::ranges::transform(tas_stick_params, tas_stick_devices.begin(),
Common::Input::CreateInputDevice);
}
void EmulatedController::LoadTASParams() {

View File

@@ -25,12 +25,12 @@ void EmulatedDevices::ReloadInput() {
Common::ParamPackage mouse_params;
mouse_params.Set("engine", "mouse");
mouse_params.Set("button", static_cast<int>(key_index));
mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
mouse_device = Common::Input::CreateInputDevice(mouse_params);
key_index++;
}
mouse_stick_device = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
"engine:mouse,axis_x:0,axis_y:1");
mouse_stick_device =
Common::Input::CreateInputDeviceFromString("engine:mouse,axis_x:0,axis_y:1");
// First two axis are reserved for mouse position
key_index = 2;
@@ -38,7 +38,7 @@ void EmulatedDevices::ReloadInput() {
Common::ParamPackage mouse_params;
mouse_params.Set("engine", "mouse");
mouse_params.Set("axis", static_cast<int>(key_index));
mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
mouse_device = Common::Input::CreateInputDevice(mouse_params);
key_index++;
}
@@ -50,7 +50,7 @@ void EmulatedDevices::ReloadInput() {
keyboard_params.Set("button", static_cast<int>(key_index));
keyboard_params.Set("port", 1);
keyboard_params.Set("pad", 0);
keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
keyboard_device = Common::Input::CreateInputDevice(keyboard_params);
key_index++;
}
@@ -62,11 +62,11 @@ void EmulatedDevices::ReloadInput() {
keyboard_params.Set("button", static_cast<int>(key_index));
keyboard_params.Set("port", 1);
keyboard_params.Set("pad", 1);
keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
keyboard_device = Common::Input::CreateInputDevice(keyboard_params);
key_index++;
}
ring_analog_device = Common::Input::CreateDevice<Common::Input::InputDevice>(ring_params);
ring_analog_device = Common::Input::CreateInputDevice(ring_params);
for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
if (!mouse_button_devices[index]) {
@@ -145,6 +145,7 @@ void EmulatedDevices::UnloadInput() {
for (auto& button : keyboard_modifier_devices) {
button.reset();
}
ring_analog_device.reset();
}
void EmulatedDevices::EnableConfiguration() {

View File

@@ -200,9 +200,6 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
x = std::clamp(x, 0.0f, 1.0f);
y = std::clamp(y, 0.0f, 1.0f);
// Limit id to maximum number of fingers
status.id = std::clamp(status.id, 0, 16);
if (status.pressed.inverted) {
status.pressed.value = !status.pressed.value;
}

View File

@@ -149,7 +149,7 @@ public:
context->AddDomainObject(std::move(iface));
} else {
kernel.CurrentProcess()->GetResourceLimit()->Reserve(
Kernel::LimitableResource::Sessions, 1);
Kernel::LimitableResource::SessionCountMax, 1);
auto* session = Kernel::KSession::Create(kernel);
session->Initialize(nullptr, iface->GetServiceName());

View File

@@ -8,6 +8,10 @@
namespace Kernel::Board::Nintendo::Nx {
class KSystemControl {
public:
// This can be overridden as needed.
static constexpr size_t SecureAppletMemorySize = 4 * 1024 * 1024; // 4_MB
public:
class Init {
public:

View File

@@ -27,16 +27,12 @@ namespace Kernel {
SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
ServiceThreadType thread_type)
: kernel{kernel_} {
if (thread_type == ServiceThreadType::CreateNew) {
service_thread = kernel.CreateServiceThread(service_name_);
} else {
service_thread = kernel.GetDefaultServiceThread();
}
}
: kernel{kernel_}, service_thread{thread_type == ServiceThreadType::CreateNew
? kernel.CreateServiceThread(service_name_)
: kernel.GetDefaultServiceThread()} {}
SessionRequestHandler::~SessionRequestHandler() {
kernel.ReleaseServiceThread(service_thread.lock());
kernel.ReleaseServiceThread(service_thread);
}
void SessionRequestHandler::AcceptSession(KServerPort* server_port) {
@@ -49,7 +45,7 @@ void SessionRequestHandler::AcceptSession(KServerPort* server_port) {
void SessionRequestHandler::RegisterSession(KServerSession* server_session,
std::shared_ptr<SessionRequestManager> manager) {
manager->SetSessionHandler(shared_from_this());
service_thread.lock()->RegisterServerSession(server_session, manager);
service_thread.RegisterServerSession(server_session, manager);
server_session->Close();
}

View File

@@ -82,13 +82,13 @@ public:
void RegisterSession(KServerSession* server_session,
std::shared_ptr<SessionRequestManager> manager);
std::weak_ptr<ServiceThread> GetServiceThread() const {
ServiceThread& GetServiceThread() const {
return service_thread;
}
protected:
KernelCore& kernel;
std::weak_ptr<ServiceThread> service_thread;
ServiceThread& service_thread;
};
using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
@@ -154,7 +154,7 @@ public:
session_handler = std::move(handler);
}
std::weak_ptr<ServiceThread> GetServiceThread() const {
ServiceThread& GetServiceThread() const {
return session_handler->GetServiceThread();
}
@@ -199,7 +199,7 @@ public:
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
u32* CommandBuffer() {
[[nodiscard]] u32* CommandBuffer() {
return cmd_buf.data();
}
@@ -207,7 +207,7 @@ public:
* Returns the session through which this request was made. This can be used as a map key to
* access per-client data on services.
*/
Kernel::KServerSession* Session() {
[[nodiscard]] Kernel::KServerSession* Session() {
return server_session;
}
@@ -217,61 +217,61 @@ public:
/// Writes data from this context back to the requesting process/thread.
Result WriteToOutgoingCommandBuffer(KThread& requesting_thread);
u32_le GetHipcCommand() const {
[[nodiscard]] u32_le GetHipcCommand() const {
return command;
}
u32_le GetTipcCommand() const {
[[nodiscard]] u32_le GetTipcCommand() const {
return static_cast<u32_le>(command_header->type.Value()) -
static_cast<u32_le>(IPC::CommandType::TIPC_CommandRegion);
}
u32_le GetCommand() const {
[[nodiscard]] u32_le GetCommand() const {
return command_header->IsTipc() ? GetTipcCommand() : GetHipcCommand();
}
bool IsTipc() const {
[[nodiscard]] bool IsTipc() const {
return command_header->IsTipc();
}
IPC::CommandType GetCommandType() const {
[[nodiscard]] IPC::CommandType GetCommandType() const {
return command_header->type;
}
u64 GetPID() const {
[[nodiscard]] u64 GetPID() const {
return pid;
}
u32 GetDataPayloadOffset() const {
[[nodiscard]] u32 GetDataPayloadOffset() const {
return data_payload_offset;
}
const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
return buffer_x_desciptors;
}
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
return buffer_a_desciptors;
}
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
return buffer_b_desciptors;
}
const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
return buffer_c_desciptors;
}
const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
[[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
return domain_message_header.value();
}
bool HasDomainMessageHeader() const {
[[nodiscard]] bool HasDomainMessageHeader() const {
return domain_message_header.has_value();
}
/// Helper function to read a buffer using the appropriate buffer descriptor
std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
[[nodiscard]] std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
/// Helper function to write a buffer using the appropriate buffer descriptor
std::size_t WriteBuffer(const void* buffer, std::size_t size,
@@ -308,22 +308,34 @@ public:
}
/// Helper function to get the size of the input buffer
std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
[[nodiscard]] std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to get the size of the output buffer
std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
[[nodiscard]] std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to derive the number of elements able to be contained in the read buffer
template <typename T>
[[nodiscard]] std::size_t GetReadBufferNumElements(std::size_t buffer_index = 0) const {
return GetReadBufferSize(buffer_index) / sizeof(T);
}
/// Helper function to derive the number of elements able to be contained in the write buffer
template <typename T>
[[nodiscard]] std::size_t GetWriteBufferNumElements(std::size_t buffer_index = 0) const {
return GetWriteBufferSize(buffer_index) / sizeof(T);
}
/// Helper function to test whether the input buffer at buffer_index can be read
bool CanReadBuffer(std::size_t buffer_index = 0) const;
[[nodiscard]] bool CanReadBuffer(std::size_t buffer_index = 0) const;
/// Helper function to test whether the output buffer at buffer_index can be written
bool CanWriteBuffer(std::size_t buffer_index = 0) const;
[[nodiscard]] bool CanWriteBuffer(std::size_t buffer_index = 0) const;
Handle GetCopyHandle(std::size_t index) const {
[[nodiscard]] Handle GetCopyHandle(std::size_t index) const {
return incoming_copy_handles.at(index);
}
Handle GetMoveHandle(std::size_t index) const {
[[nodiscard]] Handle GetMoveHandle(std::size_t index) const {
return incoming_move_handles.at(index);
}
@@ -348,13 +360,13 @@ public:
manager = manager_;
}
std::string Description() const;
[[nodiscard]] std::string Description() const;
KThread& GetThread() {
[[nodiscard]] KThread& GetThread() {
return *thread;
}
std::shared_ptr<SessionRequestManager> GetManager() const {
[[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
return manager.lock();
}

View File

@@ -10,7 +10,9 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_debug.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_event_info.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_buffer.h"
@@ -22,6 +24,7 @@
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_shared_memory_info.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/k_system_resource.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_transfer_memory.h"
@@ -44,7 +47,10 @@ namespace Kernel::Init {
HANDLER(KThreadLocalPage, \
(SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) \
HANDLER(KEventInfo, (SLAB_COUNT(KThread) + SLAB_COUNT(KDebug)), ##__VA_ARGS__) \
HANDLER(KDebug, (SLAB_COUNT(KDebug)), ##__VA_ARGS__) \
HANDLER(KSecureSystemResource, (SLAB_COUNT(KProcess)), ##__VA_ARGS__)
namespace {
@@ -73,8 +79,20 @@ constexpr size_t SlabCountKResourceLimit = 5;
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
constexpr size_t SlabCountKIoPool = 1;
constexpr size_t SlabCountKIoRegion = 6;
constexpr size_t SlabcountKSessionRequestMappings = 40;
constexpr size_t SlabCountExtraKThread = 160;
constexpr size_t SlabCountExtraKThread = (1024 + 256 + 256) - SlabCountKThread;
namespace test {
static_assert(KernelPageBufferHeapSize ==
2 * PageSize + (SlabCountKProcess + SlabCountKThread +
(SlabCountKProcess + SlabCountKThread) / 8) *
PageSize);
static_assert(KernelPageBufferAdditionalSize ==
(SlabCountExtraKThread + (SlabCountExtraKThread / 8)) * PageSize);
} // namespace test
/// Helper function to translate from the slab virtual address to the reserved location in physical
/// memory.
@@ -109,7 +127,7 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd
}
size_t CalculateSlabHeapGapSize() {
constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB;
constexpr size_t KernelSlabHeapGapSize = 2_MiB - 320_KiB;
static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
return KernelSlabHeapGapSize;
}
@@ -134,6 +152,7 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
.num_KDebug = SlabCountKDebug,
.num_KIoPool = SlabCountKIoPool,
.num_KIoRegion = SlabCountKIoRegion,
.num_KSessionRequestMappings = SlabcountKSessionRequestMappings,
};
}
@@ -164,29 +183,6 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
return size;
}
void InitializeKPageBufferSlabHeap(Core::System& system) {
auto& kernel = system.Kernel();
const auto& counts = kernel.SlabResourceCounts();
const size_t num_pages =
counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
const size_t slab_size = num_pages * PageSize;
// Reserve memory from the system resource limit.
ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
// Allocate memory for the slab.
constexpr auto AllocateOption = KMemoryManager::EncodeOption(
KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
const PAddr slab_address =
kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
ASSERT(slab_address != 0);
// Initialize the slabheap.
KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer<void>(slab_address),
slab_size);
}
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
auto& kernel = system.Kernel();
@@ -247,6 +243,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
// If we somehow get an invalid type, abort.
default:
ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
break;
}
// If we've hit the end of a gap, free it.
@@ -258,3 +255,30 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
}
} // namespace Kernel::Init
namespace Kernel {
void KPageBufferSlabHeap::Initialize(Core::System& system) {
auto& kernel = system.Kernel();
const auto& counts = kernel.SlabResourceCounts();
const size_t num_pages =
counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
const size_t slab_size = num_pages * PageSize;
// Reserve memory from the system resource limit.
ASSERT(
kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size));
// Allocate memory for the slab.
constexpr auto AllocateOption = KMemoryManager::EncodeOption(
KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
const PAddr slab_address =
kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
ASSERT(slab_address != 0);
// Initialize the slabheap.
KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer<void>(slab_address),
slab_size);
}
} // namespace Kernel

View File

@@ -33,11 +33,11 @@ struct KSlabResourceCounts {
size_t num_KDebug;
size_t num_KIoPool;
size_t num_KIoRegion;
size_t num_KSessionRequestMappings;
};
void InitializeSlabResourceCounts(KernelCore& kernel);
size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
void InitializeKPageBufferSlabHeap(Core::System& system);
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
} // namespace Kernel::Init

View File

@@ -16,6 +16,7 @@
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_system_resource.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_transfer_memory.h"
@@ -119,4 +120,6 @@ static_assert(std::is_final_v<KTransferMemory> && std::is_base_of_v<KAutoObject,
// static_assert(std::is_final_v<KCodeMemory> &&
// std::is_base_of_v<KAutoObject, KCodeMemory>);
static_assert(std::is_base_of_v<KAutoObject, KSystemResource>);
} // namespace Kernel

View File

@@ -10,6 +10,8 @@ namespace Kernel {
class KAutoObject;
class KSystemResource;
class KClassTokenGenerator {
public:
using TokenBaseType = u16;
@@ -58,7 +60,7 @@ private:
if constexpr (std::is_same<T, KAutoObject>::value) {
static_assert(T::ObjectType == ObjectType::KAutoObject);
return 0;
} else if constexpr (!std::is_final<T>::value) {
} else if constexpr (!std::is_final<T>::value && !std::same_as<T, KSystemResource>) {
static_assert(ObjectType::BaseClassesStart <= T::ObjectType &&
T::ObjectType < ObjectType::BaseClassesEnd);
constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
@@ -108,6 +110,8 @@ public:
KSessionRequest,
KCodeMemory,
KSystemResource,
// NOTE: True order for these has not been determined yet.
KAlpha,
KBeta,

View File

@@ -61,7 +61,7 @@ bool KClientPort::IsSignaled() const {
Result KClientPort::CreateSession(KClientSession** out) {
// Reserve a new session from the resource limit.
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
LimitableResource::Sessions);
LimitableResource::SessionCountMax);
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
// Update the session counts.

View File

@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KDebug final : public KAutoObjectWithSlabHeapAndContainer<KDebug, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KDebug, KAutoObject);
public:
explicit KDebug(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
};
} // namespace Kernel

View File

@@ -3,6 +3,8 @@
#pragma once
#include <vector>
#include "common/alignment.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_page_bitmap.h"
@@ -33,28 +35,36 @@ public:
return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
}
Result Initialize(VAddr addr, size_t sz) {
Result Initialize(VAddr memory, size_t size, size_t align) {
// We need to have positive size.
R_UNLESS(sz > 0, ResultOutOfMemory);
m_backing_memory.resize(sz);
R_UNLESS(size > 0, ResultOutOfMemory);
m_backing_memory.resize(size);
// Calculate management overhead.
const size_t management_size =
KPageBitmap::CalculateManagementOverheadSize(sz / sizeof(PageBuffer));
const size_t allocatable_size = sz - management_size;
// Set addresses.
m_address = memory;
m_aligned_address = Common::AlignDown(memory, align);
// Calculate extents.
const size_t managed_size = m_address + size - m_aligned_address;
const size_t overhead_size = Common::AlignUp(
KPageBitmap::CalculateManagementOverheadSize(managed_size / sizeof(PageBuffer)),
sizeof(PageBuffer));
R_UNLESS(overhead_size < size, ResultOutOfMemory);
// Set tracking fields.
m_address = addr;
m_size = Common::AlignDown(allocatable_size, sizeof(PageBuffer));
m_count = allocatable_size / sizeof(PageBuffer);
R_UNLESS(m_count > 0, ResultOutOfMemory);
m_size = Common::AlignDown(size - overhead_size, sizeof(PageBuffer));
m_count = m_size / sizeof(PageBuffer);
// Clear the management region.
u64* management_ptr = GetPointer<u64>(m_address + allocatable_size);
std::memset(management_ptr, 0, management_size);
u64* management_ptr = GetPointer<u64>(m_address + size - overhead_size);
std::memset(management_ptr, 0, overhead_size);
// Initialize the bitmap.
m_page_bitmap.Initialize(management_ptr, m_count);
const size_t allocatable_region_size =
(m_address + size - overhead_size) - m_aligned_address;
ASSERT(allocatable_region_size >= sizeof(PageBuffer));
m_page_bitmap.Initialize(management_ptr, allocatable_region_size / sizeof(PageBuffer));
// Free the pages to the bitmap.
for (size_t i = 0; i < m_count; i++) {
@@ -62,7 +72,8 @@ public:
std::memset(GetPointer<PageBuffer>(m_address) + i, 0, PageSize);
// Set the bit for the free page.
m_page_bitmap.SetBit(i);
m_page_bitmap.SetBit((m_address + (i * sizeof(PageBuffer)) - m_aligned_address) /
sizeof(PageBuffer));
}
R_SUCCEED();
@@ -101,7 +112,28 @@ public:
m_page_bitmap.ClearBit(offset);
m_peak = std::max(m_peak, (++m_used));
return GetPointer<PageBuffer>(m_address) + offset;
return GetPointer<PageBuffer>(m_aligned_address) + offset;
}
PageBuffer* Allocate(size_t count) {
// Take the lock.
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
KScopedSpinLock lk(m_lock);
// Find a random free block.
s64 soffset = m_page_bitmap.FindFreeRange(count);
if (soffset < 0) [[likely]] {
return nullptr;
}
const size_t offset = static_cast<size_t>(soffset);
// Update our tracking.
m_page_bitmap.ClearRange(offset, count);
m_used += count;
m_peak = std::max(m_peak, m_used);
return GetPointer<PageBuffer>(m_aligned_address) + offset;
}
void Free(PageBuffer* pb) {
@@ -113,7 +145,7 @@ public:
KScopedSpinLock lk(m_lock);
// Set the bit for the free page.
size_t offset = (reinterpret_cast<uintptr_t>(pb) - m_address) / sizeof(PageBuffer);
size_t offset = (reinterpret_cast<uintptr_t>(pb) - m_aligned_address) / sizeof(PageBuffer);
m_page_bitmap.SetBit(offset);
// Decrement our used count.
@@ -127,6 +159,7 @@ private:
size_t m_peak{};
size_t m_count{};
VAddr m_address{};
VAddr m_aligned_address{};
size_t m_size{};
// TODO(bunnei): Back by host memory until we emulate kernel virtual address space.

View File

@@ -6,6 +6,7 @@
#include "common/common_funcs.h"
#include "core/hle/kernel/k_dynamic_slab_heap.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_page_group.h"
namespace Kernel {
@@ -51,8 +52,10 @@ private:
DynamicSlabType* m_slab_heap{};
};
class KBlockInfoManager : public KDynamicResourceManager<KBlockInfo> {};
class KMemoryBlockSlabManager : public KDynamicResourceManager<KMemoryBlock> {};
using KBlockInfoSlabHeap = typename KBlockInfoManager::DynamicSlabType;
using KMemoryBlockSlabHeap = typename KMemoryBlockSlabManager::DynamicSlabType;
} // namespace Kernel

View File

@@ -20,8 +20,12 @@ void KEvent::Initialize(KProcess* owner) {
m_readable_event.Initialize(this);
// Set our owner process.
m_owner = owner;
m_owner->Open();
// HACK: this should never be nullptr, but service threads don't have a
// proper parent process yet.
if (owner != nullptr) {
m_owner = owner;
m_owner->Open();
}
// Mark initialized.
m_initialized = true;
@@ -50,8 +54,11 @@ Result KEvent::Clear() {
void KEvent::PostDestroy(uintptr_t arg) {
// Release the event count resource the owner process holds.
KProcess* owner = reinterpret_cast<KProcess*>(arg);
owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
owner->Close();
if (owner != nullptr) {
owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1);
owner->Close();
}
}
} // namespace Kernel

View File

@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <boost/intrusive/list.hpp>
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
namespace Kernel {
class KEventInfo : public KSlabAllocated<KEventInfo>, public boost::intrusive::list_base_hook<> {
public:
struct InfoCreateThread {
u32 thread_id{};
uintptr_t tls_address{};
};
struct InfoExitProcess {
Svc::ProcessExitReason reason{};
};
struct InfoExitThread {
Svc::ThreadExitReason reason{};
};
struct InfoException {
Svc::DebugException exception_type{};
s32 exception_data_count{};
uintptr_t exception_address{};
std::array<uintptr_t, 4> exception_data{};
};
struct InfoSystemCall {
s64 tick{};
s32 id{};
};
public:
KEventInfo() = default;
~KEventInfo() = default;
public:
Svc::DebugEvent event{};
u32 thread_id{};
u32 flags{};
bool is_attached{};
bool continue_flag{};
bool ignore_continue{};
bool close_once{};
union {
InfoCreateThread create_thread;
InfoExitProcess exit_process;
InfoExitThread exit_thread;
InfoException exception;
InfoSystemCall system_call;
} info{};
KThread* debug_thread{};
};
} // namespace Kernel

View File

@@ -2,17 +2,15 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_process.h"
namespace Kernel {
KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {}
KHandleTable::~KHandleTable() = default;
Result KHandleTable::Finalize() {
// Get the table and clear our record of it.
u16 saved_table_size = 0;
{
KScopedDisableDispatch dd(kernel);
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);
std::swap(m_table_size, saved_table_size);
@@ -25,28 +23,28 @@ Result KHandleTable::Finalize() {
}
}
return ResultSuccess;
R_SUCCEED();
}
bool KHandleTable::Remove(Handle handle) {
// Don't allow removal of a pseudo-handle.
if (Svc::IsPseudoHandle(handle)) {
if (Svc::IsPseudoHandle(handle)) [[unlikely]] {
return false;
}
// Handles must not have reserved bits set.
const auto handle_pack = HandlePack(handle);
if (handle_pack.reserved != 0) {
if (handle_pack.reserved != 0) [[unlikely]] {
return false;
}
// Find the object and free the entry.
KAutoObject* obj = nullptr;
{
KScopedDisableDispatch dd(kernel);
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);
if (this->IsValidHandle(handle)) {
if (this->IsValidHandle(handle)) [[likely]] {
const auto index = handle_pack.index;
obj = m_objects[index];
@@ -57,13 +55,13 @@ bool KHandleTable::Remove(Handle handle) {
}
// Close the object.
kernel.UnregisterInUseObject(obj);
m_kernel.UnregisterInUseObject(obj);
obj->Close();
return true;
}
Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
@@ -82,22 +80,38 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
*out_handle = EncodeHandle(static_cast<u16>(index), linear_id);
}
return ResultSuccess;
R_SUCCEED();
}
KScopedAutoObject<KAutoObject> KHandleTable::GetObjectForIpc(Handle handle,
KThread* cur_thread) const {
// Handle pseudo-handles.
ASSERT(cur_thread != nullptr);
if (handle == Svc::PseudoHandle::CurrentProcess) {
auto* const cur_process = cur_thread->GetOwnerProcess();
ASSERT(cur_process != nullptr);
return cur_process;
}
if (handle == Svc::PseudoHandle::CurrentThread) {
return cur_thread;
}
return GetObjectForIpcWithoutPseudoHandle(handle);
}
Result KHandleTable::Reserve(Handle* out_handle) {
KScopedDisableDispatch dd(kernel);
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
R_UNLESS(m_count < m_table_size, ResultOutOfHandles);
*out_handle = EncodeHandle(static_cast<u16>(this->AllocateEntry()), this->AllocateLinearId());
return ResultSuccess;
R_SUCCEED();
}
void KHandleTable::Unreserve(Handle handle) {
KScopedDisableDispatch dd(kernel);
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);
// Unpack the handle.
@@ -108,7 +122,7 @@ void KHandleTable::Unreserve(Handle handle) {
ASSERT(reserved == 0);
ASSERT(linear_id != 0);
if (index < m_table_size) {
if (index < m_table_size) [[likely]] {
// NOTE: This code does not check the linear id.
ASSERT(m_objects[index] == nullptr);
this->FreeEntry(index);
@@ -116,7 +130,7 @@ void KHandleTable::Unreserve(Handle handle) {
}
void KHandleTable::Register(Handle handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);
// Unpack the handle.
@@ -127,7 +141,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj) {
ASSERT(reserved == 0);
ASSERT(linear_id != 0);
if (index < m_table_size) {
if (index < m_table_size) [[likely]] {
// Set the entry.
ASSERT(m_objects[index] == nullptr);

View File

@@ -21,33 +21,38 @@ namespace Kernel {
class KernelCore;
class KHandleTable {
public:
YUZU_NON_COPYABLE(KHandleTable);
YUZU_NON_MOVEABLE(KHandleTable);
public:
static constexpr size_t MaxTableSize = 1024;
explicit KHandleTable(KernelCore& kernel_);
~KHandleTable();
public:
explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
Result Initialize(s32 size) {
// Check that the table size is valid.
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
// Lock.
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);
// Initialize all fields.
m_max_count = 0;
m_table_size = static_cast<u16>((size <= 0) ? MaxTableSize : size);
m_table_size = static_cast<s16>((size <= 0) ? MaxTableSize : size);
m_next_linear_id = MinLinearId;
m_count = 0;
m_free_head_index = -1;
// Free all entries.
for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) {
for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
m_objects[i] = nullptr;
m_entry_infos[i].next_free_index = i - 1;
m_entry_infos[i].next_free_index = static_cast<s16>(i - 1);
m_free_head_index = i;
}
return ResultSuccess;
R_SUCCEED();
}
size_t GetTableSize() const {
@@ -66,13 +71,13 @@ public:
template <typename T = KAutoObject>
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
// Lock and look up in table.
KScopedDisableDispatch dd(kernel);
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);
if constexpr (std::is_same_v<T, KAutoObject>) {
return this->GetObjectImpl(handle);
} else {
if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) {
if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) [[likely]] {
return obj->DynamicCast<T*>();
} else {
return nullptr;
@@ -85,13 +90,13 @@ public:
// Handle pseudo-handles.
if constexpr (std::derived_from<KProcess, T>) {
if (handle == Svc::PseudoHandle::CurrentProcess) {
auto* const cur_process = kernel.CurrentProcess();
auto* const cur_process = m_kernel.CurrentProcess();
ASSERT(cur_process != nullptr);
return cur_process;
}
} else if constexpr (std::derived_from<KThread, T>) {
if (handle == Svc::PseudoHandle::CurrentThread) {
auto* const cur_thread = GetCurrentThreadPointer(kernel);
auto* const cur_thread = GetCurrentThreadPointer(m_kernel);
ASSERT(cur_thread != nullptr);
return cur_thread;
}
@@ -100,6 +105,23 @@ public:
return this->template GetObjectWithoutPseudoHandle<T>(handle);
}
KScopedAutoObject<KAutoObject> GetObjectForIpcWithoutPseudoHandle(Handle handle) const {
// Lock and look up in table.
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);
return this->GetObjectImpl(handle);
}
KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const;
KScopedAutoObject<KAutoObject> GetObjectByIndex(Handle* out_handle, size_t index) const {
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);
return this->GetObjectByIndexImpl(out_handle, index);
}
Result Reserve(Handle* out_handle);
void Unreserve(Handle handle);
@@ -112,7 +134,7 @@ public:
size_t num_opened;
{
// Lock the table.
KScopedDisableDispatch dd(kernel);
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);
for (num_opened = 0; num_opened < num_handles; num_opened++) {
// Get the current handle.
@@ -120,13 +142,13 @@ public:
// Get the object for the current handle.
KAutoObject* cur_object = this->GetObjectImpl(cur_handle);
if (cur_object == nullptr) {
if (cur_object == nullptr) [[unlikely]] {
break;
}
// Cast the current object to the desired type.
T* cur_t = cur_object->DynamicCast<T*>();
if (cur_t == nullptr) {
if (cur_t == nullptr) [[unlikely]] {
break;
}
@@ -137,7 +159,7 @@ public:
}
// If we converted every object, succeed.
if (num_opened == num_handles) {
if (num_opened == num_handles) [[likely]] {
return true;
}
@@ -191,21 +213,21 @@ private:
ASSERT(reserved == 0);
// Validate our indexing information.
if (raw_value == 0) {
if (raw_value == 0) [[unlikely]] {
return false;
}
if (linear_id == 0) {
if (linear_id == 0) [[unlikely]] {
return false;
}
if (index >= m_table_size) {
if (index >= m_table_size) [[unlikely]] {
return false;
}
// Check that there's an object, and our serial id is correct.
if (m_objects[index] == nullptr) {
if (m_objects[index] == nullptr) [[unlikely]] {
return false;
}
if (m_entry_infos[index].GetLinearId() != linear_id) {
if (m_entry_infos[index].GetLinearId() != linear_id) [[unlikely]] {
return false;
}
@@ -215,11 +237,11 @@ private:
KAutoObject* GetObjectImpl(Handle handle) const {
// Handles must not have reserved bits set.
const auto handle_pack = HandlePack(handle);
if (handle_pack.reserved != 0) {
if (handle_pack.reserved != 0) [[unlikely]] {
return nullptr;
}
if (this->IsValidHandle(handle)) {
if (this->IsValidHandle(handle)) [[likely]] {
return m_objects[handle_pack.index];
} else {
return nullptr;
@@ -227,9 +249,8 @@ private:
}
KAutoObject* GetObjectByIndexImpl(Handle* out_handle, size_t index) const {
// Index must be in bounds.
if (index >= m_table_size) {
if (index >= m_table_size) [[unlikely]] {
return nullptr;
}
@@ -244,18 +265,15 @@ private:
private:
union HandlePack {
HandlePack() = default;
HandlePack(Handle handle) : raw{static_cast<u32>(handle)} {}
constexpr HandlePack() = default;
constexpr HandlePack(Handle handle) : raw{static_cast<u32>(handle)} {}
u32 raw;
u32 raw{};
BitField<0, 15, u32> index;
BitField<15, 15, u32> linear_id;
BitField<30, 2, u32> reserved;
};
static constexpr u16 MinLinearId = 1;
static constexpr u16 MaxLinearId = 0x7FFF;
static constexpr Handle EncodeHandle(u16 index, u16 linear_id) {
HandlePack handle{};
handle.index.Assign(index);
@@ -264,6 +282,10 @@ private:
return handle.raw;
}
private:
static constexpr u16 MinLinearId = 1;
static constexpr u16 MaxLinearId = 0x7FFF;
union EntryInfo {
u16 linear_id;
s16 next_free_index;
@@ -271,21 +293,21 @@ private:
constexpr u16 GetLinearId() const {
return linear_id;
}
constexpr s16 GetNextFreeIndex() const {
constexpr s32 GetNextFreeIndex() const {
return next_free_index;
}
};
private:
KernelCore& m_kernel;
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
std::array<KAutoObject*, MaxTableSize> m_objects{};
s32 m_free_head_index{-1};
mutable KSpinLock m_lock;
s32 m_free_head_index{};
u16 m_table_size{};
u16 m_max_count{};
u16 m_next_linear_id{MinLinearId};
u16 m_next_linear_id{};
u16 m_count{};
mutable KSpinLock m_lock;
KernelCore& kernel;
};
} // namespace Kernel

View File

@@ -35,26 +35,32 @@ enum class KMemoryState : u32 {
FlagCanMapProcess = (1 << 23),
FlagCanChangeAttribute = (1 << 24),
FlagCanCodeMemory = (1 << 25),
FlagLinearMapped = (1 << 26),
FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagCanIpcUserBuffer |
FlagReferenceCounted | FlagCanChangeAttribute,
FlagReferenceCounted | FlagCanChangeAttribute | FlagLinearMapped,
FlagsCode = FlagCanDebug | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
FlagMapped | FlagCode | FlagCanQueryPhysical | FlagCanDeviceMap |
FlagCanAlignedDeviceMap | FlagReferenceCounted,
FlagCanAlignedDeviceMap | FlagReferenceCounted | FlagLinearMapped,
FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap,
FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap |
FlagLinearMapped,
Free = static_cast<u32>(Svc::MemoryState::Free),
Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped,
Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
FlagCanAlignedDeviceMap,
Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
FlagCanCodeMemory,
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted,
Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
FlagLinearMapped,
// Alias was removed after 1.0.0.
AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
FlagCanCodeAlias,
@@ -67,18 +73,18 @@ enum class KMemoryState : u32 {
Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
ThreadLocal =
static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagLinearMapped,
Transfered = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
FlagReferenceCounted | FlagLinearMapped | FlagCanUseNonSecureIpc |
FlagCanUseNonDeviceIpc,
Inaccessible = static_cast<u32>(Svc::MemoryState::Inaccessible),
@@ -91,69 +97,69 @@ enum class KMemoryState : u32 {
Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
FlagReferenceCounted | FlagCanDebug,
CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted |
FlagLinearMapped,
Coverage = static_cast<u32>(Svc::MemoryState::Coverage) | FlagMapped,
Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00002001);
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00182001);
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
static_assert(static_cast<u32>(KMemoryState::Code) == 0x00DC7E03);
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x03FEBD04);
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x037EBD05);
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x00402006);
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x00DD7E08);
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09);
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A);
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B);
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C);
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x015C3C0D);
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x005C380E);
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F);
static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x07FEBD04);
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x07FFBD09);
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400200C);
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811);
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812);
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214);
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x05583817);
enum class KMemoryPermission : u8 {
None = 0,
All = static_cast<u8>(~None),
Read = 1 << 0,
Write = 1 << 1,
Execute = 1 << 2,
ReadAndWrite = Read | Write,
ReadAndExecute = Read | Execute,
UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
Svc::MemoryPermission::Execute),
KernelShift = 3,
KernelRead = Read << KernelShift,
KernelWrite = Write << KernelShift,
KernelExecute = Execute << KernelShift,
KernelRead = static_cast<u8>(Svc::MemoryPermission::Read) << KernelShift,
KernelWrite = static_cast<u8>(Svc::MemoryPermission::Write) << KernelShift,
KernelExecute = static_cast<u8>(Svc::MemoryPermission::Execute) << KernelShift,
NotMapped = (1 << (2 * KernelShift)),
KernelReadWrite = KernelRead | KernelWrite,
KernelReadExecute = KernelRead | KernelExecute,
UserRead = Read | KernelRead,
UserWrite = Write | KernelWrite,
UserExecute = Execute,
UserRead = static_cast<u8>(Svc::MemoryPermission::Read) | KernelRead,
UserWrite = static_cast<u8>(Svc::MemoryPermission::Write) | KernelWrite,
UserExecute = static_cast<u8>(Svc::MemoryPermission::Execute),
UserReadWrite = UserRead | UserWrite,
UserReadExecute = UserRead | UserExecute,
IpcLockChangeMask = NotMapped | UserReadWrite
UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
Svc::MemoryPermission::Execute),
IpcLockChangeMask = NotMapped | UserReadWrite,
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
@@ -210,13 +216,15 @@ struct KMemoryInfo {
constexpr Svc::MemoryInfo GetSvcMemoryInfo() const {
return {
.addr = m_address,
.base_address = m_address,
.size = m_size,
.state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask),
.attr = static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask),
.perm = static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask),
.ipc_refcount = m_ipc_lock_count,
.device_refcount = m_device_use_count,
.attribute =
static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask),
.permission =
static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask),
.ipc_count = m_ipc_lock_count,
.device_count = m_device_use_count,
.padding = {},
};
}
@@ -468,6 +476,7 @@ public:
constexpr void UpdateDeviceDisableMergeStateForShareLeft(
[[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) {
// New permission/right aren't used.
if (left) {
m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceLeft);
@@ -478,6 +487,7 @@ public:
constexpr void UpdateDeviceDisableMergeStateForShareRight(
[[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) {
// New permission/left aren't used.
if (right) {
m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceRight);
@@ -494,6 +504,8 @@ public:
constexpr void ShareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left,
bool right) {
// New permission isn't used.
// We must either be shared or have a zero lock count.
ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared ||
m_device_use_count == 0);
@@ -509,6 +521,7 @@ public:
constexpr void UpdateDeviceDisableMergeStateForUnshareLeft(
[[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) {
// New permission/right aren't used.
if (left) {
if (!m_device_disable_merge_left_count) {
@@ -528,6 +541,8 @@ public:
constexpr void UpdateDeviceDisableMergeStateForUnshareRight(
[[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) {
// New permission/left aren't used.
if (right) {
const u16 old_device_disable_merge_right_count = m_device_disable_merge_right_count--;
ASSERT(old_device_disable_merge_right_count > 0);
@@ -546,6 +561,8 @@ public:
constexpr void UnshareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left,
bool right) {
// New permission isn't used.
// We must be shared.
ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared);
@@ -563,6 +580,7 @@ public:
constexpr void UnshareToDeviceRight([[maybe_unused]] KMemoryPermission new_perm, bool left,
bool right) {
// New permission isn't used.
// We must be shared.
ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared);
@@ -613,6 +631,8 @@ public:
constexpr void UnlockForIpc([[maybe_unused]] KMemoryPermission new_perm, bool left,
[[maybe_unused]] bool right) {
// New permission isn't used.
// We must be locked.
ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::IpcLocked);

View File

@@ -153,13 +153,9 @@ void KMemoryLayout::InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_
}
}
size_t KMemoryLayout::GetResourceRegionSizeForInit() {
// Calculate resource region size based on whether we allow extra threads.
const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit();
size_t resource_region_size =
KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0);
return resource_region_size;
size_t KMemoryLayout::GetResourceRegionSizeForInit(bool use_extra_resource) {
return KernelResourceSize + KSystemControl::SecureAppletMemorySize +
(use_extra_resource ? KernelSlabHeapAdditionalSize + KernelPageBufferAdditionalSize : 0);
}
} // namespace Kernel

View File

@@ -60,10 +60,12 @@ constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB;
constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000;
constexpr size_t KernelPageBufferHeapSize = 0x3E0000;
constexpr size_t KernelSlabHeapAdditionalSize = 0x148000;
constexpr size_t KernelPageBufferAdditionalSize = 0x33C000;
constexpr std::size_t KernelResourceSize =
KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize +
KernelSlabHeapSize + KernelPageBufferHeapSize;
constexpr bool IsKernelAddressKey(VAddr key) {
return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast;
@@ -168,6 +170,11 @@ public:
KMemoryRegionType_VirtualDramKernelTraceBuffer));
}
const KMemoryRegion& GetSecureAppletMemoryRegion() {
return Dereference(GetVirtualMemoryRegionTree().FindByType(
KMemoryRegionType_VirtualDramKernelSecureAppletMemory));
}
const KMemoryRegion& GetVirtualLinearRegion(VAddr address) const {
return Dereference(FindVirtualLinear(address));
}
@@ -229,7 +236,7 @@ public:
void InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start,
VAddr linear_virtual_start);
static size_t GetResourceRegionSizeForInit();
static size_t GetResourceRegionSizeForInit(bool use_extra_resource);
auto GetKernelRegionExtents() const {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel);
@@ -279,6 +286,10 @@ public:
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelSlab);
}
auto GetKernelSecureAppletMemoryRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelSecureAppletMemory);
}
auto GetKernelPageTableHeapRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelPtHeap);

View File

@@ -29,43 +29,44 @@ constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
} else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
return KMemoryManager::Pool::SystemNonSecure;
} else {
ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool");
return {};
UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool");
}
}
} // namespace
KMemoryManager::KMemoryManager(Core::System& system_)
: system{system_}, pool_locks{
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
} {}
KMemoryManager::KMemoryManager(Core::System& system)
: m_system{system}, m_memory_layout{system.Kernel().MemoryLayout()},
m_pool_locks{
KLightLock{system.Kernel()},
KLightLock{system.Kernel()},
KLightLock{system.Kernel()},
KLightLock{system.Kernel()},
} {}
void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) {
// Clear the management region to zero.
const VAddr management_region_end = management_region + management_region_size;
// std::memset(GetVoidPointer(management_region), 0, management_region_size);
// Reset our manager count.
num_managers = 0;
m_num_managers = 0;
// Traverse the virtual memory layout tree, initializing each manager as appropriate.
while (num_managers != MaxManagerCount) {
while (m_num_managers != MaxManagerCount) {
// Locate the region that should initialize the current manager.
PAddr region_address = 0;
size_t region_size = 0;
Pool region_pool = Pool::Count;
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
// We only care about regions that we need to create managers for.
if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
continue;
}
// We want to initialize the managers in order.
if (it.GetAttributes() != num_managers) {
if (it.GetAttributes() != m_num_managers) {
continue;
}
@@ -97,8 +98,8 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
}
// Initialize a new manager for the region.
Impl* manager = std::addressof(managers[num_managers++]);
ASSERT(num_managers <= managers.size());
Impl* manager = std::addressof(m_managers[m_num_managers++]);
ASSERT(m_num_managers <= m_managers.size());
const size_t cur_size = manager->Initialize(region_address, region_size, management_region,
management_region_end, region_pool);
@@ -107,13 +108,13 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
// Insert the manager into the pool list.
const auto region_pool_index = static_cast<u32>(region_pool);
if (pool_managers_tail[region_pool_index] == nullptr) {
pool_managers_head[region_pool_index] = manager;
if (m_pool_managers_tail[region_pool_index] == nullptr) {
m_pool_managers_head[region_pool_index] = manager;
} else {
pool_managers_tail[region_pool_index]->SetNext(manager);
manager->SetPrev(pool_managers_tail[region_pool_index]);
m_pool_managers_tail[region_pool_index]->SetNext(manager);
manager->SetPrev(m_pool_managers_tail[region_pool_index]);
}
pool_managers_tail[region_pool_index] = manager;
m_pool_managers_tail[region_pool_index] = manager;
}
// Free each region to its corresponding heap.
@@ -121,11 +122,10 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress();
const PAddr ini_end = ini_start + InitialProcessBinarySizeMax;
const PAddr ini_last = ini_end - 1;
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
// Get the manager for the region.
auto index = it.GetAttributes();
auto& manager = managers[index];
auto& manager = m_managers[it.GetAttributes()];
const PAddr cur_start = it.GetAddress();
const PAddr cur_last = it.GetLastAddress();
@@ -162,11 +162,19 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
}
// Update the used size for all managers.
for (size_t i = 0; i < num_managers; ++i) {
managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
for (size_t i = 0; i < m_num_managers; ++i) {
m_managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
}
}
Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) {
UNREACHABLE();
}
void KMemoryManager::FinalizeOptimizedMemory(u64 process_id, Pool pool) {
UNREACHABLE();
}
PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) {
// Early return if we're allocating no pages.
if (num_pages == 0) {
@@ -175,7 +183,7 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
// Lock the pool that we're allocating from.
const auto [pool, dir] = DecodeOption(option);
KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]);
KScopedLightLock lk(m_pool_locks[static_cast<std::size_t>(pool)]);
// Choose a heap based on our page size request.
const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages);
@@ -185,7 +193,7 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
PAddr allocated_block = 0;
for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr;
chosen_manager = this->GetNextManager(chosen_manager, dir)) {
allocated_block = chosen_manager->AllocateBlock(heap_index, true);
allocated_block = chosen_manager->AllocateAligned(heap_index, num_pages, align_pages);
if (allocated_block != 0) {
break;
}
@@ -196,10 +204,9 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
return 0;
}
// If we allocated more than we need, free some.
const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index);
if (allocated_pages > num_pages) {
chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
// Maintain the optimized memory bitmap, if we should.
if (m_has_optimized_process[static_cast<size_t>(pool)]) {
UNIMPLEMENTED();
}
// Open the first reference to the pages.
@@ -209,20 +216,21 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
}
Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool,
Direction dir, bool random) {
Direction dir, bool unoptimized, bool random) {
// Choose a heap based on our page size request.
const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
R_UNLESS(0 <= heap_index, ResultOutOfMemory);
// Ensure that we don't leave anything un-freed.
auto group_guard = SCOPE_GUARD({
ON_RESULT_FAILURE {
for (const auto& it : out->Nodes()) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress());
const size_t num_pages_to_free =
auto& manager = this->GetManager(it.GetAddress());
const size_t node_num_pages =
std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
manager.Free(it.GetAddress(), num_pages_to_free);
manager.Free(it.GetAddress(), node_num_pages);
}
});
out->Finalize();
};
// Keep allocating until we've allocated all our pages.
for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) {
@@ -236,12 +244,17 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
break;
}
// Safely add it to our group.
{
auto block_guard =
SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); });
R_TRY(out->AddBlock(allocated_block, pages_per_alloc));
block_guard.Cancel();
// Ensure we don't leak the block if we fail.
ON_RESULT_FAILURE_2 {
cur_manager->Free(allocated_block, pages_per_alloc);
};
// Add the block to our group.
R_TRY(out->AddBlock(allocated_block, pages_per_alloc));
// Maintain the optimized memory bitmap, if we should.
if (unoptimized) {
UNIMPLEMENTED();
}
num_pages -= pages_per_alloc;
@@ -253,8 +266,7 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
R_UNLESS(num_pages == 0, ResultOutOfMemory);
// We succeeded!
group_guard.Cancel();
return ResultSuccess;
R_SUCCEED();
}
Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) {
@@ -266,10 +278,11 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
// Lock the pool that we're allocating from.
const auto [pool, dir] = DecodeOption(option);
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
// Allocate the page group.
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir,
m_has_optimized_process[static_cast<size_t>(pool)], true));
// Open the first reference to the pages.
for (const auto& block : out->Nodes()) {
@@ -277,7 +290,7 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
size_t remaining_pages = block.GetNumPages();
while (remaining_pages > 0) {
// Get the manager for the current address.
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
auto& manager = this->GetManager(cur_address);
// Process part or all of the block.
const size_t cur_pages =
@@ -290,11 +303,11 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
}
}
return ResultSuccess;
R_SUCCEED();
}
Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option,
u64 process_id, u8 fill_pattern) {
Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32 option,
u64 process_id, u8 fill_pattern) {
ASSERT(out != nullptr);
ASSERT(out->GetNumPages() == 0);
@@ -302,83 +315,89 @@ Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pag
const auto [pool, dir] = DecodeOption(option);
// Allocate the memory.
bool optimized;
{
// Lock the pool that we're allocating from.
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
// Check if we have an optimized process.
const bool has_optimized = m_has_optimized_process[static_cast<size_t>(pool)];
const bool is_optimized = m_optimized_process_ids[static_cast<size_t>(pool)] == process_id;
// Allocate the page group.
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, has_optimized && !is_optimized,
false));
// Open the first reference to the pages.
// Set whether we should optimize.
optimized = has_optimized && is_optimized;
}
// Perform optimized memory tracking, if we should.
if (optimized) {
// Iterate over the allocated blocks.
for (const auto& block : out->Nodes()) {
PAddr cur_address = block.GetAddress();
size_t remaining_pages = block.GetNumPages();
while (remaining_pages > 0) {
// Get the manager for the current address.
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
// Get the block extents.
const PAddr block_address = block.GetAddress();
const size_t block_pages = block.GetNumPages();
// Process part or all of the block.
const size_t cur_pages =
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
manager.OpenFirst(cur_address, cur_pages);
// If it has no pages, we don't need to do anything.
if (block_pages == 0) {
continue;
}
// Advance.
cur_address += cur_pages * PageSize;
remaining_pages -= cur_pages;
// Fill all the pages that we need to fill.
bool any_new = false;
{
PAddr cur_address = block_address;
size_t remaining_pages = block_pages;
while (remaining_pages > 0) {
// Get the manager for the current address.
auto& manager = this->GetManager(cur_address);
// Process part or all of the block.
const size_t cur_pages =
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
any_new =
manager.ProcessOptimizedAllocation(cur_address, cur_pages, fill_pattern);
// Advance.
cur_address += cur_pages * PageSize;
remaining_pages -= cur_pages;
}
}
// If there are new pages, update tracking for the allocation.
if (any_new) {
// Update tracking for the allocation.
PAddr cur_address = block_address;
size_t remaining_pages = block_pages;
while (remaining_pages > 0) {
// Get the manager for the current address.
auto& manager = this->GetManager(cur_address);
// Lock the pool for the manager.
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
// Track some or all of the current pages.
const size_t cur_pages =
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
manager.TrackOptimizedAllocation(cur_address, cur_pages);
// Advance.
cur_address += cur_pages * PageSize;
remaining_pages -= cur_pages;
}
}
}
}
// Set all the allocated memory.
for (const auto& block : out->Nodes()) {
std::memset(system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
block.GetSize());
}
return ResultSuccess;
}
void KMemoryManager::Open(PAddr address, size_t num_pages) {
// Repeatedly open references until we've done so for all pages.
while (num_pages) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
{
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
manager.Open(address, cur_pages);
} else {
// Set all the allocated memory.
for (const auto& block : out->Nodes()) {
std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
block.GetSize());
}
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
void KMemoryManager::Close(PAddr address, size_t num_pages) {
// Repeatedly close references until we've done so for all pages.
while (num_pages) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
{
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
manager.Close(address, cur_pages);
}
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
void KMemoryManager::Close(const KPageGroup& pg) {
for (const auto& node : pg.Nodes()) {
Close(node.GetAddress(), node.GetNumPages());
}
}
void KMemoryManager::Open(const KPageGroup& pg) {
for (const auto& node : pg.Nodes()) {
Open(node.GetAddress(), node.GetNumPages());
}
R_SUCCEED();
}
size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management,
@@ -394,18 +413,31 @@ size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr manage
ASSERT(Common::IsAligned(total_management_size, PageSize));
// Setup region.
pool = p;
management_region = management;
page_reference_counts.resize(
m_pool = p;
m_management_region = management;
m_page_reference_counts.resize(
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize);
ASSERT(Common::IsAligned(management_region, PageSize));
ASSERT(Common::IsAligned(m_management_region, PageSize));
// Initialize the manager's KPageHeap.
heap.Initialize(address, size, management + manager_size, page_heap_size);
m_heap.Initialize(address, size, management + manager_size, page_heap_size);
return total_management_size;
}
void KMemoryManager::Impl::TrackUnoptimizedAllocation(PAddr block, size_t num_pages) {
UNREACHABLE();
}
void KMemoryManager::Impl::TrackOptimizedAllocation(PAddr block, size_t num_pages) {
UNREACHABLE();
}
bool KMemoryManager::Impl::ProcessOptimizedAllocation(PAddr block, size_t num_pages,
u8 fill_pattern) {
UNREACHABLE();
}
size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {
const size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
const size_t optimize_map_size =

View File

@@ -21,11 +21,8 @@ namespace Kernel {
class KPageGroup;
class KMemoryManager final {
class KMemoryManager {
public:
YUZU_NON_COPYABLE(KMemoryManager);
YUZU_NON_MOVEABLE(KMemoryManager);
enum class Pool : u32 {
Application = 0,
Applet = 1,
@@ -45,16 +42,85 @@ public:
enum class Direction : u32 {
FromFront = 0,
FromBack = 1,
Shift = 0,
Mask = (0xF << Shift),
};
explicit KMemoryManager(Core::System& system_);
static constexpr size_t MaxManagerCount = 10;
explicit KMemoryManager(Core::System& system);
void Initialize(VAddr management_region, size_t management_region_size);
constexpr size_t GetSize(Pool pool) const {
Result InitializeOptimizedMemory(u64 process_id, Pool pool);
void FinalizeOptimizedMemory(u64 process_id, Pool pool);
PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option);
Result AllocateForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id,
u8 fill_pattern);
Pool GetPool(PAddr address) const {
return this->GetManager(address).GetPool();
}
void Open(PAddr address, size_t num_pages) {
// Repeatedly open references until we've done so for all pages.
while (num_pages) {
auto& manager = this->GetManager(address);
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
{
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
manager.Open(address, cur_pages);
}
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
void OpenFirst(PAddr address, size_t num_pages) {
// Repeatedly open references until we've done so for all pages.
while (num_pages) {
auto& manager = this->GetManager(address);
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
{
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
manager.OpenFirst(address, cur_pages);
}
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
void Close(PAddr address, size_t num_pages) {
// Repeatedly close references until we've done so for all pages.
while (num_pages) {
auto& manager = this->GetManager(address);
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
{
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
manager.Close(address, cur_pages);
}
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
size_t GetSize() {
size_t total = 0;
for (size_t i = 0; i < m_num_managers; i++) {
total += m_managers[i].GetSize();
}
return total;
}
size_t GetSize(Pool pool) {
constexpr Direction GetSizeDirection = Direction::FromFront;
size_t total = 0;
for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
@@ -64,18 +130,36 @@ public:
return total;
}
PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option);
Result AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id,
u8 fill_pattern);
size_t GetFreeSize() {
size_t total = 0;
for (size_t i = 0; i < m_num_managers; i++) {
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(m_managers[i].GetPool())]);
total += m_managers[i].GetFreeSize();
}
return total;
}
static constexpr size_t MaxManagerCount = 10;
size_t GetFreeSize(Pool pool) {
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
void Close(PAddr address, size_t num_pages);
void Close(const KPageGroup& pg);
constexpr Direction GetSizeDirection = Direction::FromFront;
size_t total = 0;
for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
manager = this->GetNextManager(manager, GetSizeDirection)) {
total += manager->GetFreeSize();
}
return total;
}
void Open(PAddr address, size_t num_pages);
void Open(const KPageGroup& pg);
void DumpFreeList(Pool pool) {
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
constexpr Direction DumpDirection = Direction::FromFront;
for (auto* manager = this->GetFirstManager(pool, DumpDirection); manager != nullptr;
manager = this->GetNextManager(manager, DumpDirection)) {
manager->DumpFreeList();
}
}
public:
static size_t CalculateManagementOverheadSize(size_t region_size) {
@@ -88,14 +172,13 @@ public:
}
static constexpr Pool GetPool(u32 option) {
return static_cast<Pool>((static_cast<u32>(option) & static_cast<u32>(Pool::Mask)) >>
return static_cast<Pool>((option & static_cast<u32>(Pool::Mask)) >>
static_cast<u32>(Pool::Shift));
}
static constexpr Direction GetDirection(u32 option) {
return static_cast<Direction>(
(static_cast<u32>(option) & static_cast<u32>(Direction::Mask)) >>
static_cast<u32>(Direction::Shift));
return static_cast<Direction>((option & static_cast<u32>(Direction::Mask)) >>
static_cast<u32>(Direction::Shift));
}
static constexpr std::tuple<Pool, Direction> DecodeOption(u32 option) {
@@ -103,74 +186,88 @@ public:
}
private:
class Impl final {
class Impl {
public:
YUZU_NON_COPYABLE(Impl);
YUZU_NON_MOVEABLE(Impl);
static size_t CalculateManagementOverheadSize(size_t region_size);
static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
}
public:
Impl() = default;
~Impl() = default;
size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end,
Pool p);
VAddr AllocateBlock(s32 index, bool random) {
return heap.AllocateBlock(index, random);
PAddr AllocateBlock(s32 index, bool random) {
return m_heap.AllocateBlock(index, random);
}
void Free(VAddr addr, size_t num_pages) {
heap.Free(addr, num_pages);
PAddr AllocateAligned(s32 index, size_t num_pages, size_t align_pages) {
return m_heap.AllocateAligned(index, num_pages, align_pages);
}
void Free(PAddr addr, size_t num_pages) {
m_heap.Free(addr, num_pages);
}
void SetInitialUsedHeapSize(size_t reserved_size) {
heap.SetInitialUsedSize(reserved_size);
m_heap.SetInitialUsedSize(reserved_size);
}
void InitializeOptimizedMemory() {
UNIMPLEMENTED();
}
void TrackUnoptimizedAllocation(PAddr block, size_t num_pages);
void TrackOptimizedAllocation(PAddr block, size_t num_pages);
bool ProcessOptimizedAllocation(PAddr block, size_t num_pages, u8 fill_pattern);
constexpr Pool GetPool() const {
return pool;
return m_pool;
}
constexpr size_t GetSize() const {
return heap.GetSize();
return m_heap.GetSize();
}
constexpr PAddr GetEndAddress() const {
return m_heap.GetEndAddress();
}
constexpr VAddr GetAddress() const {
return heap.GetAddress();
size_t GetFreeSize() const {
return m_heap.GetFreeSize();
}
constexpr VAddr GetEndAddress() const {
return heap.GetEndAddress();
void DumpFreeList() const {
UNIMPLEMENTED();
}
constexpr size_t GetPageOffset(PAddr address) const {
return heap.GetPageOffset(address);
return m_heap.GetPageOffset(address);
}
constexpr size_t GetPageOffsetToEnd(PAddr address) const {
return heap.GetPageOffsetToEnd(address);
return m_heap.GetPageOffsetToEnd(address);
}
constexpr void SetNext(Impl* n) {
next = n;
m_next = n;
}
constexpr void SetPrev(Impl* n) {
prev = n;
m_prev = n;
}
constexpr Impl* GetNext() const {
return next;
return m_next;
}
constexpr Impl* GetPrev() const {
return prev;
return m_prev;
}
void OpenFirst(PAddr address, size_t num_pages) {
size_t index = this->GetPageOffset(address);
const size_t end = index + num_pages;
while (index < end) {
const RefCount ref_count = (++page_reference_counts[index]);
const RefCount ref_count = (++m_page_reference_counts[index]);
ASSERT(ref_count == 1);
index++;
@@ -181,7 +278,7 @@ private:
size_t index = this->GetPageOffset(address);
const size_t end = index + num_pages;
while (index < end) {
const RefCount ref_count = (++page_reference_counts[index]);
const RefCount ref_count = (++m_page_reference_counts[index]);
ASSERT(ref_count > 1);
index++;
@@ -195,8 +292,8 @@ private:
size_t free_start = 0;
size_t free_count = 0;
while (index < end) {
ASSERT(page_reference_counts[index] > 0);
const RefCount ref_count = (--page_reference_counts[index]);
ASSERT(m_page_reference_counts[index] > 0);
const RefCount ref_count = (--m_page_reference_counts[index]);
// Keep track of how many zero refcounts we see in a row, to minimize calls to free.
if (ref_count == 0) {
@@ -208,7 +305,7 @@ private:
}
} else {
if (free_count > 0) {
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
this->Free(m_heap.GetAddress() + free_start * PageSize, free_count);
free_count = 0;
}
}
@@ -217,44 +314,36 @@ private:
}
if (free_count > 0) {
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
this->Free(m_heap.GetAddress() + free_start * PageSize, free_count);
}
}
static size_t CalculateManagementOverheadSize(size_t region_size);
static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
}
private:
using RefCount = u16;
KPageHeap heap;
std::vector<RefCount> page_reference_counts;
VAddr management_region{};
Pool pool{};
Impl* next{};
Impl* prev{};
KPageHeap m_heap;
std::vector<RefCount> m_page_reference_counts;
VAddr m_management_region{};
Pool m_pool{};
Impl* m_next{};
Impl* m_prev{};
};
private:
Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) {
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
Impl& GetManager(PAddr address) {
return m_managers[m_memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
}
const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const {
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
const Impl& GetManager(PAddr address) const {
return m_managers[m_memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
}
constexpr Impl* GetFirstManager(Pool pool, Direction dir) const {
return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)]
: pool_managers_head[static_cast<size_t>(pool)];
constexpr Impl* GetFirstManager(Pool pool, Direction dir) {
return dir == Direction::FromBack ? m_pool_managers_tail[static_cast<size_t>(pool)]
: m_pool_managers_head[static_cast<size_t>(pool)];
}
constexpr Impl* GetNextManager(Impl* cur, Direction dir) const {
constexpr Impl* GetNextManager(Impl* cur, Direction dir) {
if (dir == Direction::FromBack) {
return cur->GetPrev();
} else {
@@ -263,15 +352,21 @@ private:
}
Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir,
bool random);
bool unoptimized, bool random);
private:
Core::System& system;
std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks;
std::array<Impl*, MaxManagerCount> pool_managers_head{};
std::array<Impl*, MaxManagerCount> pool_managers_tail{};
std::array<Impl, MaxManagerCount> managers;
size_t num_managers{};
template <typename T>
using PoolArray = std::array<T, static_cast<size_t>(Pool::Count)>;
Core::System& m_system;
const KMemoryLayout& m_memory_layout;
PoolArray<KLightLock> m_pool_locks;
std::array<Impl*, MaxManagerCount> m_pool_managers_head{};
std::array<Impl*, MaxManagerCount> m_pool_managers_tail{};
std::array<Impl, MaxManagerCount> m_managers;
size_t m_num_managers{};
PoolArray<u64> m_optimized_process_ids{};
PoolArray<bool> m_has_optimized_process{};
};
} // namespace Kernel

View File

@@ -142,32 +142,38 @@ private:
} // namespace impl
constexpr auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue();
constexpr auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2);
constexpr auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2);
constexpr inline auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue();
constexpr inline auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2);
constexpr inline auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2);
static_assert(KMemoryRegionType_Kernel.GetValue() == 0x1);
static_assert(KMemoryRegionType_Dram.GetValue() == 0x2);
constexpr auto KMemoryRegionType_DramKernelBase =
// constexpr inline auto KMemoryRegionType_CoreLocalRegion =
// KMemoryRegionType_None.DeriveInitial(2).Finalize();
// static_assert(KMemoryRegionType_CoreLocalRegion.GetValue() == 0x4);
constexpr inline auto KMemoryRegionType_DramKernelBase =
KMemoryRegionType_Dram.DeriveSparse(0, 3, 0)
.SetAttribute(KMemoryRegionAttr_NoUserMap)
.SetAttribute(KMemoryRegionAttr_CarveoutProtected);
constexpr auto KMemoryRegionType_DramReservedBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 1);
constexpr auto KMemoryRegionType_DramHeapBase =
constexpr inline auto KMemoryRegionType_DramReservedBase =
KMemoryRegionType_Dram.DeriveSparse(0, 3, 1);
constexpr inline auto KMemoryRegionType_DramHeapBase =
KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr_LinearMapped);
static_assert(KMemoryRegionType_DramKernelBase.GetValue() ==
(0xE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16));
static_assert(KMemoryRegionType_DramHeapBase.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped));
constexpr auto KMemoryRegionType_DramKernelCode =
constexpr inline auto KMemoryRegionType_DramKernelCode =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0);
constexpr auto KMemoryRegionType_DramKernelSlab =
constexpr inline auto KMemoryRegionType_DramKernelSlab =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1);
constexpr auto KMemoryRegionType_DramKernelPtHeap =
constexpr inline auto KMemoryRegionType_DramKernelPtHeap =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute(
KMemoryRegionAttr_LinearMapped);
constexpr auto KMemoryRegionType_DramKernelInitPt =
constexpr inline auto KMemoryRegionType_DramKernelInitPt =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute(
KMemoryRegionAttr_LinearMapped);
static_assert(KMemoryRegionType_DramKernelCode.GetValue() ==
@@ -181,32 +187,40 @@ static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
(0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_LinearMapped));
constexpr auto KMemoryRegionType_DramReservedEarly =
constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory =
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(
KMemoryRegionAttr_LinearMapped);
static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() ==
(0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_LinearMapped));
constexpr inline auto KMemoryRegionType_DramReservedEarly =
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
static_assert(KMemoryRegionType_DramReservedEarly.GetValue() ==
(0x16 | KMemoryRegionAttr_NoUserMap));
constexpr auto KMemoryRegionType_KernelTraceBuffer =
constexpr inline auto KMemoryRegionType_KernelTraceBuffer =
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 0)
.SetAttribute(KMemoryRegionAttr_LinearMapped)
.SetAttribute(KMemoryRegionAttr_UserReadOnly);
constexpr auto KMemoryRegionType_OnMemoryBootImage =
constexpr inline auto KMemoryRegionType_OnMemoryBootImage =
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1);
constexpr auto KMemoryRegionType_DTB = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2);
constexpr inline auto KMemoryRegionType_DTB =
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2);
static_assert(KMemoryRegionType_KernelTraceBuffer.GetValue() ==
(0xD6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_UserReadOnly));
static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156);
static_assert(KMemoryRegionType_DTB.GetValue() == 0x256);
constexpr auto KMemoryRegionType_DramPoolPartition =
constexpr inline auto KMemoryRegionType_DramPoolPartition =
KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
constexpr auto KMemoryRegionType_DramPoolManagement =
constexpr inline auto KMemoryRegionType_DramPoolManagement =
KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(
KMemoryRegionAttr_CarveoutProtected);
constexpr auto KMemoryRegionType_DramUserPool =
constexpr inline auto KMemoryRegionType_DramUserPool =
KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition();
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
(0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
@@ -214,11 +228,13 @@ static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
(0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
constexpr auto KMemoryRegionType_DramApplicationPool = KMemoryRegionType_DramUserPool.Derive(4, 0);
constexpr auto KMemoryRegionType_DramAppletPool = KMemoryRegionType_DramUserPool.Derive(4, 1);
constexpr auto KMemoryRegionType_DramSystemNonSecurePool =
constexpr inline auto KMemoryRegionType_DramApplicationPool =
KMemoryRegionType_DramUserPool.Derive(4, 0);
constexpr inline auto KMemoryRegionType_DramAppletPool =
KMemoryRegionType_DramUserPool.Derive(4, 1);
constexpr inline auto KMemoryRegionType_DramSystemNonSecurePool =
KMemoryRegionType_DramUserPool.Derive(4, 2);
constexpr auto KMemoryRegionType_DramSystemPool =
constexpr inline auto KMemoryRegionType_DramSystemPool =
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
(0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
@@ -230,50 +246,55 @@ static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
(0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_CarveoutProtected));
constexpr auto KMemoryRegionType_VirtualDramHeapBase = KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
constexpr auto KMemoryRegionType_VirtualDramKernelPtHeap =
constexpr inline auto KMemoryRegionType_VirtualDramHeapBase =
KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap =
KMemoryRegionType_Dram.DeriveSparse(1, 3, 1);
constexpr auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
KMemoryRegionType_Dram.DeriveSparse(1, 3, 2);
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
// UNUSED: .DeriveSparse(2, 2, 0);
constexpr auto KMemoryRegionType_VirtualDramUnknownDebug =
constexpr inline auto KMemoryRegionType_VirtualDramUnknownDebug =
KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
constexpr auto KMemoryRegionType_VirtualDramKernelInitPt =
constexpr inline auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
KMemoryRegionType_Dram.DeriveSparse(3, 1, 0);
static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x62));
constexpr inline auto KMemoryRegionType_VirtualDramKernelInitPt =
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
constexpr auto KMemoryRegionType_VirtualDramPoolManagement =
constexpr inline auto KMemoryRegionType_VirtualDramPoolManagement =
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
constexpr auto KMemoryRegionType_VirtualDramUserPool =
constexpr inline auto KMemoryRegionType_VirtualDramUserPool =
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A);
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A);
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A);
// NOTE: For unknown reason, the pools are derived out-of-order here. It's worth eventually trying
// to understand why Nintendo made this choice.
// NOTE: For unknown reason, the pools are derived out-of-order here.
// It's worth eventually trying to understand why Nintendo made this choice.
// UNUSED: .Derive(6, 0);
// UNUSED: .Derive(6, 1);
constexpr auto KMemoryRegionType_VirtualDramAppletPool =
constexpr inline auto KMemoryRegionType_VirtualDramAppletPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 2);
constexpr auto KMemoryRegionType_VirtualDramApplicationPool =
constexpr inline auto KMemoryRegionType_VirtualDramApplicationPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 3);
constexpr auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
constexpr inline auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 4);
constexpr auto KMemoryRegionType_VirtualDramSystemPool =
constexpr inline auto KMemoryRegionType_VirtualDramSystemPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 5);
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A);
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A);
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A);
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A);
constexpr auto KMemoryRegionType_ArchDeviceBase =
constexpr inline auto KMemoryRegionType_ArchDeviceBase =
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
constexpr auto KMemoryRegionType_BoardDeviceBase =
constexpr inline auto KMemoryRegionType_BoardDeviceBase =
KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly();
static_assert(KMemoryRegionType_ArchDeviceBase.GetValue() == 0x5);
static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5);
@@ -284,7 +305,7 @@ static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5);
#error "Unimplemented"
#else
// Default to no architecture devices.
constexpr auto NumArchitectureDeviceRegions = 0;
constexpr inline auto NumArchitectureDeviceRegions = 0;
#endif
static_assert(NumArchitectureDeviceRegions >= 0);
@@ -292,34 +313,35 @@ static_assert(NumArchitectureDeviceRegions >= 0);
#include "core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc"
#else
// Default to no board devices.
constexpr auto NumBoardDeviceRegions = 0;
constexpr inline auto NumBoardDeviceRegions = 0;
#endif
static_assert(NumBoardDeviceRegions >= 0);
constexpr auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0);
constexpr auto KMemoryRegionType_KernelStack = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1);
constexpr auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2);
constexpr auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3);
constexpr inline auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0);
constexpr inline auto KMemoryRegionType_KernelStack =
KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1);
constexpr inline auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2);
constexpr inline auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3);
static_assert(KMemoryRegionType_KernelCode.GetValue() == 0x19);
static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29);
static_assert(KMemoryRegionType_KernelMisc.GetValue() == 0x49);
static_assert(KMemoryRegionType_KernelSlab.GetValue() == 0x89);
constexpr auto KMemoryRegionType_KernelMiscDerivedBase =
constexpr inline auto KMemoryRegionType_KernelMiscDerivedBase =
KMemoryRegionType_KernelMisc.DeriveTransition();
static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149);
// UNUSED: .Derive(7, 0);
constexpr auto KMemoryRegionType_KernelMiscMainStack =
constexpr inline auto KMemoryRegionType_KernelMiscMainStack =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1);
constexpr auto KMemoryRegionType_KernelMiscMappedDevice =
constexpr inline auto KMemoryRegionType_KernelMiscMappedDevice =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2);
constexpr auto KMemoryRegionType_KernelMiscExceptionStack =
constexpr inline auto KMemoryRegionType_KernelMiscExceptionStack =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3);
constexpr auto KMemoryRegionType_KernelMiscUnknownDebug =
constexpr inline auto KMemoryRegionType_KernelMiscUnknownDebug =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4);
// UNUSED: .Derive(7, 5);
constexpr auto KMemoryRegionType_KernelMiscIdleStack =
constexpr inline auto KMemoryRegionType_KernelMiscIdleStack =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6);
static_assert(KMemoryRegionType_KernelMiscMainStack.GetValue() == 0xB49);
static_assert(KMemoryRegionType_KernelMiscMappedDevice.GetValue() == 0xD49);
@@ -327,7 +349,8 @@ static_assert(KMemoryRegionType_KernelMiscExceptionStack.GetValue() == 0x1349);
static_assert(KMemoryRegionType_KernelMiscUnknownDebug.GetValue() == 0x1549);
static_assert(KMemoryRegionType_KernelMiscIdleStack.GetValue() == 0x2349);
constexpr auto KMemoryRegionType_KernelTemp = KMemoryRegionType_Kernel.Advance(2).Derive(2, 0);
constexpr inline auto KMemoryRegionType_KernelTemp =
KMemoryRegionType_Kernel.Advance(2).Derive(2, 0);
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
@@ -335,6 +358,8 @@ constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelPtHeap;
} else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
return KMemoryRegionType_VirtualDramUnknownDebug;
} else {

View File

@@ -16,107 +16,126 @@
namespace Kernel {
class KPageBitmap {
private:
public:
class RandomBitGenerator {
private:
Common::TinyMT rng{};
u32 entropy{};
u32 bits_available{};
private:
void RefreshEntropy() {
entropy = rng.GenerateRandomU32();
bits_available = static_cast<u32>(Common::BitSize<decltype(entropy)>());
}
bool GenerateRandomBit() {
if (bits_available == 0) {
this->RefreshEntropy();
}
const bool rnd_bit = (entropy & 1) != 0;
entropy >>= 1;
--bits_available;
return rnd_bit;
}
public:
RandomBitGenerator() {
rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
m_rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
}
std::size_t SelectRandomBit(u64 bitmap) {
u64 SelectRandomBit(u64 bitmap) {
u64 selected = 0;
u64 cur_num_bits = Common::BitSize<decltype(bitmap)>() / 2;
u64 cur_mask = (1ULL << cur_num_bits) - 1;
for (size_t cur_num_bits = Common::BitSize<decltype(bitmap)>() / 2; cur_num_bits != 0;
cur_num_bits /= 2) {
const u64 high = (bitmap >> cur_num_bits);
const u64 low = (bitmap & (~(UINT64_C(0xFFFFFFFFFFFFFFFF) << cur_num_bits)));
while (cur_num_bits) {
const u64 low = (bitmap >> 0) & cur_mask;
const u64 high = (bitmap >> cur_num_bits) & cur_mask;
bool choose_low;
if (high == 0) {
// If only low val is set, choose low.
choose_low = true;
} else if (low == 0) {
// If only high val is set, choose high.
choose_low = false;
} else {
// If both are set, choose random.
choose_low = this->GenerateRandomBit();
}
// If we chose low, proceed with low.
if (choose_low) {
bitmap = low;
selected += 0;
} else {
// Choose high if we have high and (don't have low or select high randomly).
if (high && (low == 0 || this->GenerateRandomBit())) {
bitmap = high;
selected += cur_num_bits;
} else {
bitmap = low;
selected += 0;
}
// Proceed.
cur_num_bits /= 2;
cur_mask >>= cur_num_bits;
}
return selected;
}
u64 GenerateRandom(u64 max) {
// Determine the number of bits we need.
const u64 bits_needed = 1 + (Common::BitSize<decltype(max)>() - std::countl_zero(max));
// Generate a random value of the desired bitwidth.
const u64 rnd = this->GenerateRandomBits(static_cast<u32>(bits_needed));
// Adjust the value to be in range.
return rnd - ((rnd / max) * max);
}
private:
void RefreshEntropy() {
m_entropy = m_rng.GenerateRandomU32();
m_bits_available = static_cast<u32>(Common::BitSize<decltype(m_entropy)>());
}
bool GenerateRandomBit() {
if (m_bits_available == 0) {
this->RefreshEntropy();
}
const bool rnd_bit = (m_entropy & 1) != 0;
m_entropy >>= 1;
--m_bits_available;
return rnd_bit;
}
u64 GenerateRandomBits(u32 num_bits) {
u64 result = 0;
// Iteratively add random bits to our result.
while (num_bits > 0) {
// Ensure we have random bits to take from.
if (m_bits_available == 0) {
this->RefreshEntropy();
}
// Determine how many bits to take this round.
const auto cur_bits = std::min(num_bits, m_bits_available);
// Generate mask for our current bits.
const u64 mask = (static_cast<u64>(1) << cur_bits) - 1;
// Add bits to output from our entropy.
result <<= cur_bits;
result |= (m_entropy & mask);
// Remove bits from our entropy.
m_entropy >>= cur_bits;
m_bits_available -= cur_bits;
// Advance.
num_bits -= cur_bits;
}
return result;
}
private:
Common::TinyMT m_rng;
u32 m_entropy{};
u32 m_bits_available{};
};
public:
static constexpr std::size_t MaxDepth = 4;
private:
std::array<u64*, MaxDepth> bit_storages{};
RandomBitGenerator rng{};
std::size_t num_bits{};
std::size_t used_depths{};
static constexpr size_t MaxDepth = 4;
public:
KPageBitmap() = default;
constexpr std::size_t GetNumBits() const {
return num_bits;
constexpr size_t GetNumBits() const {
return m_num_bits;
}
constexpr s32 GetHighestDepthIndex() const {
return static_cast<s32>(used_depths) - 1;
return static_cast<s32>(m_used_depths) - 1;
}
u64* Initialize(u64* storage, std::size_t size) {
u64* Initialize(u64* storage, size_t size) {
// Initially, everything is un-set.
num_bits = 0;
m_num_bits = 0;
// Calculate the needed bitmap depth.
used_depths = static_cast<std::size_t>(GetRequiredDepth(size));
ASSERT(used_depths <= MaxDepth);
m_used_depths = static_cast<size_t>(GetRequiredDepth(size));
ASSERT(m_used_depths <= MaxDepth);
// Set the bitmap pointers.
for (s32 depth = this->GetHighestDepthIndex(); depth >= 0; depth--) {
bit_storages[depth] = storage;
m_bit_storages[depth] = storage;
size = Common::AlignUp(size, Common::BitSize<u64>()) / Common::BitSize<u64>();
storage += size;
m_end_storages[depth] = storage;
}
return storage;
@@ -128,19 +147,19 @@ public:
if (random) {
do {
const u64 v = bit_storages[depth][offset];
const u64 v = m_bit_storages[depth][offset];
if (v == 0) {
// If depth is bigger than zero, then a previous level indicated a block was
// free.
ASSERT(depth == 0);
return -1;
}
offset = offset * Common::BitSize<u64>() + rng.SelectRandomBit(v);
offset = offset * Common::BitSize<u64>() + m_rng.SelectRandomBit(v);
++depth;
} while (depth < static_cast<s32>(used_depths));
} while (depth < static_cast<s32>(m_used_depths));
} else {
do {
const u64 v = bit_storages[depth][offset];
const u64 v = m_bit_storages[depth][offset];
if (v == 0) {
// If depth is bigger than zero, then a previous level indicated a block was
// free.
@@ -149,28 +168,69 @@ public:
}
offset = offset * Common::BitSize<u64>() + std::countr_zero(v);
++depth;
} while (depth < static_cast<s32>(used_depths));
} while (depth < static_cast<s32>(m_used_depths));
}
return static_cast<s64>(offset);
}
void SetBit(std::size_t offset) {
s64 FindFreeRange(size_t count) {
// Check that it is possible to find a range.
const u64* const storage_start = m_bit_storages[m_used_depths - 1];
const u64* const storage_end = m_end_storages[m_used_depths - 1];
// If we don't have a storage to iterate (or want more blocks than fit in a single storage),
// we can't find a free range.
if (!(storage_start < storage_end && count <= Common::BitSize<u64>())) {
return -1;
}
// Walk the storages to select a random free range.
const size_t options_per_storage = std::max<size_t>(Common::BitSize<u64>() / count, 1);
const size_t num_entries = std::max<size_t>(storage_end - storage_start, 1);
const u64 free_mask = (static_cast<u64>(1) << count) - 1;
size_t num_valid_options = 0;
s64 chosen_offset = -1;
for (size_t storage_index = 0; storage_index < num_entries; ++storage_index) {
u64 storage = storage_start[storage_index];
for (size_t option = 0; option < options_per_storage; ++option) {
if ((storage & free_mask) == free_mask) {
// We've found a new valid option.
++num_valid_options;
// Select the Kth valid option with probability 1/K. This leads to an overall
// uniform distribution.
if (num_valid_options == 1 || m_rng.GenerateRandom(num_valid_options) == 0) {
// This is our first option, so select it.
chosen_offset = storage_index * Common::BitSize<u64>() + option * count;
}
}
storage >>= count;
}
}
// Return the random offset we chose.*/
return chosen_offset;
}
void SetBit(size_t offset) {
this->SetBit(this->GetHighestDepthIndex(), offset);
num_bits++;
m_num_bits++;
}
void ClearBit(std::size_t offset) {
void ClearBit(size_t offset) {
this->ClearBit(this->GetHighestDepthIndex(), offset);
num_bits--;
m_num_bits--;
}
bool ClearRange(std::size_t offset, std::size_t count) {
bool ClearRange(size_t offset, size_t count) {
s32 depth = this->GetHighestDepthIndex();
u64* bits = bit_storages[depth];
std::size_t bit_ind = offset / Common::BitSize<u64>();
if (count < Common::BitSize<u64>()) {
const std::size_t shift = offset % Common::BitSize<u64>();
u64* bits = m_bit_storages[depth];
size_t bit_ind = offset / Common::BitSize<u64>();
if (count < Common::BitSize<u64>()) [[likely]] {
const size_t shift = offset % Common::BitSize<u64>();
ASSERT(shift + count <= Common::BitSize<u64>());
// Check that all the bits are set.
const u64 mask = ((u64(1) << count) - 1) << shift;
@@ -189,8 +249,8 @@ public:
ASSERT(offset % Common::BitSize<u64>() == 0);
ASSERT(count % Common::BitSize<u64>() == 0);
// Check that all the bits are set.
std::size_t remaining = count;
std::size_t i = 0;
size_t remaining = count;
size_t i = 0;
do {
if (bits[bit_ind + i++] != ~u64(0)) {
return false;
@@ -209,18 +269,18 @@ public:
} while (remaining > 0);
}
num_bits -= count;
m_num_bits -= count;
return true;
}
private:
void SetBit(s32 depth, std::size_t offset) {
void SetBit(s32 depth, size_t offset) {
while (depth >= 0) {
std::size_t ind = offset / Common::BitSize<u64>();
std::size_t which = offset % Common::BitSize<u64>();
size_t ind = offset / Common::BitSize<u64>();
size_t which = offset % Common::BitSize<u64>();
const u64 mask = u64(1) << which;
u64* bit = std::addressof(bit_storages[depth][ind]);
u64* bit = std::addressof(m_bit_storages[depth][ind]);
u64 v = *bit;
ASSERT((v & mask) == 0);
*bit = v | mask;
@@ -232,13 +292,13 @@ private:
}
}
void ClearBit(s32 depth, std::size_t offset) {
void ClearBit(s32 depth, size_t offset) {
while (depth >= 0) {
std::size_t ind = offset / Common::BitSize<u64>();
std::size_t which = offset % Common::BitSize<u64>();
size_t ind = offset / Common::BitSize<u64>();
size_t which = offset % Common::BitSize<u64>();
const u64 mask = u64(1) << which;
u64* bit = std::addressof(bit_storages[depth][ind]);
u64* bit = std::addressof(m_bit_storages[depth][ind]);
u64 v = *bit;
ASSERT((v & mask) != 0);
v &= ~mask;
@@ -252,7 +312,7 @@ private:
}
private:
static constexpr s32 GetRequiredDepth(std::size_t region_size) {
static constexpr s32 GetRequiredDepth(size_t region_size) {
s32 depth = 0;
while (true) {
region_size /= Common::BitSize<u64>();
@@ -264,8 +324,8 @@ private:
}
public:
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size) {
std::size_t overhead_bits = 0;
static constexpr size_t CalculateManagementOverheadSize(size_t region_size) {
size_t overhead_bits = 0;
for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) {
region_size =
Common::AlignUp(region_size, Common::BitSize<u64>()) / Common::BitSize<u64>();
@@ -273,6 +333,13 @@ public:
}
return overhead_bits * sizeof(u64);
}
private:
std::array<u64*, MaxDepth> m_bit_storages{};
std::array<u64*, MaxDepth> m_end_storages{};
RandomBitGenerator m_rng;
size_t m_num_bits{};
size_t m_used_depths{};
};
} // namespace Kernel

View File

@@ -11,6 +11,16 @@
namespace Kernel {
class KernelCore;
class KPageBufferSlabHeap : protected impl::KSlabHeapImpl {
public:
static constexpr size_t BufferSize = PageSize;
public:
void Initialize(Core::System& system);
};
class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
public:
explicit KPageBuffer(KernelCore&) {}
@@ -21,8 +31,6 @@ public:
private:
[[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{};
};
static_assert(sizeof(KPageBuffer) == PageSize);
static_assert(alignof(KPageBuffer) == PageSize);
static_assert(sizeof(KPageBuffer) == KPageBufferSlabHeap::BufferSize);
} // namespace Kernel

View File

@@ -5,6 +5,7 @@
#include <list>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory_types.h"
@@ -12,6 +13,89 @@
namespace Kernel {
class KPageGroup;
class KBlockInfo {
private:
friend class KPageGroup;
public:
constexpr KBlockInfo() = default;
constexpr void Initialize(PAddr addr, size_t np) {
ASSERT(Common::IsAligned(addr, PageSize));
ASSERT(static_cast<u32>(np) == np);
m_page_index = static_cast<u32>(addr) / PageSize;
m_num_pages = static_cast<u32>(np);
}
constexpr PAddr GetAddress() const {
return m_page_index * PageSize;
}
constexpr size_t GetNumPages() const {
return m_num_pages;
}
constexpr size_t GetSize() const {
return this->GetNumPages() * PageSize;
}
constexpr PAddr GetEndAddress() const {
return (m_page_index + m_num_pages) * PageSize;
}
constexpr PAddr GetLastAddress() const {
return this->GetEndAddress() - 1;
}
constexpr KBlockInfo* GetNext() const {
return m_next;
}
constexpr bool IsEquivalentTo(const KBlockInfo& rhs) const {
return m_page_index == rhs.m_page_index && m_num_pages == rhs.m_num_pages;
}
constexpr bool operator==(const KBlockInfo& rhs) const {
return this->IsEquivalentTo(rhs);
}
constexpr bool operator!=(const KBlockInfo& rhs) const {
return !(*this == rhs);
}
constexpr bool IsStrictlyBefore(PAddr addr) const {
const PAddr end = this->GetEndAddress();
if (m_page_index != 0 && end == 0) {
return false;
}
return end < addr;
}
constexpr bool operator<(PAddr addr) const {
return this->IsStrictlyBefore(addr);
}
constexpr bool TryConcatenate(PAddr addr, size_t np) {
if (addr != 0 && addr == this->GetEndAddress()) {
m_num_pages += static_cast<u32>(np);
return true;
}
return false;
}
private:
constexpr void SetNext(KBlockInfo* next) {
m_next = next;
}
private:
KBlockInfo* m_next{};
u32 m_page_index{};
u32 m_num_pages{};
};
static_assert(sizeof(KBlockInfo) <= 0x10);
class KPageGroup final {
public:
class Node final {
@@ -92,6 +176,8 @@ public:
return nodes.empty();
}
void Finalize() {}
private:
std::list<Node> nodes;
};

View File

@@ -44,11 +44,11 @@ size_t KPageHeap::GetNumFreePages() const {
return num_free;
}
PAddr KPageHeap::AllocateBlock(s32 index, bool random) {
PAddr KPageHeap::AllocateByLinearSearch(s32 index) {
const size_t needed_size = m_blocks[index].GetSize();
for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) {
if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) {
if (const PAddr addr = m_blocks[i].PopBlock(false); addr != 0) {
if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) {
this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
}
@@ -59,6 +59,88 @@ PAddr KPageHeap::AllocateBlock(s32 index, bool random) {
return 0;
}
PAddr KPageHeap::AllocateByRandom(s32 index, size_t num_pages, size_t align_pages) {
// Get the size and required alignment.
const size_t needed_size = num_pages * PageSize;
const size_t align_size = align_pages * PageSize;
// Determine meta-alignment of our desired alignment size.
const size_t align_shift = std::countr_zero(align_size);
// Decide on a block to allocate from.
constexpr size_t MinimumPossibleAlignmentsForRandomAllocation = 4;
{
// By default, we'll want to look at all blocks larger than our current one.
s32 max_blocks = static_cast<s32>(m_num_blocks);
// Determine the maximum block we should try to allocate from.
size_t possible_alignments = 0;
for (s32 i = index; i < max_blocks; ++i) {
// Add the possible alignments from blocks at the current size.
possible_alignments += (1 + ((m_blocks[i].GetSize() - needed_size) >> align_shift)) *
m_blocks[i].GetNumFreeBlocks();
// If there are enough possible alignments, we don't need to look at larger blocks.
if (possible_alignments >= MinimumPossibleAlignmentsForRandomAllocation) {
max_blocks = i + 1;
break;
}
}
// If we have any possible alignments which require a larger block, we need to pick one.
if (possible_alignments > 0 && index + 1 < max_blocks) {
// Select a random alignment from the possibilities.
const size_t rnd = m_rng.GenerateRandom(possible_alignments);
// Determine which block corresponds to the random alignment we chose.
possible_alignments = 0;
for (s32 i = index; i < max_blocks; ++i) {
// Add the possible alignments from blocks at the current size.
possible_alignments +=
(1 + ((m_blocks[i].GetSize() - needed_size) >> align_shift)) *
m_blocks[i].GetNumFreeBlocks();
// If the current block gets us to our random choice, use the current block.
if (rnd < possible_alignments) {
index = i;
break;
}
}
}
}
// Pop a block from the index we selected.
if (PAddr addr = m_blocks[index].PopBlock(true); addr != 0) {
// Determine how much size we have left over.
if (const size_t leftover_size = m_blocks[index].GetSize() - needed_size;
leftover_size > 0) {
// Determine how many valid alignments we can have.
const size_t possible_alignments = 1 + (leftover_size >> align_shift);
// Select a random valid alignment.
const size_t random_offset = m_rng.GenerateRandom(possible_alignments) << align_shift;
// Free memory before the random offset.
if (random_offset != 0) {
this->Free(addr, random_offset / PageSize);
}
// Advance our block by the random offset.
addr += random_offset;
// Free memory after our allocated block.
if (random_offset != leftover_size) {
this->Free(addr + needed_size, (leftover_size - random_offset) / PageSize);
}
}
// Return the block we allocated.
return addr;
}
return 0;
}
void KPageHeap::FreeBlock(PAddr block, s32 index) {
do {
block = m_blocks[index++].PushBlock(block);

View File

@@ -14,13 +14,9 @@
namespace Kernel {
class KPageHeap final {
class KPageHeap {
public:
YUZU_NON_COPYABLE(KPageHeap);
YUZU_NON_MOVEABLE(KPageHeap);
KPageHeap() = default;
~KPageHeap() = default;
constexpr PAddr GetAddress() const {
return m_heap_address;
@@ -57,7 +53,20 @@ public:
m_initial_used_size = m_heap_size - free_size - reserved_size;
}
PAddr AllocateBlock(s32 index, bool random);
PAddr AllocateBlock(s32 index, bool random) {
if (random) {
const size_t block_pages = m_blocks[index].GetNumPages();
return this->AllocateByRandom(index, block_pages, block_pages);
} else {
return this->AllocateByLinearSearch(index);
}
}
PAddr AllocateAligned(s32 index, size_t num_pages, size_t align_pages) {
// TODO: linear search support?
return this->AllocateByRandom(index, num_pages, align_pages);
}
void Free(PAddr addr, size_t num_pages);
static size_t CalculateManagementOverheadSize(size_t region_size) {
@@ -68,7 +77,7 @@ public:
static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) {
const size_t target_pages = std::max(num_pages, align_pages);
for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
if (target_pages <= (static_cast<size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
return static_cast<s32>(i);
}
}
@@ -77,7 +86,7 @@ public:
static constexpr s32 GetBlockIndex(size_t num_pages) {
for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) {
if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
if (num_pages >= (static_cast<size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
return i;
}
}
@@ -85,7 +94,7 @@ public:
}
static constexpr size_t GetBlockSize(size_t index) {
return size_t(1) << MemoryBlockPageShifts[index];
return static_cast<size_t>(1) << MemoryBlockPageShifts[index];
}
static constexpr size_t GetBlockNumPages(size_t index) {
@@ -93,13 +102,9 @@ public:
}
private:
class Block final {
class Block {
public:
YUZU_NON_COPYABLE(Block);
YUZU_NON_MOVEABLE(Block);
Block() = default;
~Block() = default;
constexpr size_t GetShift() const {
return m_block_shift;
@@ -201,6 +206,9 @@ private:
};
private:
PAddr AllocateByLinearSearch(s32 index);
PAddr AllocateByRandom(s32 index, size_t num_pages, size_t align_pages);
static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
size_t num_block_shifts);
@@ -209,7 +217,8 @@ private:
size_t m_heap_size{};
size_t m_initial_used_size{};
size_t m_num_blocks{};
std::array<Block, NumMemoryBlockPageShifts> m_blocks{};
std::array<Block, NumMemoryBlockPageShifts> m_blocks;
KPageBitmap::RandomBitGenerator m_rng;
std::vector<u64> m_management_data;
};

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Core {
class System;
@@ -23,7 +24,10 @@ class System;
namespace Kernel {
class KBlockInfoManager;
class KMemoryBlockManager;
class KResourceLimit;
class KSystemResource;
class KPageTable final {
public:
@@ -36,9 +40,9 @@ public:
~KPageTable();
Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
VAddr code_addr, size_t code_size,
KMemoryBlockSlabManager* mem_block_slab_manager,
KMemoryManager::Pool pool);
bool enable_das_merge, bool from_back, KMemoryManager::Pool pool,
VAddr code_addr, size_t code_size, KSystemResource* system_resource,
KResourceLimit* resource_limit);
void Finalize();
@@ -74,12 +78,20 @@ public:
KMemoryState state, KMemoryPermission perm,
PAddr map_addr = 0);
Result LockForMapDeviceAddressSpace(VAddr address, size_t size, KMemoryPermission perm,
bool is_aligned);
Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size);
Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
KMemoryPermission perm, bool is_aligned, bool check_heap);
Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap);
Result UnlockForDeviceAddressSpace(VAddr addr, size_t size);
Result LockForIpcUserBuffer(PAddr* out, VAddr address, size_t size);
Result UnlockForIpcUserBuffer(VAddr address, size_t size);
Result SetupForIpc(VAddr* out_dst_addr, size_t size, VAddr src_addr, KPageTable& src_page_table,
KMemoryPermission test_perm, KMemoryState dst_state, bool send);
Result CleanupForIpcServer(VAddr address, size_t size, KMemoryState dst_state);
Result CleanupForIpcClient(VAddr address, size_t size, KMemoryState dst_state);
Result LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size);
Result UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg);
Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages,
@@ -97,13 +109,54 @@ public:
bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
protected:
struct PageLinkedList {
private:
struct Node {
Node* m_next;
std::array<u8, PageSize - sizeof(Node*)> m_buffer;
};
public:
constexpr PageLinkedList() = default;
void Push(Node* n) {
ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize));
n->m_next = m_root;
m_root = n;
}
void Push(Core::Memory::Memory& memory, VAddr addr) {
this->Push(memory.GetPointer<Node>(addr));
}
Node* Peek() const {
return m_root;
}
Node* Pop() {
Node* const r = m_root;
m_root = r->m_next;
r->m_next = nullptr;
return r;
}
private:
Node* m_root{};
};
static_assert(std::is_trivially_destructible<PageLinkedList>::value);
private:
enum class OperationType : u32 {
Map,
MapGroup,
Unmap,
ChangePermissions,
ChangePermissionsAndRefresh,
Map = 0,
MapFirst = 1,
MapGroup = 2,
Unmap = 3,
ChangePermissions = 4,
ChangePermissionsAndRefresh = 5,
Separate = 6,
};
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
@@ -123,6 +176,7 @@ private:
OperationType operation);
Result Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, OperationType operation,
PAddr map_addr = 0);
void FinalizeUpdate(PageLinkedList* page_list);
VAddr GetRegionAddress(KMemoryState state) const;
size_t GetRegionSize(KMemoryState state) const;
@@ -199,6 +253,18 @@ private:
return *out != 0;
}
Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed, VAddr address,
size_t size, KMemoryPermission test_perm, KMemoryState dst_state);
Result SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_addr,
KMemoryPermission test_perm, KMemoryState dst_state,
KPageTable& src_page_table, bool send);
void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address,
size_t size, KMemoryPermission prot_perm);
// HACK: These will be removed once we automatically manage page reference counts.
void HACK_OpenPages(PAddr phys_addr, size_t num_pages);
void HACK_ClosePages(VAddr virt_addr, size_t num_pages);
mutable KLightLock m_general_lock;
mutable KLightLock m_map_physical_memory_lock;
@@ -254,6 +320,9 @@ public:
constexpr VAddr GetAliasCodeRegionStart() const {
return m_alias_code_region_start;
}
constexpr VAddr GetAliasCodeRegionEnd() const {
return m_alias_code_region_end;
}
constexpr VAddr GetAliasCodeRegionSize() const {
return m_alias_code_region_end - m_alias_code_region_start;
}
@@ -316,6 +385,31 @@ public:
addr + size - 1 <= m_address_space_end - 1;
}
public:
static VAddr GetLinearMappedVirtualAddress(const KMemoryLayout& layout, PAddr addr) {
return layout.GetLinearVirtualAddress(addr);
}
static PAddr GetLinearMappedPhysicalAddress(const KMemoryLayout& layout, VAddr addr) {
return layout.GetLinearPhysicalAddress(addr);
}
static VAddr GetHeapVirtualAddress(const KMemoryLayout& layout, PAddr addr) {
return GetLinearMappedVirtualAddress(layout, addr);
}
static PAddr GetHeapPhysicalAddress(const KMemoryLayout& layout, VAddr addr) {
return GetLinearMappedPhysicalAddress(layout, addr);
}
static VAddr GetPageTableVirtualAddress(const KMemoryLayout& layout, PAddr addr) {
return GetLinearMappedVirtualAddress(layout, addr);
}
static PAddr GetPageTablePhysicalAddress(const KMemoryLayout& layout, VAddr addr) {
return GetLinearMappedPhysicalAddress(layout, addr);
}
private:
constexpr bool IsKernel() const {
return m_is_kernel;
@@ -330,6 +424,24 @@ private:
(addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
}
private:
class KScopedPageTableUpdater {
private:
KPageTable* m_pt{};
PageLinkedList m_ll;
public:
explicit KScopedPageTableUpdater(KPageTable* pt) : m_pt(pt) {}
explicit KScopedPageTableUpdater(KPageTable& pt) : KScopedPageTableUpdater(&pt) {}
~KScopedPageTableUpdater() {
m_pt->FinalizeUpdate(this->GetPageList());
}
PageLinkedList* GetPageList() {
return &m_ll;
}
};
private:
VAddr m_address_space_start{};
VAddr m_address_space_end{};
@@ -347,20 +459,27 @@ private:
VAddr m_alias_code_region_start{};
VAddr m_alias_code_region_end{};
size_t m_mapped_physical_memory_size{};
size_t m_max_heap_size{};
size_t m_max_physical_memory_size{};
size_t m_mapped_physical_memory_size{};
size_t m_mapped_unsafe_physical_memory{};
size_t m_mapped_insecure_memory{};
size_t m_mapped_ipc_server_memory{};
size_t m_address_space_width{};
KMemoryBlockManager m_memory_block_manager;
u32 m_allocate_option{};
bool m_is_kernel{};
bool m_enable_aslr{};
bool m_enable_device_address_space_merge{};
KMemoryBlockSlabManager* m_memory_block_slab_manager{};
KBlockInfoManager* m_block_info_manager{};
KResourceLimit* m_resource_limit{};
u32 m_heap_fill_value{};
u32 m_ipc_fill_value{};
u32 m_stack_fill_value{};
const KMemoryRegion* m_cached_physical_heap_region{};
KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application};

View File

@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "common/common_types.h"
#include "core/hle/kernel/k_dynamic_resource_manager.h"
#include "core/hle/kernel/k_page_table_slab_heap.h"
namespace Kernel {
class KPageTableManager : public KDynamicResourceManager<impl::PageTablePage, true> {
public:
using RefCount = KPageTableSlabHeap::RefCount;
static constexpr size_t PageTableSize = KPageTableSlabHeap::PageTableSize;
public:
KPageTableManager() = default;
void Initialize(KDynamicPageManager* page_allocator, KPageTableSlabHeap* pt_heap) {
m_pt_heap = pt_heap;
static_assert(std::derived_from<KPageTableSlabHeap, DynamicSlabType>);
BaseHeap::Initialize(page_allocator, pt_heap);
}
VAddr Allocate() {
return VAddr(BaseHeap::Allocate());
}
RefCount GetRefCount(VAddr addr) const {
return m_pt_heap->GetRefCount(addr);
}
void Open(VAddr addr, int count) {
return m_pt_heap->Open(addr, count);
}
bool Close(VAddr addr, int count) {
return m_pt_heap->Close(addr, count);
}
bool IsInPageTableHeap(VAddr addr) const {
return m_pt_heap->IsInRange(addr);
}
private:
using BaseHeap = KDynamicResourceManager<impl::PageTablePage, true>;
KPageTableSlabHeap* m_pt_heap{};
};
} // namespace Kernel

View File

@@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/k_dynamic_slab_heap.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
namespace impl {
class PageTablePage {
public:
// Do not initialize anything.
PageTablePage() = default;
private:
std::array<u8, PageSize> m_buffer{};
};
static_assert(sizeof(PageTablePage) == PageSize);
} // namespace impl
class KPageTableSlabHeap : public KDynamicSlabHeap<impl::PageTablePage, true> {
public:
using RefCount = u16;
static constexpr size_t PageTableSize = sizeof(impl::PageTablePage);
static_assert(PageTableSize == PageSize);
public:
KPageTableSlabHeap() = default;
static constexpr size_t CalculateReferenceCountSize(size_t size) {
return (size / PageSize) * sizeof(RefCount);
}
void Initialize(KDynamicPageManager* page_allocator, size_t object_count, RefCount* rc) {
BaseHeap::Initialize(page_allocator, object_count);
this->Initialize(rc);
}
RefCount GetRefCount(VAddr addr) {
ASSERT(this->IsInRange(addr));
return *this->GetRefCountPointer(addr);
}
void Open(VAddr addr, int count) {
ASSERT(this->IsInRange(addr));
*this->GetRefCountPointer(addr) += static_cast<RefCount>(count);
ASSERT(this->GetRefCount(addr) > 0);
}
bool Close(VAddr addr, int count) {
ASSERT(this->IsInRange(addr));
ASSERT(this->GetRefCount(addr) >= count);
*this->GetRefCountPointer(addr) -= static_cast<RefCount>(count);
return this->GetRefCount(addr) == 0;
}
bool IsInPageTableHeap(VAddr addr) const {
return this->IsInRange(addr);
}
private:
void Initialize([[maybe_unused]] RefCount* rc) {
// TODO(bunnei): Use rc once we support kernel virtual memory allocations.
const auto count = this->GetSize() / PageSize;
m_ref_counts.resize(count);
for (size_t i = 0; i < count; i++) {
m_ref_counts[i] = 0;
}
}
RefCount* GetRefCountPointer(VAddr addr) {
return m_ref_counts.data() + ((addr - this->GetAddress()) / PageSize);
}
private:
using BaseHeap = KDynamicSlabHeap<impl::PageTablePage, true>;
std::vector<RefCount> m_ref_counts;
};
} // namespace Kernel

View File

@@ -38,7 +38,7 @@ namespace {
*/
void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) {
const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1));
KThread* thread = KThread::Create(system.Kernel());
SCOPE_EXIT({ thread->Close(); });
@@ -124,7 +124,7 @@ void KProcess::DecrementRunningThreadCount() {
}
u64 KProcess::GetTotalPhysicalMemoryAvailable() {
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemoryMax) +
page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size +
main_thread_stack_size};
if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
@@ -349,8 +349,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
// We currently do not support process-specific system resource
UNIMPLEMENTED_IF(system_resource_size != 0);
KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
code_size + system_resource_size);
KScopedResourceReservation memory_reservation(
resource_limit, LimitableResource::PhysicalMemoryMax, code_size + system_resource_size);
if (!memory_reservation.Succeeded()) {
LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes",
code_size + system_resource_size);
@@ -358,8 +358,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
}
// Initialize proces address space
if (const Result result{page_table.InitializeForProcess(
metadata.GetAddressSpaceType(), false, 0x8000000, code_size,
&kernel.GetApplicationMemoryBlockManager(), KMemoryManager::Pool::Application)};
metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application,
0x8000000, code_size, &kernel.GetSystemSystemResource(), resource_limit)};
result.IsError()) {
R_RETURN(result);
}
@@ -395,6 +395,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
default:
ASSERT(false);
break;
}
// Create TLS region
@@ -406,8 +407,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
AllocateMainThreadStack(stack_size);
resource_limit->Reserve(LimitableResource::Threads, 1);
resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
resource_limit->Reserve(LimitableResource::ThreadCountMax, 1);
resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size);
const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError());
@@ -442,7 +443,7 @@ void KProcess::PrepareForTermination() {
plr_address = 0;
if (resource_limit) {
resource_limit->Release(LimitableResource::PhysicalMemory,
resource_limit->Release(LimitableResource::PhysicalMemoryMax,
main_thread_stack_size + image_size);
}

View File

@@ -159,12 +159,13 @@ KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical
// TODO(bunnei): These values are the system defaults, the limits for service processes are
// lower. These should use the correct limit values.
ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size)
ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, physical_memory_size)
.IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900).IsSuccess());
ASSERT(
resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133).IsSuccess());
return resource_limit;
}

View File

@@ -16,15 +16,8 @@ class CoreTiming;
namespace Kernel {
class KernelCore;
enum class LimitableResource : u32 {
PhysicalMemory = 0,
Threads = 1,
Events = 2,
TransferMemory = 3,
Sessions = 4,
Count,
};
using LimitableResource = Svc::LimitableResource;
constexpr bool IsValidResourceType(LimitableResource type) {
return type < LimitableResource::Count;

View File

@@ -384,7 +384,8 @@ void KScheduler::SwitchThread(KThread* next_thread) {
void KScheduler::ScheduleImpl() {
// First, clear the needs scheduling bool.
m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
m_state.needs_scheduling.store(false, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
// Load the appropriate thread pointers for scheduling.
KThread* const cur_thread{GetCurrentThreadPointer(kernel)};
@@ -400,7 +401,8 @@ void KScheduler::ScheduleImpl() {
// If there aren't, we want to check if the highest priority thread is the same as the current
// thread.
if (highest_priority_thread == cur_thread) {
// If they're the same, then we can just return.
// If they're the same, then we can just issue a memory barrier and return.
std::atomic_thread_fence(std::memory_order_seq_cst);
return;
}
@@ -476,7 +478,8 @@ void KScheduler::ScheduleImplFiber() {
// We failed to successfully do the context switch, and need to retry.
// Clear needs_scheduling.
m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
m_state.needs_scheduling.store(false, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
// Refresh the highest priority thread.
highest_priority_thread = m_state.highest_priority_thread;

View File

@@ -60,6 +60,9 @@ public:
// Release an instance of the lock.
if ((--lock_count) == 0) {
// Perform a memory barrier here.
std::atomic_thread_fence(std::memory_order_seq_cst);
// We're no longer going to hold the lock. Take note of what cores need scheduling.
const u64 cores_needing_scheduling =
SchedulerType::UpdateHighestPriorityThreads(kernel);

View File

@@ -76,7 +76,7 @@ void KSession::OnClientClosed() {
void KSession::PostDestroy(uintptr_t arg) {
// Release the session count resource the owner process holds.
KProcess* owner = reinterpret_cast<KProcess*>(arg);
owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1);
owner->GetResourceLimit()->Release(LimitableResource::SessionCountMax, 1);
owner->Close();
}

View File

@@ -14,7 +14,7 @@ namespace Kernel {
KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
KSharedMemory::~KSharedMemory() {
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size);
}
Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
@@ -35,7 +35,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
KResourceLimit* reslimit = kernel.GetSystemResourceLimit();
// Reserve memory for ourselves.
KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory,
KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemoryMax,
size_);
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
@@ -57,7 +57,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
void KSharedMemory::Finalize() {
// Release the memory reservation.
resource_limit->Release(LimitableResource::PhysicalMemory, size);
resource_limit->Release(LimitableResource::PhysicalMemoryMax, size);
resource_limit->Close();
// Perform inherited finalization.

View File

@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_system_resource.h"
namespace Kernel {
Result KSecureSystemResource::Initialize([[maybe_unused]] size_t size,
[[maybe_unused]] KResourceLimit* resource_limit,
[[maybe_unused]] KMemoryManager::Pool pool) {
// Unimplemented
UNREACHABLE();
}
void KSecureSystemResource::Finalize() {
// Unimplemented
UNREACHABLE();
}
size_t KSecureSystemResource::CalculateRequiredSecureMemorySize(
[[maybe_unused]] size_t size, [[maybe_unused]] KMemoryManager::Pool pool) {
// Unimplemented
UNREACHABLE();
}
} // namespace Kernel

View File

@@ -0,0 +1,137 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_dynamic_resource_manager.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_table_manager.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
// NOTE: Nintendo's implementation does not have the "is_secure_resource" field, and instead uses
// virtual IsSecureResource().
class KSystemResource : public KAutoObject {
KERNEL_AUTOOBJECT_TRAITS(KSystemResource, KAutoObject);
public:
explicit KSystemResource(KernelCore& kernel_) : KAutoObject(kernel_) {}
protected:
void SetSecureResource() {
m_is_secure_resource = true;
}
public:
virtual void Destroy() override {
UNREACHABLE_MSG("KSystemResource::Destroy() was called");
}
bool IsSecureResource() const {
return m_is_secure_resource;
}
void SetManagers(KMemoryBlockSlabManager& mb, KBlockInfoManager& bi, KPageTableManager& pt) {
ASSERT(m_p_memory_block_slab_manager == nullptr);
ASSERT(m_p_block_info_manager == nullptr);
ASSERT(m_p_page_table_manager == nullptr);
m_p_memory_block_slab_manager = std::addressof(mb);
m_p_block_info_manager = std::addressof(bi);
m_p_page_table_manager = std::addressof(pt);
}
const KMemoryBlockSlabManager& GetMemoryBlockSlabManager() const {
return *m_p_memory_block_slab_manager;
}
const KBlockInfoManager& GetBlockInfoManager() const {
return *m_p_block_info_manager;
}
const KPageTableManager& GetPageTableManager() const {
return *m_p_page_table_manager;
}
KMemoryBlockSlabManager& GetMemoryBlockSlabManager() {
return *m_p_memory_block_slab_manager;
}
KBlockInfoManager& GetBlockInfoManager() {
return *m_p_block_info_manager;
}
KPageTableManager& GetPageTableManager() {
return *m_p_page_table_manager;
}
KMemoryBlockSlabManager* GetMemoryBlockSlabManagerPointer() {
return m_p_memory_block_slab_manager;
}
KBlockInfoManager* GetBlockInfoManagerPointer() {
return m_p_block_info_manager;
}
KPageTableManager* GetPageTableManagerPointer() {
return m_p_page_table_manager;
}
private:
KMemoryBlockSlabManager* m_p_memory_block_slab_manager{};
KBlockInfoManager* m_p_block_info_manager{};
KPageTableManager* m_p_page_table_manager{};
bool m_is_secure_resource{false};
};
class KSecureSystemResource final
: public KAutoObjectWithSlabHeap<KSecureSystemResource, KSystemResource> {
public:
explicit KSecureSystemResource(KernelCore& kernel_)
: KAutoObjectWithSlabHeap<KSecureSystemResource, KSystemResource>(kernel_) {
// Mark ourselves as being a secure resource.
this->SetSecureResource();
}
Result Initialize(size_t size, KResourceLimit* resource_limit, KMemoryManager::Pool pool);
void Finalize();
bool IsInitialized() const {
return m_is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
size_t CalculateRequiredSecureMemorySize() const {
return CalculateRequiredSecureMemorySize(m_resource_size, m_resource_pool);
}
size_t GetSize() const {
return m_resource_size;
}
size_t GetUsedSize() const {
return m_dynamic_page_manager.GetUsed() * PageSize;
}
const KDynamicPageManager& GetDynamicPageManager() const {
return m_dynamic_page_manager;
}
public:
static size_t CalculateRequiredSecureMemorySize(size_t size, KMemoryManager::Pool pool);
private:
bool m_is_initialized{};
KMemoryManager::Pool m_resource_pool{};
KDynamicPageManager m_dynamic_page_manager;
KMemoryBlockSlabManager m_memory_block_slab_manager;
KBlockInfoManager m_block_info_manager;
KPageTableManager m_page_table_manager;
KMemoryBlockSlabHeap m_memory_block_heap;
KBlockInfoSlabHeap m_block_info_heap;
KPageTableSlabHeap m_page_table_heap;
KResourceLimit* m_resource_limit{};
VAddr m_resource_address{};
size_t m_resource_size{};
};
} // namespace Kernel

View File

@@ -263,9 +263,9 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_
R_SUCCEED();
}
Result KThread::InitializeDummyThread(KThread* thread) {
Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) {
// Initialize the thread.
R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy));
R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy));
// Initialize emulation parameters.
thread->stack_parameters.disable_count = 0;
@@ -303,7 +303,7 @@ void KThread::PostDestroy(uintptr_t arg) {
const bool resource_limit_release_hint = (arg & 1);
const s64 hint_value = (resource_limit_release_hint ? 0 : 1);
if (owner != nullptr) {
owner->GetResourceLimit()->Release(LimitableResource::Threads, 1, hint_value);
owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value);
owner->Close();
}
}
@@ -1054,7 +1054,7 @@ void KThread::Exit() {
// Release the thread resource hint, running thread count from parent.
if (parent != nullptr) {
parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1);
parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1);
resource_limit_release_hint = true;
parent->DecrementRunningThreadCount();
}

View File

@@ -415,7 +415,7 @@ public:
static void PostDestroy(uintptr_t arg);
[[nodiscard]] static Result InitializeDummyThread(KThread* thread);
[[nodiscard]] static Result InitializeDummyThread(KThread* thread, KProcess* owner);
[[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread,
s32 virt_core);

View File

@@ -37,7 +37,7 @@ void KTransferMemory::Finalize() {
void KTransferMemory::PostDestroy(uintptr_t arg) {
KProcess* owner = reinterpret_cast<KProcess*>(arg);
owner->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1);
owner->GetResourceLimit()->Release(LimitableResource::TransferMemoryCountMax, 1);
owner->Close();
}

View File

@@ -28,10 +28,12 @@
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_system_resource.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_worker_task_manager.h"
#include "core/hle/kernel/kernel.h"
@@ -47,6 +49,11 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
namespace Kernel {
struct KernelCore::Impl {
static constexpr size_t ApplicationMemoryBlockSlabHeapSize = 20000;
static constexpr size_t SystemMemoryBlockSlabHeapSize = 10000;
static constexpr size_t BlockInfoSlabHeapSize = 4000;
static constexpr size_t ReservedDynamicPageCount = 64;
explicit Impl(Core::System& system_, KernelCore& kernel_)
: time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"},
service_thread_barrier{2}, system{system_} {}
@@ -71,7 +78,6 @@ struct KernelCore::Impl {
// Initialize kernel memory and resources.
InitializeSystemResourceLimit(kernel, system.CoreTiming());
InitializeMemoryLayout();
Init::InitializeKPageBufferSlabHeap(system);
InitializeShutdownThreads();
InitializePhysicalCores();
InitializePreemption(kernel);
@@ -81,12 +87,13 @@ struct KernelCore::Impl {
const auto& pt_heap_region = memory_layout->GetPageTableHeapRegion();
ASSERT(pt_heap_region.GetEndAddress() != 0);
InitializeResourceManagers(pt_heap_region.GetAddress(), pt_heap_region.GetSize());
InitializeResourceManagers(kernel, pt_heap_region.GetAddress(),
pt_heap_region.GetSize());
}
RegisterHostThread();
RegisterHostThread(nullptr);
default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread");
}
void InitializeCores() {
@@ -222,18 +229,22 @@ struct KernelCore::Impl {
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)
ASSERT(
system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, total_size)
.IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800)
.IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200)
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900)
.IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size);
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200)
.IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133)
.IsSuccess());
system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, kernel_size);
// Reserve secure applet memory, introduced in firmware 5.0.0
constexpr u64 secure_applet_memory_size{4_MiB};
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax,
secure_applet_memory_size));
}
@@ -253,16 +264,82 @@ struct KernelCore::Impl {
system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event);
}
void InitializeResourceManagers(VAddr address, size_t size) {
dynamic_page_manager = std::make_unique<KDynamicPageManager>();
memory_block_heap = std::make_unique<KMemoryBlockSlabHeap>();
app_memory_block_manager = std::make_unique<KMemoryBlockSlabManager>();
void InitializeResourceManagers(KernelCore& kernel, VAddr address, size_t size) {
// Ensure that the buffer is suitable for our use.
ASSERT(Common::IsAligned(address, PageSize));
ASSERT(Common::IsAligned(size, PageSize));
dynamic_page_manager->Initialize(address, size);
static constexpr size_t ApplicationMemoryBlockSlabHeapSize = 20000;
memory_block_heap->Initialize(dynamic_page_manager.get(),
ApplicationMemoryBlockSlabHeapSize);
app_memory_block_manager->Initialize(nullptr, memory_block_heap.get());
// Ensure that we have space for our reference counts.
const size_t rc_size =
Common::AlignUp(KPageTableSlabHeap::CalculateReferenceCountSize(size), PageSize);
ASSERT(rc_size < size);
size -= rc_size;
// Initialize the resource managers' shared page manager.
resource_manager_page_manager = std::make_unique<KDynamicPageManager>();
resource_manager_page_manager->Initialize(
address, size, std::max<size_t>(PageSize, KPageBufferSlabHeap::BufferSize));
// Initialize the KPageBuffer slab heap.
page_buffer_slab_heap.Initialize(system);
// Initialize the fixed-size slabheaps.
app_memory_block_heap = std::make_unique<KMemoryBlockSlabHeap>();
sys_memory_block_heap = std::make_unique<KMemoryBlockSlabHeap>();
block_info_heap = std::make_unique<KBlockInfoSlabHeap>();
app_memory_block_heap->Initialize(resource_manager_page_manager.get(),
ApplicationMemoryBlockSlabHeapSize);
sys_memory_block_heap->Initialize(resource_manager_page_manager.get(),
SystemMemoryBlockSlabHeapSize);
block_info_heap->Initialize(resource_manager_page_manager.get(), BlockInfoSlabHeapSize);
// Reserve all but a fixed number of remaining pages for the page table heap.
const size_t num_pt_pages = resource_manager_page_manager->GetCount() -
resource_manager_page_manager->GetUsed() -
ReservedDynamicPageCount;
page_table_heap = std::make_unique<KPageTableSlabHeap>();
// TODO(bunnei): Pass in address once we support kernel virtual memory allocations.
page_table_heap->Initialize(
resource_manager_page_manager.get(), num_pt_pages,
/*GetPointer<KPageTableManager::RefCount>(address + size)*/ nullptr);
// Setup the slab managers.
KDynamicPageManager* const app_dynamic_page_manager = nullptr;
KDynamicPageManager* const sys_dynamic_page_manager =
/*KTargetSystem::IsDynamicResourceLimitsEnabled()*/ true
? resource_manager_page_manager.get()
: nullptr;
app_memory_block_manager = std::make_unique<KMemoryBlockSlabManager>();
sys_memory_block_manager = std::make_unique<KMemoryBlockSlabManager>();
app_block_info_manager = std::make_unique<KBlockInfoManager>();
sys_block_info_manager = std::make_unique<KBlockInfoManager>();
app_page_table_manager = std::make_unique<KPageTableManager>();
sys_page_table_manager = std::make_unique<KPageTableManager>();
app_memory_block_manager->Initialize(app_dynamic_page_manager, app_memory_block_heap.get());
sys_memory_block_manager->Initialize(sys_dynamic_page_manager, sys_memory_block_heap.get());
app_block_info_manager->Initialize(app_dynamic_page_manager, block_info_heap.get());
sys_block_info_manager->Initialize(sys_dynamic_page_manager, block_info_heap.get());
app_page_table_manager->Initialize(app_dynamic_page_manager, page_table_heap.get());
sys_page_table_manager->Initialize(sys_dynamic_page_manager, page_table_heap.get());
// Check that we have the correct number of dynamic pages available.
ASSERT(resource_manager_page_manager->GetCount() -
resource_manager_page_manager->GetUsed() ==
ReservedDynamicPageCount);
// Create the system page table managers.
app_system_resource = std::make_unique<KSystemResource>(kernel);
sys_system_resource = std::make_unique<KSystemResource>(kernel);
// Set the managers for the system resources.
app_system_resource->SetManagers(*app_memory_block_manager, *app_block_info_manager,
*app_page_table_manager);
sys_system_resource->SetManagers(*sys_memory_block_manager, *sys_block_info_manager,
*sys_page_table_manager);
}
void InitializeShutdownThreads() {
@@ -300,15 +377,18 @@ struct KernelCore::Impl {
}
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread() {
KThread* GetHostDummyThread(KThread* existing_thread) {
auto initialize = [this](KThread* thread) {
ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess());
thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
return thread;
};
thread_local auto raw_thread = KThread(system.Kernel());
thread_local auto thread = initialize(&raw_thread);
thread_local KThread raw_thread{system.Kernel()};
thread_local KThread* thread = nullptr;
if (thread == nullptr) {
thread = (existing_thread == nullptr) ? initialize(&raw_thread) : existing_thread;
}
return thread;
}
@@ -323,9 +403,9 @@ struct KernelCore::Impl {
}
/// Registers a new host thread by allocating a host thread ID for it
void RegisterHostThread() {
void RegisterHostThread(KThread* existing_thread) {
[[maybe_unused]] const auto this_id = GetHostThreadId();
[[maybe_unused]] const auto dummy_thread = GetHostDummyThread();
[[maybe_unused]] const auto dummy_thread = GetHostDummyThread(existing_thread);
}
[[nodiscard]] u32 GetCurrentHostThreadID() {
@@ -356,7 +436,7 @@ struct KernelCore::Impl {
KThread* GetCurrentEmuThread() {
const auto thread_id = GetCurrentHostThreadID();
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
return GetHostDummyThread();
return GetHostDummyThread(nullptr);
}
return current_thread;
@@ -446,6 +526,9 @@ struct KernelCore::Impl {
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc));
// Determine if we'll use extra thread resources.
const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit();
// Setup the stack region.
constexpr size_t StackRegionSize = 14_MiB;
constexpr size_t StackRegionAlign = KernelAslrAlignment;
@@ -456,7 +539,8 @@ struct KernelCore::Impl {
stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack));
// Determine the size of the resource region.
const size_t resource_region_size = memory_layout->GetResourceRegionSizeForInit();
const size_t resource_region_size =
memory_layout->GetResourceRegionSizeForInit(use_extra_resources);
// Determine the size of the slab region.
const size_t slab_region_size =
@@ -702,33 +786,31 @@ struct KernelCore::Impl {
search->second(system.ServiceManager(), server_port);
}
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
const std::string& name) {
auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, name);
Kernel::ServiceThread& CreateServiceThread(KernelCore& kernel, const std::string& name) {
auto* ptr = new ServiceThread(kernel, name);
service_threads_manager.QueueWork(
[this, service_thread]() { service_threads.emplace(service_thread); });
[this, ptr]() { service_threads.emplace(ptr, std::unique_ptr<ServiceThread>(ptr)); });
return service_thread;
return *ptr;
}
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
if (auto strong_ptr = service_thread.lock()) {
if (strong_ptr == default_service_thread.lock()) {
// Nothing to do here, the service is using default_service_thread, which will be
// released on shutdown.
return;
}
void ReleaseServiceThread(Kernel::ServiceThread& service_thread) {
auto* ptr = &service_thread;
service_threads_manager.QueueWork(
[this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
if (ptr == default_service_thread) {
// Nothing to do here, the service is using default_service_thread, which will be
// released on shutdown.
return;
}
service_threads_manager.QueueWork([this, ptr]() { service_threads.erase(ptr); });
}
void ClearServiceThreads() {
service_threads_manager.QueueWork([this] {
service_threads.clear();
default_service_thread.reset();
default_service_thread = nullptr;
service_thread_barrier.Sync();
});
service_thread_barrier.Sync();
@@ -751,6 +833,8 @@ struct KernelCore::Impl {
Init::KSlabResourceCounts slab_resource_counts{};
KResourceLimit* system_resource_limit{};
KPageBufferSlabHeap page_buffer_slab_heap;
std::shared_ptr<Core::Timing::EventType> preemption_event;
// This is the kernel's handle table or supervisor handle table which
@@ -776,10 +860,20 @@ struct KernelCore::Impl {
// Kernel memory management
std::unique_ptr<KMemoryManager> memory_manager;
// Dynamic slab managers
std::unique_ptr<KDynamicPageManager> dynamic_page_manager;
std::unique_ptr<KMemoryBlockSlabHeap> memory_block_heap;
// Resource managers
std::unique_ptr<KDynamicPageManager> resource_manager_page_manager;
std::unique_ptr<KPageTableSlabHeap> page_table_heap;
std::unique_ptr<KMemoryBlockSlabHeap> app_memory_block_heap;
std::unique_ptr<KMemoryBlockSlabHeap> sys_memory_block_heap;
std::unique_ptr<KBlockInfoSlabHeap> block_info_heap;
std::unique_ptr<KPageTableManager> app_page_table_manager;
std::unique_ptr<KPageTableManager> sys_page_table_manager;
std::unique_ptr<KMemoryBlockSlabManager> app_memory_block_manager;
std::unique_ptr<KMemoryBlockSlabManager> sys_memory_block_manager;
std::unique_ptr<KBlockInfoManager> app_block_info_manager;
std::unique_ptr<KBlockInfoManager> sys_block_info_manager;
std::unique_ptr<KSystemResource> app_system_resource;
std::unique_ptr<KSystemResource> sys_system_resource;
// Shared memory for services
Kernel::KSharedMemory* hid_shared_mem{};
@@ -792,8 +886,8 @@ struct KernelCore::Impl {
std::unique_ptr<KMemoryLayout> memory_layout;
// Threads used for services
std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
std::weak_ptr<ServiceThread> default_service_thread;
std::unordered_map<ServiceThread*, std::unique_ptr<ServiceThread>> service_threads;
ServiceThread* default_service_thread{};
Common::ThreadWorker service_threads_manager;
Common::Barrier service_thread_barrier;
@@ -1033,8 +1127,12 @@ void KernelCore::RegisterCoreThread(std::size_t core_id) {
impl->RegisterCoreThread(core_id);
}
void KernelCore::RegisterHostThread() {
impl->RegisterHostThread();
void KernelCore::RegisterHostThread(KThread* existing_thread) {
impl->RegisterHostThread(existing_thread);
if (existing_thread != nullptr) {
ASSERT(GetCurrentEmuThread() == existing_thread);
}
}
u32 KernelCore::GetCurrentHostThreadID() const {
@@ -1057,12 +1155,12 @@ const KMemoryManager& KernelCore::MemoryManager() const {
return *impl->memory_manager;
}
KMemoryBlockSlabManager& KernelCore::GetApplicationMemoryBlockManager() {
return *impl->app_memory_block_manager;
KSystemResource& KernelCore::GetSystemSystemResource() {
return *impl->sys_system_resource;
}
const KMemoryBlockSlabManager& KernelCore::GetApplicationMemoryBlockManager() const {
return *impl->app_memory_block_manager;
const KSystemResource& KernelCore::GetSystemSystemResource() const {
return *impl->sys_system_resource;
}
Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
@@ -1109,16 +1207,28 @@ 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);
std::vector<KScopedAutoObject<KThread>> process_threads;
{
KScopedSchedulerLock sl{*this};
if (auto* process = CurrentProcess(); process != nullptr) {
process->SetActivity(activity);
if (!should_suspend) {
// Runnable now; no need to wait.
return;
}
if (should_suspend) {
// Wait for execution to stop
for (auto* thread : process->GetThreadList()) {
thread->WaitUntilSuspended();
process_threads.emplace_back(thread);
}
}
}
// Wait for execution to stop.
for (auto& thread : process_threads) {
thread->WaitUntilSuspended();
}
}
void KernelCore::ShutdownCores() {
@@ -1150,15 +1260,15 @@ void KernelCore::ExitSVCProfile() {
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
}
std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
Kernel::ServiceThread& KernelCore::CreateServiceThread(const std::string& name) {
return impl->CreateServiceThread(*this, name);
}
std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const {
return impl->default_service_thread;
Kernel::ServiceThread& KernelCore::GetDefaultServiceThread() const {
return *impl->default_service_thread;
}
void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
void KernelCore::ReleaseServiceThread(Kernel::ServiceThread& service_thread) {
impl->ReleaseServiceThread(service_thread);
}

View File

@@ -34,13 +34,16 @@ class KClientPort;
class GlobalSchedulerContext;
class KAutoObjectWithListContainer;
class KClientSession;
class KDebug;
class KDynamicPageManager;
class KEvent;
class KEventInfo;
class KHandleTable;
class KLinkedListNode;
class KMemoryBlockSlabManager;
class KMemoryLayout;
class KMemoryManager;
class KPageBuffer;
class KPageBufferSlabHeap;
class KPort;
class KProcess;
class KResourceLimit;
@@ -51,6 +54,7 @@ class KSession;
class KSessionRequest;
class KSharedMemory;
class KSharedMemoryInfo;
class KSecureSystemResource;
class KThread;
class KThreadLocalPage;
class KTransferMemory;
@@ -236,7 +240,7 @@ public:
void RegisterCoreThread(std::size_t core_id);
/// Register the current thread as a non CPU core thread.
void RegisterHostThread();
void RegisterHostThread(KThread* existing_thread = nullptr);
/// Gets the virtual memory manager for the kernel.
KMemoryManager& MemoryManager();
@@ -244,11 +248,11 @@ public:
/// Gets the virtual memory manager for the kernel.
const KMemoryManager& MemoryManager() const;
/// Gets the application memory block manager for the kernel.
KMemoryBlockSlabManager& GetApplicationMemoryBlockManager();
/// Gets the system resource manager.
KSystemResource& GetSystemSystemResource();
/// Gets the application memory block manager for the kernel.
const KMemoryBlockSlabManager& GetApplicationMemoryBlockManager() const;
/// Gets the system resource manager.
const KSystemResource& GetSystemSystemResource() const;
/// Gets the shared memory object for HID services.
Kernel::KSharedMemory& GetHidSharedMem();
@@ -305,24 +309,24 @@ public:
* See GetDefaultServiceThread.
* @param name String name for the ServerSession creating this thread, used for debug
* purposes.
* @returns The a weak pointer newly created service thread.
* @returns A reference to the newly created service thread.
*/
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
Kernel::ServiceThread& CreateServiceThread(const std::string& name);
/**
* Gets the default host service thread, which executes HLE service requests. Unless service
* requests need to block on the host, the default service thread should be used in favor of
* creating a new service thread.
* @returns The a weak pointer for the default service thread.
* @returns A reference to the default service thread.
*/
std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const;
Kernel::ServiceThread& GetDefaultServiceThread() const;
/**
* Releases a HLE service thread, instructing KernelCore to free it. This should be called when
* the ServerSession associated with the thread is destroyed.
* @param service_thread Service thread to release.
*/
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread);
void ReleaseServiceThread(Kernel::ServiceThread& service_thread);
/// Workaround for single-core mode when preempting threads while idle.
bool IsPhantomModeForSingleCore() const;
@@ -364,6 +368,12 @@ public:
return slab_heap_container->thread_local_page;
} else if constexpr (std::is_same_v<T, KSessionRequest>) {
return slab_heap_container->session_request;
} else if constexpr (std::is_same_v<T, KSecureSystemResource>) {
return slab_heap_container->secure_system_resource;
} else if constexpr (std::is_same_v<T, KEventInfo>) {
return slab_heap_container->event_info;
} else if constexpr (std::is_same_v<T, KDebug>) {
return slab_heap_container->debug;
}
}
@@ -427,6 +437,9 @@ private:
KSlabHeap<KPageBuffer> page_buffer;
KSlabHeap<KThreadLocalPage> thread_local_page;
KSlabHeap<KSessionRequest> session_request;
KSlabHeap<KSecureSystemResource> secure_system_resource;
KSlabHeap<KEventInfo> event_info;
KSlabHeap<KDebug> debug;
};
std::unique_ptr<SlabHeapContainer> slab_heap_container;

View File

@@ -12,7 +12,7 @@ namespace Kernel {
PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_)
: core_index{core_index_}, system{system_}, scheduler{scheduler_} {
#ifdef ARCHITECTURE_x86_64
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
auto& kernel = system.Kernel();
@@ -26,7 +26,7 @@ PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KSche
PhysicalCore::~PhysicalCore() = default;
void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
#ifdef ARCHITECTURE_x86_64
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
auto& kernel = system.Kernel();
if (!is_64_bit) {
// We already initialized a 64-bit core, replace with a 32-bit one.

View File

@@ -36,11 +36,11 @@ public:
private:
KernelCore& kernel;
std::jthread m_thread;
std::jthread m_host_thread;
std::mutex m_session_mutex;
std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions;
KEvent* m_wakeup_event;
KProcess* m_process;
KThread* m_thread;
std::atomic<bool> m_shutdown_requested;
const std::string m_service_name;
};
@@ -132,7 +132,7 @@ void ServiceThread::Impl::SessionClosed(KServerSession* server_session,
void ServiceThread::Impl::LoopProcess() {
Common::SetCurrentThreadName(m_service_name.c_str());
kernel.RegisterHostThread();
kernel.RegisterHostThread(m_thread);
while (!m_shutdown_requested.load()) {
WaitAndProcessImpl();
@@ -160,7 +160,7 @@ ServiceThread::Impl::~Impl() {
// Shut down the processing thread.
m_shutdown_requested.store(true);
m_wakeup_event->Signal();
m_thread.join();
m_host_thread.join();
// Lock mutex.
m_session_mutex.lock();
@@ -177,33 +177,22 @@ ServiceThread::Impl::~Impl() {
m_wakeup_event->GetReadableEvent().Close();
m_wakeup_event->Close();
// Close process.
m_process->Close();
// Close thread.
m_thread->Close();
}
ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name)
: kernel{kernel_}, m_service_name{service_name} {
// Initialize process.
m_process = KProcess::Create(kernel);
KProcess::Initialize(m_process, kernel.System(), service_name,
KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit());
// Reserve a new event from the process resource limit
KScopedResourceReservation event_reservation(m_process, LimitableResource::Events);
ASSERT(event_reservation.Succeeded());
// Initialize event.
m_wakeup_event = KEvent::Create(kernel);
m_wakeup_event->Initialize(m_process);
m_wakeup_event->Initialize(nullptr);
// Commit the event reservation.
event_reservation.Commit();
// Register the event.
KEvent::Register(kernel, m_wakeup_event);
// Initialize thread.
m_thread = KThread::Create(kernel);
ASSERT(KThread::InitializeDummyThread(m_thread, nullptr).IsSuccess());
// Start thread.
m_thread = std::jthread([this] { LoopProcess(); });
m_host_thread = std::jthread([this] { LoopProcess(); });
}
ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name)

View File

@@ -52,6 +52,84 @@ public:
}
};
template <typename Derived, typename Base>
class KAutoObjectWithSlabHeap : public Base {
static_assert(std::is_base_of<KAutoObject, Base>::value);
private:
static Derived* Allocate(KernelCore& kernel) {
return kernel.SlabHeap<Derived>().Allocate(kernel);
}
static void Free(KernelCore& kernel, Derived* obj) {
kernel.SlabHeap<Derived>().Free(obj);
}
public:
explicit KAutoObjectWithSlabHeap(KernelCore& kernel_) : Base(kernel_), kernel(kernel_) {}
virtual ~KAutoObjectWithSlabHeap() = default;
virtual void Destroy() override {
const bool is_initialized = this->IsInitialized();
uintptr_t arg = 0;
if (is_initialized) {
arg = this->GetPostDestroyArgument();
this->Finalize();
}
Free(kernel, static_cast<Derived*>(this));
if (is_initialized) {
Derived::PostDestroy(arg);
}
}
virtual bool IsInitialized() const {
return true;
}
virtual uintptr_t GetPostDestroyArgument() const {
return 0;
}
size_t GetSlabIndex() const {
return SlabHeap<Derived>(kernel).GetObjectIndex(static_cast<const Derived*>(this));
}
public:
static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
}
static Derived* Create(KernelCore& kernel) {
Derived* obj = Allocate(kernel);
if (obj != nullptr) {
KAutoObject::Create(obj);
}
return obj;
}
static size_t GetObjectSize(KernelCore& kernel) {
return kernel.SlabHeap<Derived>().GetObjectSize();
}
static size_t GetSlabHeapSize(KernelCore& kernel) {
return kernel.SlabHeap<Derived>().GetSlabHeapSize();
}
static size_t GetPeakIndex(KernelCore& kernel) {
return kernel.SlabHeap<Derived>().GetPeakIndex();
}
static uintptr_t GetSlabHeapAddress(KernelCore& kernel) {
return kernel.SlabHeap<Derived>().GetSlabHeapAddress();
}
static size_t GetNumRemaining(KernelCore& kernel) {
return kernel.SlabHeap<Derived>().GetNumRemaining();
}
protected:
KernelCore& kernel;
};
template <typename Derived, typename Base>
class KAutoObjectWithSlabHeapAndContainer : public Base {
static_assert(std::is_base_of<KAutoObjectWithList, Base>::value);

View File

@@ -267,7 +267,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
// Reserve a new session from the process resource limit.
// FIXME: LimitableResource_SessionCountMax
KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions);
KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
if (session_reservation.Succeeded()) {
session = T::Create(system.Kernel());
} else {
@@ -298,7 +298,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
// We successfully allocated a session, so add the object we allocated to the resource
// limit.
// system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1);
// system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
}
// Check that we successfully created a session.
@@ -656,27 +656,12 @@ static Result ArbitrateUnlock32(Core::System& system, u32 address) {
return ArbitrateUnlock(system, address);
}
enum class BreakType : u32 {
Panic = 0,
AssertionFailed = 1,
PreNROLoad = 3,
PostNROLoad = 4,
PreNROUnload = 5,
PostNROUnload = 6,
CppException = 7,
};
struct BreakReason {
union {
u32 raw;
BitField<0, 30, BreakType> break_type;
BitField<31, 1, u32> signal_debugger;
};
};
/// Break program execution
static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
BreakReason break_reason{reason};
BreakReason break_reason =
static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag));
bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0;
bool has_dumped_buffer{};
std::vector<u8> debug_buffer;
@@ -705,57 +690,56 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
}
has_dumped_buffer = true;
};
switch (break_reason.break_type) {
case BreakType::Panic:
LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
switch (break_reason) {
case BreakReason::Panic:
LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
info2);
handle_debug_buffer(info1, info2);
break;
case BreakReason::Assert:
LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
info1, info2);
handle_debug_buffer(info1, info2);
break;
case BreakType::AssertionFailed:
LOG_CRITICAL(Debug_Emulated,
"Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
info1, info2);
case BreakReason::User:
LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
handle_debug_buffer(info1, info2);
break;
case BreakType::PreNROLoad:
LOG_WARNING(
Debug_Emulated,
"Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
info1, info2);
case BreakReason::PreLoadDll:
LOG_INFO(Debug_Emulated,
"Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
break;
case BreakType::PostNROLoad:
LOG_WARNING(Debug_Emulated,
"Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
case BreakReason::PostLoadDll:
LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
break;
case BreakType::PreNROUnload:
LOG_WARNING(
Debug_Emulated,
"Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
info1, info2);
case BreakReason::PreUnloadDll:
LOG_INFO(Debug_Emulated,
"Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
break;
case BreakType::PostNROUnload:
LOG_WARNING(Debug_Emulated,
"Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
case BreakReason::PostUnloadDll:
LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
info1, info2);
break;
case BreakType::CppException:
case BreakReason::CppException:
LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
break;
default:
LOG_WARNING(
Debug_Emulated,
"Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
static_cast<u32>(break_reason.break_type.Value()), info1, info2);
"Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
handle_debug_buffer(info1, info2);
break;
}
system.GetReporter().SaveSvcBreakReport(
static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger.As<bool>(),
info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2,
has_dumped_buffer ? std::make_optional(debug_buffer)
: std::nullopt);
if (!break_reason.signal_debugger) {
if (!notification_only) {
LOG_CRITICAL(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
@@ -1716,13 +1700,13 @@ static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address
auto& memory{system.Memory()};
const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
memory.Write64(memory_info_address + 0x00, memory_info.addr);
memory.Write64(memory_info_address + 0x00, memory_info.base_address);
memory.Write64(memory_info_address + 0x08, memory_info.size);
memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attr));
memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.perm));
memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount);
memory.Write32(memory_info_address + 0x20, memory_info.device_refcount);
memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute));
memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission));
memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count);
memory.Write32(memory_info_address + 0x20, memory_info.device_count);
memory.Write32(memory_info_address + 0x24, 0);
// Page info appears to be currently unused by the kernel and is always set to zero.
@@ -1943,7 +1927,7 @@ static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry
// Reserve a new thread from the process resource limit (waiting up to 100ms).
KScopedResourceReservation thread_reservation(
kernel.CurrentProcess(), LimitableResource::Threads, 1,
kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
if (!thread_reservation.Succeeded()) {
LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
@@ -2247,7 +2231,7 @@ static u64 GetSystemTick(Core::System& system) {
auto& core_timing = system.CoreTiming();
// Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
const u64 result{system.CoreTiming().GetClockTicks()};
const u64 result{core_timing.GetClockTicks()};
if (!system.Kernel().IsMulticore()) {
core_timing.AddTicks(400U);
@@ -2344,7 +2328,7 @@ static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr addr
// Reserve a new transfer memory from the process resource limit.
KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
LimitableResource::TransferMemory);
LimitableResource::TransferMemoryCountMax);
R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
// Create the transfer memory.
@@ -2496,7 +2480,7 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r
// Reserve a new event from the process resource limit
KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
LimitableResource::Events);
LimitableResource::EventCountMax);
R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
// Create a new event.
@@ -2539,11 +2523,6 @@ static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out
static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
// This function currently only allows retrieving a process' status.
enum class InfoType {
Status,
};
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
if (process.IsNull()) {
@@ -2552,9 +2531,9 @@ static Result GetProcessInfo(Core::System& system, u64* out, Handle process_hand
return ResultInvalidHandle;
}
const auto info_type = static_cast<InfoType>(type);
if (info_type != InfoType::Status) {
LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type);
const auto info_type = static_cast<ProcessInfoType>(type);
if (info_type != ProcessInfoType::ProcessState) {
LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type);
return ResultInvalidEnumValue;
}
@@ -2722,14 +2701,24 @@ static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr ou
return ResultSuccess;
}
static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system,
[[maybe_unused]] Handle handle, [[maybe_unused]] u32 address,
[[maybe_unused]] u32 size) {
// Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op,
// as all emulation is done in the same cache level in host architecture, thus data cache
// does not need flushing.
LOG_DEBUG(Kernel_SVC, "called");
return ResultSuccess;
static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address,
u64 size) {
// Validate address/size.
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
// Get the process from its handle.
KScopedAutoObject process =
system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
// Verify the region is within range.
auto& page_table = process->PageTable();
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
// Perform the operation.
R_RETURN(system.Memory().FlushDataCache(*process, address, size));
}
namespace {

View File

@@ -37,6 +37,7 @@ constexpr Result ResultInvalidState{ErrorModule::Kernel, 125};
constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126};
constexpr Result ResultPortClosed{ErrorModule::Kernel, 131};
constexpr Result ResultLimitReached{ErrorModule::Kernel, 132};
constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259};
constexpr Result ResultInvalidId{ErrorModule::Kernel, 519};
} // namespace Kernel

View File

@@ -8,6 +8,8 @@
namespace Kernel::Svc {
using Handle = u32;
enum class MemoryState : u32 {
Free = 0x00,
Io = 0x01,
@@ -22,8 +24,8 @@ enum class MemoryState : u32 {
Ipc = 0x0A,
Stack = 0x0B,
ThreadLocal = 0x0C,
Transferred = 0x0D,
SharedTransferred = 0x0E,
Transfered = 0x0D,
SharedTransfered = 0x0E,
SharedCode = 0x0F,
Inaccessible = 0x10,
NonSecureIpc = 0x11,
@@ -32,6 +34,7 @@ enum class MemoryState : u32 {
GeneratedCode = 0x14,
CodeOut = 0x15,
Coverage = 0x16,
Insecure = 0x17,
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryState);
@@ -54,17 +57,6 @@ enum class MemoryPermission : u32 {
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
struct MemoryInfo {
u64 addr{};
u64 size{};
MemoryState state{};
MemoryAttribute attr{};
MemoryPermission perm{};
u32 ipc_refcount{};
u32 device_refcount{};
u32 padding{};
};
enum class SignalType : u32 {
Signal = 0,
SignalAndIncrementIfEqual = 1,
@@ -83,6 +75,13 @@ enum class YieldType : s64 {
ToAnyThread = -2,
};
enum class ThreadExitReason : u32 {
ExitThread = 0,
TerminateThread = 1,
ExitProcess = 2,
TerminateProcess = 3,
};
enum class ThreadActivity : u32 {
Runnable = 0,
Paused = 1,
@@ -108,6 +107,489 @@ enum class ProcessState : u32 {
DebugBreak = 7,
};
enum class ProcessExitReason : u32 {
ExitProcess = 0,
TerminateProcess = 1,
Exception = 2,
};
constexpr inline size_t ThreadLocalRegionSize = 0x200;
struct PageInfo {
u32 flags;
};
// Info Types.
enum class InfoType : u32 {
CoreMask = 0,
PriorityMask = 1,
AliasRegionAddress = 2,
AliasRegionSize = 3,
HeapRegionAddress = 4,
HeapRegionSize = 5,
TotalMemorySize = 6,
UsedMemorySize = 7,
DebuggerAttached = 8,
ResourceLimit = 9,
IdleTickCount = 10,
RandomEntropy = 11,
AslrRegionAddress = 12,
AslrRegionSize = 13,
StackRegionAddress = 14,
StackRegionSize = 15,
SystemResourceSizeTotal = 16,
SystemResourceSizeUsed = 17,
ProgramId = 18,
InitialProcessIdRange = 19,
UserExceptionContextAddress = 20,
TotalNonSystemMemorySize = 21,
UsedNonSystemMemorySize = 22,
IsApplication = 23,
FreeThreadCount = 24,
ThreadTickCount = 25,
IsSvcPermitted = 26,
MesosphereMeta = 65000,
MesosphereCurrentProcess = 65001,
};
enum class BreakReason : u32 {
Panic = 0,
Assert = 1,
User = 2,
PreLoadDll = 3,
PostLoadDll = 4,
PreUnloadDll = 5,
PostUnloadDll = 6,
CppException = 7,
NotificationOnlyFlag = 0x80000000,
};
enum class DebugEvent : u32 {
CreateProcess = 0,
CreateThread = 1,
ExitProcess = 2,
ExitThread = 3,
Exception = 4,
};
enum class DebugThreadParam : u32 {
Priority = 0,
State = 1,
IdealCore = 2,
CurrentCore = 3,
AffinityMask = 4,
};
enum class DebugException : u32 {
UndefinedInstruction = 0,
InstructionAbort = 1,
DataAbort = 2,
AlignmentFault = 3,
DebuggerAttached = 4,
BreakPoint = 5,
UserBreak = 6,
DebuggerBreak = 7,
UndefinedSystemCall = 8,
MemorySystemError = 9,
};
enum class DebugEventFlag : u32 {
Stopped = (1u << 0),
};
enum class BreakPointType : u32 {
HardwareInstruction = 0,
HardwareData = 1,
};
enum class HardwareBreakPointRegisterName : u32 {
I0 = 0,
I1 = 1,
I2 = 2,
I3 = 3,
I4 = 4,
I5 = 5,
I6 = 6,
I7 = 7,
I8 = 8,
I9 = 9,
I10 = 10,
I11 = 11,
I12 = 12,
I13 = 13,
I14 = 14,
I15 = 15,
D0 = 16,
D1 = 17,
D2 = 18,
D3 = 19,
D4 = 20,
D5 = 21,
D6 = 22,
D7 = 23,
D8 = 24,
D9 = 25,
D10 = 26,
D11 = 27,
D12 = 28,
D13 = 29,
D14 = 30,
D15 = 31,
};
namespace lp64 {
struct LastThreadContext {
u64 fp;
u64 sp;
u64 lr;
u64 pc;
};
struct PhysicalMemoryInfo {
PAddr physical_address;
u64 virtual_address;
u64 size;
};
struct DebugInfoCreateProcess {
u64 program_id;
u64 process_id;
std::array<char, 0xC> name;
u32 flags;
u64 user_exception_context_address; // 5.0.0+
};
struct DebugInfoCreateThread {
u64 thread_id;
u64 tls_address;
// Removed in 11.0.0 u64 entrypoint;
};
struct DebugInfoExitProcess {
ProcessExitReason reason;
};
struct DebugInfoExitThread {
ThreadExitReason reason;
};
struct DebugInfoUndefinedInstructionException {
u32 insn;
};
struct DebugInfoDataAbortException {
u64 address;
};
struct DebugInfoAlignmentFaultException {
u64 address;
};
struct DebugInfoBreakPointException {
BreakPointType type;
u64 address;
};
struct DebugInfoUserBreakException {
BreakReason break_reason;
u64 address;
u64 size;
};
struct DebugInfoDebuggerBreakException {
std::array<u64, 4> active_thread_ids;
};
struct DebugInfoUndefinedSystemCallException {
u32 id;
};
union DebugInfoSpecificException {
DebugInfoUndefinedInstructionException undefined_instruction;
DebugInfoDataAbortException data_abort;
DebugInfoAlignmentFaultException alignment_fault;
DebugInfoBreakPointException break_point;
DebugInfoUserBreakException user_break;
DebugInfoDebuggerBreakException debugger_break;
DebugInfoUndefinedSystemCallException undefined_system_call;
u64 raw;
};
struct DebugInfoException {
DebugException type;
u64 address;
DebugInfoSpecificException specific;
};
union DebugInfo {
DebugInfoCreateProcess create_process;
DebugInfoCreateThread create_thread;
DebugInfoExitProcess exit_process;
DebugInfoExitThread exit_thread;
DebugInfoException exception;
};
struct DebugEventInfo {
DebugEvent type;
u32 flags;
u64 thread_id;
DebugInfo info;
};
static_assert(sizeof(DebugEventInfo) >= 0x40);
struct SecureMonitorArguments {
std::array<u64, 8> r;
};
static_assert(sizeof(SecureMonitorArguments) == 0x40);
} // namespace lp64
namespace ilp32 {
struct LastThreadContext {
u32 fp;
u32 sp;
u32 lr;
u32 pc;
};
struct PhysicalMemoryInfo {
PAddr physical_address;
u32 virtual_address;
u32 size;
};
struct DebugInfoCreateProcess {
u64 program_id;
u64 process_id;
std::array<char, 0xC> name;
u32 flags;
u32 user_exception_context_address; // 5.0.0+
};
struct DebugInfoCreateThread {
u64 thread_id;
u32 tls_address;
// Removed in 11.0.0 u32 entrypoint;
};
struct DebugInfoExitProcess {
ProcessExitReason reason;
};
struct DebugInfoExitThread {
ThreadExitReason reason;
};
struct DebugInfoUndefinedInstructionException {
u32 insn;
};
struct DebugInfoDataAbortException {
u32 address;
};
struct DebugInfoAlignmentFaultException {
u32 address;
};
struct DebugInfoBreakPointException {
BreakPointType type;
u32 address;
};
struct DebugInfoUserBreakException {
BreakReason break_reason;
u32 address;
u32 size;
};
struct DebugInfoDebuggerBreakException {
std::array<u64, 4> active_thread_ids;
};
struct DebugInfoUndefinedSystemCallException {
u32 id;
};
union DebugInfoSpecificException {
DebugInfoUndefinedInstructionException undefined_instruction;
DebugInfoDataAbortException data_abort;
DebugInfoAlignmentFaultException alignment_fault;
DebugInfoBreakPointException break_point;
DebugInfoUserBreakException user_break;
DebugInfoDebuggerBreakException debugger_break;
DebugInfoUndefinedSystemCallException undefined_system_call;
u64 raw;
};
struct DebugInfoException {
DebugException type;
u32 address;
DebugInfoSpecificException specific;
};
union DebugInfo {
DebugInfoCreateProcess create_process;
DebugInfoCreateThread create_thread;
DebugInfoExitProcess exit_process;
DebugInfoExitThread exit_thread;
DebugInfoException exception;
};
struct DebugEventInfo {
DebugEvent type;
u32 flags;
u64 thread_id;
DebugInfo info;
};
struct SecureMonitorArguments {
std::array<u32, 8> r;
};
static_assert(sizeof(SecureMonitorArguments) == 0x20);
} // namespace ilp32
struct ThreadContext {
std::array<u64, 29> r;
u64 fp;
u64 lr;
u64 sp;
u64 pc;
u32 pstate;
u32 padding;
std::array<u128, 32> v;
u32 fpcr;
u32 fpsr;
u64 tpidr;
};
static_assert(sizeof(ThreadContext) == 0x320);
struct MemoryInfo {
u64 base_address;
u64 size;
MemoryState state;
MemoryAttribute attribute;
MemoryPermission permission;
u32 ipc_count;
u32 device_count;
u32 padding;
};
enum class LimitableResource : u32 {
PhysicalMemoryMax = 0,
ThreadCountMax = 1,
EventCountMax = 2,
TransferMemoryCountMax = 3,
SessionCountMax = 4,
Count,
};
enum class IoPoolType : u32 {
// Not supported.
Count = 0,
};
enum class MemoryMapping : u32 {
IoRegister = 0,
Uncached = 1,
Memory = 2,
};
enum class KernelDebugType : u32 {
Thread = 0,
ThreadCallStack = 1,
KernelObject = 2,
Handle_ = 3,
Memory = 4,
PageTable = 5,
CpuUtilization = 6,
Process = 7,
SuspendProcess = 8,
ResumeProcess = 9,
Port = 10,
};
enum class KernelTraceState : u32 {
Disabled = 0,
Enabled = 1,
};
enum class CodeMemoryOperation : u32 {
Map = 0,
MapToOwner = 1,
Unmap = 2,
UnmapFromOwner = 3,
};
enum class InterruptType : u32 {
Edge = 0,
Level = 1,
};
enum class DeviceName {
Afi = 0,
Avpc = 1,
Dc = 2,
Dcb = 3,
Hc = 4,
Hda = 5,
Isp2 = 6,
MsencNvenc = 7,
Nv = 8,
Nv2 = 9,
Ppcs = 10,
Sata = 11,
Vi = 12,
Vic = 13,
XusbHost = 14,
XusbDev = 15,
Tsec = 16,
Ppcs1 = 17,
Dc1 = 18,
Sdmmc1a = 19,
Sdmmc2a = 20,
Sdmmc3a = 21,
Sdmmc4a = 22,
Isp2b = 23,
Gpu = 24,
Gpub = 25,
Ppcs2 = 26,
Nvdec = 27,
Ape = 28,
Se = 29,
Nvjpg = 30,
Hc1 = 31,
Se1 = 32,
Axiap = 33,
Etr = 34,
Tsecb = 35,
Tsec1 = 36,
Tsecb1 = 37,
Nvdec1 = 38,
Count,
};
enum class SystemInfoType : u32 {
TotalPhysicalMemorySize = 0,
UsedPhysicalMemorySize = 1,
InitialProcessIdRange = 2,
};
enum class ProcessInfoType : u32 {
ProcessState = 0,
};
struct CreateProcessParameter {
std::array<char, 12> name;
u32 version;
u64 program_id;
u64 code_address;
s32 code_num_pages;
u32 flags;
Handle reslimit;
s32 system_resource_num_pages;
};
static_assert(sizeof(CreateProcessParameter) == 0x30);
} // namespace Kernel::Svc

View File

@@ -722,4 +722,12 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval);
}
// Used by Invalidate/Store/FlushProcessDataCache32
template <Result func(Core::System&, Handle, u64, u64)>
void SvcWrap32(Core::System& system) {
const u64 address = (Param(system, 3) << 32) | Param(system, 2);
const u64 size = (Param(system, 4) << 32) | Param(system, 1);
FuncReturn32(system, func(system, Param32(system, 0), address, size).raw);
}
} // namespace Kernel

View File

@@ -28,30 +28,49 @@ enum class ErrorModule : u32 {
Loader = 9,
CMIF = 10,
HIPC = 11,
TMA = 12,
DMNT = 13,
GDS = 14,
PM = 15,
NS = 16,
BSDSockets = 17,
HTC = 18,
TSC = 19,
NCMContent = 20,
SM = 21,
RO = 22,
GC = 23,
SDMMC = 24,
OVLN = 25,
SPL = 26,
Socket = 27,
HTCLOW = 29,
DDSF = 30,
HTCFS = 31,
Async = 32,
Util = 33,
TIPC = 35,
ANIF = 37,
ETHC = 100,
I2C = 101,
GPIO = 102,
UART = 103,
CPAD = 104,
Settings = 105,
FTM = 106,
WLAN = 107,
XCD = 108,
TMP451 = 109,
NIFM = 110,
Hwopus = 111,
LSM6DS3 = 112,
Bluetooth = 113,
VI = 114,
NFP = 115,
Time = 116,
FGM = 117,
OE = 118,
BH1730FVC = 119,
PCIe = 120,
Friends = 121,
BCAT = 122,
@@ -65,7 +84,7 @@ enum class ErrorModule : u32 {
AHID = 130,
Qlaunch = 132,
PCV = 133,
OMM = 134,
USBPD = 134,
BPC = 135,
PSM = 136,
NIM = 137,
@@ -75,18 +94,22 @@ enum class ErrorModule : u32 {
NSD = 141,
PCTL = 142,
BTM = 143,
LA = 144,
ETicket = 145,
NGC = 146,
ERPT = 147,
APM = 148,
CEC = 149,
Profiler = 150,
ErrorUpload = 151,
LIDBE = 152,
Audio = 153,
NPNS = 154,
NPNSHTTPSTREAM = 155,
ARP = 157,
SWKBD = 158,
BOOT = 159,
NetDiag = 160,
NFCMifare = 161,
UserlandAssert = 162,
Fatal = 163,
@@ -94,17 +117,68 @@ enum class ErrorModule : u32 {
SPSM = 165,
BGTC = 167,
UserlandCrash = 168,
SASBUS = 169,
PI = 170,
AudioCtrl = 172,
LBL = 173,
JIT = 175,
HDCP = 176,
OMM = 177,
PDM = 178,
OLSC = 179,
SREPO = 180,
Dauth = 181,
STDFU = 182,
DBG = 183,
DHCPS = 186,
SPI = 187,
AVM = 188,
PWM = 189,
RTC = 191,
Regulator = 192,
LED = 193,
SIO = 195,
PCM = 196,
CLKRST = 197,
POWCTL = 198,
AudioOld = 201,
HID = 202,
LDN = 203,
CS = 204,
Irsensor = 205,
Capture = 206,
Manu = 208,
ATK = 209,
WEB = 210,
LCS = 211,
GRC = 212,
Repair = 213,
Album = 214,
RID = 215,
Migration = 216,
MigrationLdcServ = 217,
HIDBUS = 218,
ENS = 219,
WebSocket = 223,
DCDMTP = 227,
PGL = 228,
Notification = 229,
INS = 230,
LP2P = 231,
RCD = 232,
LCM40607 = 233,
PRC = 235,
TMAHTC = 237,
ECTX = 238,
MNPP = 239,
HSHL = 240,
CAPMTP = 242,
DP2HDMI = 244,
Cradle = 245,
SProfile = 246,
NDRM = 250,
TSPM = 499,
DevMenu = 500,
GeneralWebApplet = 800,
WifiWebAuthApplet = 809,
WhitelistedApplet = 810,
@@ -423,16 +497,17 @@ constexpr void UpdateCurrentResultReference<const Result>(Result result_referenc
} // namespace ResultImpl
#define DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(COUNTER_VALUE) \
[[maybe_unused]] constexpr bool HasPrevRef_##COUNTER_VALUE = \
[[maybe_unused]] constexpr bool CONCAT2(HasPrevRef_, COUNTER_VALUE) = \
std::same_as<decltype(__TmpCurrentResultReference), Result&>; \
[[maybe_unused]] auto& PrevRef_##COUNTER_VALUE = __TmpCurrentResultReference; \
[[maybe_unused]] Result __tmp_result_##COUNTER_VALUE = ResultSuccess; \
Result& __TmpCurrentResultReference = \
HasPrevRef_##COUNTER_VALUE ? PrevRef_##COUNTER_VALUE : __tmp_result_##COUNTER_VALUE
[[maybe_unused]] Result CONCAT2(PrevRef_, COUNTER_VALUE) = __TmpCurrentResultReference; \
[[maybe_unused]] Result CONCAT2(__tmp_result_, COUNTER_VALUE) = ResultSuccess; \
Result& __TmpCurrentResultReference = CONCAT2(HasPrevRef_, COUNTER_VALUE) \
? CONCAT2(PrevRef_, COUNTER_VALUE) \
: CONCAT2(__tmp_result_, COUNTER_VALUE)
#define ON_RESULT_RETURN_IMPL(...) \
static_assert(std::same_as<decltype(__TmpCurrentResultReference), Result&>); \
auto RESULT_GUARD_STATE_##__COUNTER__ = \
auto CONCAT2(RESULT_GUARD_STATE_, __COUNTER__) = \
ResultImpl::ResultReferenceForScopedResultGuard<__VA_ARGS__>( \
__TmpCurrentResultReference) + \
[&]()

View File

@@ -0,0 +1,177 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/frontend/applets/cabinet.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_cabinet.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfp/nfp_device.h"
namespace Service::AM::Applets {
Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
const Core::Frontend::CabinetApplet& frontend_)
: Applet{system_, applet_mode_}, frontend{frontend_}, system{system_}, service_context{
system_,
"CabinetApplet"} {
availability_change_event =
service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent");
}
Cabinet::~Cabinet() = default;
void Cabinet::Initialize() {
Applet::Initialize();
LOG_INFO(Service_HID, "Initializing Cabinet Applet.");
LOG_DEBUG(Service_HID,
"Initializing Applet with common_args: arg_version={}, lib_version={}, "
"play_startup_sound={}, size={}, system_tick={}, theme_color={}",
common_args.arguments_version, common_args.library_version,
common_args.play_startup_sound, common_args.size, common_args.system_tick,
common_args.theme_color);
const auto storage = broker.PopNormalDataToApplet();
ASSERT(storage != nullptr);
const auto applet_input_data = storage->GetData();
ASSERT(applet_input_data.size() >= sizeof(StartParamForAmiiboSettings));
std::memcpy(&applet_input_common, applet_input_data.data(),
sizeof(StartParamForAmiiboSettings));
}
bool Cabinet::TransactionComplete() const {
return is_complete;
}
Result Cabinet::GetStatus() const {
return ResultSuccess;
}
void Cabinet::ExecuteInteractive() {
ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}
void Cabinet::Execute() {
if (is_complete) {
return;
}
const auto callback = [this](bool apply_changes, const std::string& amiibo_name) {
DisplayCompleted(apply_changes, amiibo_name);
};
// TODO: listen on all controllers
if (nfp_device == nullptr) {
nfp_device = std::make_shared<Service::NFP::NfpDevice>(
system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
nfp_device->Initialize();
nfp_device->StartDetection(Service::NFP::TagProtocol::All);
}
const Core::Frontend::CabinetParameters parameters{
.tag_info = applet_input_common.tag_info,
.register_info = applet_input_common.register_info,
.mode = applet_input_common.applet_mode,
};
switch (applet_input_common.applet_mode) {
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
case Service::NFP::CabinetMode::StartGameDataEraser:
case Service::NFP::CabinetMode::StartRestorer:
case Service::NFP::CabinetMode::StartFormatter:
frontend.ShowCabinetApplet(callback, parameters, nfp_device);
break;
default:
UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
DisplayCompleted(false, {});
break;
}
}
void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) {
Service::Mii::MiiManager manager;
ReturnValueForAmiiboSettings applet_output{};
if (!apply_changes) {
Cancel();
}
if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
Cancel();
}
if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) {
nfp_device->Mount(Service::NFP::MountTarget::All);
}
switch (applet_input_common.applet_mode) {
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: {
Service::NFP::AmiiboName name{};
std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1));
nfp_device->SetNicknameAndOwner(name);
break;
}
case Service::NFP::CabinetMode::StartGameDataEraser:
nfp_device->DeleteApplicationArea();
break;
case Service::NFP::CabinetMode::StartRestorer:
nfp_device->RestoreAmiibo();
break;
case Service::NFP::CabinetMode::StartFormatter:
nfp_device->DeleteAllData();
break;
default:
UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
break;
}
applet_output.device_handle = applet_input_common.device_handle;
applet_output.result = CabinetResult::Cancel;
const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
nfp_device->Finalize();
if (reg_result.IsSuccess()) {
applet_output.result |= CabinetResult::RegisterInfo;
}
if (tag_result.IsSuccess()) {
applet_output.result |= CabinetResult::TagInfo;
}
std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
is_complete = true;
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
broker.SignalStateChanged();
}
void Cabinet::Cancel() {
ReturnValueForAmiiboSettings applet_output{};
applet_output.device_handle = applet_input_common.device_handle;
applet_output.result = CabinetResult::Cancel;
nfp_device->Finalize();
std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
is_complete = true;
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
broker.SignalStateChanged();
}
} // namespace Service::AM::Applets

View File

@@ -0,0 +1,104 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "core/hle/result.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp_types.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Service::NFP {
class NfpDevice;
}
namespace Service::AM::Applets {
enum class CabinetAppletVersion : u32 {
Version1 = 0x1,
};
enum class CabinetResult : u8 {
Cancel = 0,
TagInfo = 1 << 1,
RegisterInfo = 1 << 2,
All = TagInfo | RegisterInfo,
};
DECLARE_ENUM_FLAG_OPERATORS(CabinetResult)
// This is nn::nfp::AmiiboSettingsStartParam
struct AmiiboSettingsStartParam {
u64 device_handle;
std::array<u8, 0x20> param_1;
u8 param_2;
};
static_assert(sizeof(AmiiboSettingsStartParam) == 0x30,
"AmiiboSettingsStartParam is an invalid size");
#pragma pack(push, 1)
// This is nn::nfp::StartParamForAmiiboSettings
struct StartParamForAmiiboSettings {
u8 param_1;
Service::NFP::CabinetMode applet_mode;
u8 flags;
u8 amiibo_settings_1;
u64 device_handle;
Service::NFP::TagInfo tag_info;
Service::NFP::RegisterInfo register_info;
std::array<u8, 0x20> amiibo_settings_3;
INSERT_PADDING_BYTES(0x24);
};
static_assert(sizeof(StartParamForAmiiboSettings) == 0x1A8,
"StartParamForAmiiboSettings is an invalid size");
// This is nn::nfp::ReturnValueForAmiiboSettings
struct ReturnValueForAmiiboSettings {
CabinetResult result;
INSERT_PADDING_BYTES(0x3);
u64 device_handle;
Service::NFP::TagInfo tag_info;
Service::NFP::RegisterInfo register_info;
INSERT_PADDING_BYTES(0x24);
};
static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188,
"ReturnValueForAmiiboSettings is an invalid size");
#pragma pack(pop)
class Cabinet final : public Applet {
public:
explicit Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
const Core::Frontend::CabinetApplet& frontend_);
~Cabinet() override;
void Initialize() override;
bool TransactionComplete() const override;
Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
void DisplayCompleted(bool apply_changes, std::string_view amiibo_name);
void Cancel();
private:
const Core::Frontend::CabinetApplet& frontend;
Core::System& system;
bool is_complete{false};
std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
Kernel::KEvent* availability_change_event;
KernelHelpers::ServiceContext service_context;
StartParamForAmiiboSettings applet_input_common{};
};
} // namespace Service::AM::Applets

View File

@@ -144,6 +144,7 @@ void Error::Initialize() {
break;
default:
UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode);
break;
}
}

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