Compare commits

...

161 Commits

Author SHA1 Message Date
Alessandro Astone
32d9a83f8e pl_u: Fix read out of bounds 2021-02-06 18:44:01 +01:00
bunnei
00cb631b2f Merge pull request #5876 from lioncash/truncation
k_affinity_mask: Avoid implicit truncation to bool
2021-02-04 14:44:11 -08:00
Lioncash
756365386a k_affinity_mask: Avoid implicit truncation to bool
This can cause compiler warnings. Instead, we can explicitly add a
boolean expression around it to naturally turn the result into a bool.
2021-02-04 15:35:46 -05:00
bunnei
eb3afd30b1 Merge pull request #5874 from Morph1984/create-keys-dir
key_manager: Create the keys directory if it does not exist
2021-02-04 11:47:14 -08:00
Morph
806e2d7900 key_manager: Create the keys directory if it does not exist 2021-02-04 06:05:50 -05:00
bunnei
b331bb5210 Merge pull request #5870 from german77/hanheldfix2
config: Always update handheld config
2021-02-03 19:02:59 -08:00
bunnei
cde532cc52 Merge pull request #5863 from ogniK5377/disable-reverb
audren: Disable reverb for the time being
2021-02-03 18:05:54 -08:00
german
c1b81f776c Always update handheld config 2021-02-03 17:48:04 -06:00
bunnei
b0c9752663 Merge pull request #5848 from ogniK5377/k-resourcelimit
kernel: Rewrite resource limit to be more accurate
2021-02-03 14:53:25 -08:00
Chloe Marcec
2c6e940493 Simplify limitableresource names 2021-02-03 12:55:16 +11:00
bunnei
48d040fded Merge pull request #5842 from german77/userfix
acc: Fix error when second user is selected
2021-02-02 15:43:51 -08:00
bunnei
e5a76d728f Merge pull request #5841 from german77/username
Avoid overwriting username
2021-02-02 15:04:09 -08:00
LC
82d232db46 Merge pull request #5866 from Morph1984/log-all-paths
settings: Log the cache, config and mod load directories
2021-02-02 11:07:42 -05:00
Morph
9137f3ec68 settings: Log the cache, config, and mod load directories 2021-02-02 04:01:05 -05:00
bunnei
d4ebc9a120 Merge pull request #5861 from german77/HandheldFix
hid: Only update motion for npad and prevent over scheduling events
2021-02-01 18:46:26 -08:00
Chloe Marcec
64c3582705 Compile error 2021-02-02 13:23:34 +11:00
Chloe Marcec
9e4b2d60bc Address issues 2021-02-02 13:23:00 +11:00
LC
5e49b81d4d Merge pull request #5864 from MerryMage/is-thumb
arm_dynarmic_32: Print out CPSR.T on exception
2021-02-01 13:36:55 -05:00
MerryMage
821fc4a7b6 arm_dynarmic_32: Print out CPSR.T on exception 2021-02-01 18:35:33 +00:00
bunnei
f317b0d354 Merge pull request #5859 from Morph1984/nifm
nifm: Stub GetCurrentNetworkProfile and GetCurrentIpConfigInfo
2021-01-31 21:31:14 -08:00
Ameer J
f614d7d887 Merge pull request #5856 from Morph1984/nifm-fix-getappletinfo-stub
nifm: Fix GetAppletInfo stub
2021-01-31 23:33:11 -05:00
Chloe Marcec
9fc7f60b94 audren: Disable reverb for the time being
As this is causing issues in a few games, it's best to have it disabled until it's completely implemented
2021-02-01 14:41:58 +11:00
bunnei
67d08f14af Merge pull request #5858 from Morph1984/IsGamePlayRecordingSupported-stub
am/IApplicationFunctions: Stub IsGamePlayRecordingSupported
2021-01-31 18:04:04 -08:00
german
2489547dc5 Only update motion for npad and prevent over scheduling events 2021-01-31 18:52:38 -06:00
bunnei
1498ece290 Merge pull request #5860 from Morph1984/prepo-transmission-stub
prepo: Stub RequestImmediateTransmission and GetTransmissionStatus
2021-01-31 12:26:00 -08:00
Morph
f7dc77e03a Merge pull request #5846 from ameerj/analog-join
analog_from_button: Fix update_thread.join exception
2021-01-31 23:06:06 +08:00
Morph
8c7d89e6c7 nifm: Stub GetCurrentIpConfigInfo
- Used by Lets Sing 12
2021-01-31 07:36:37 -05:00
Morph
7d9465d47a prepo: Stub GetTransmissionStatus 2021-01-31 07:07:11 -05:00
Morph
2394807b42 prepo: Stub RequestImmediateTransmission
- Used by Animal Crossing: New Horizons
2021-01-31 07:04:35 -05:00
Morph
4bf1cf1f81 nifm: Stub GetCurrentNetworkProfile
- Used by Minecraft Bedrock Edition
- Used by Bloons TD 5
2021-01-31 06:56:29 -05:00
Morph
70a2065828 nifm: Add several structs 2021-01-31 06:56:29 -05:00
bunnei
ec51f8de82 Merge pull request #5857 from Morph1984/bsd-fix-eventfd-stub
bsd: Fix EventFd stub
2021-01-31 01:41:06 -08:00
Morph
9f6b35e61f am/IApplicationFunctions: Stub IsGamePlayRecordingSupported
Used by RetroArch
2021-01-31 03:11:03 -05:00
Morph
ded094a340 bsd: Fix EventFd stub 2021-01-31 02:57:56 -05:00
bunnei
1cc009a996 Merge pull request #5855 from Morph1984/bsd-fix-getsockopt-stub
bsd: Fix GetSockOpt stub
2021-01-30 23:21:21 -08:00
Morph
9914db8daa nifm: Fix GetAppletInfo stub 2021-01-31 02:19:36 -05:00
Morph
94f660d1cb Merge pull request #5851 from ameerj/pop-inv-stub
am: Stub TryPopFromFriendInvitationStorageChannel
2021-01-31 14:18:40 +08:00
Morph
6cc769065d bsd: Fix GetSockOpt stub 2021-01-31 01:08:56 -05:00
bunnei
360ea64738 Merge pull request #5852 from ameerj/event-fd-stub
bsd: Stub EventFd
2021-01-30 21:38:08 -08:00
ameerj
ad146eca60 bsd: Stub EventFd
Used by Family Feud
2021-01-30 21:47:32 -05:00
ameerj
99dcf7da21 am: Stub TryPopFromFriendInvitationStorageChannel
Used by Family Feud
2021-01-30 21:43:55 -05:00
Chloe Marcec
ee333e063d fix compile error 2021-01-30 21:51:22 +11:00
Chloe Marcec
56742c6222 cleanup commenting 2021-01-30 21:20:35 +11:00
Chloe Marcec
7791cfd960 Drop m_ from lock 2021-01-30 21:19:49 +11:00
Chloe Marcec
3bf62c7a8a Move to GetGlobalTimeNs, fix GetTotalPhysicalMemoryAvailable 2021-01-30 21:03:10 +11:00
Chloe Marcec
3be1a565f8 kernel: Rewrite resource limit to be more accurate
Matches closer to hardware
2021-01-30 20:40:49 +11:00
bunnei
a4526c4e1a Merge pull request #5779 from bunnei/kthread-rewrite
Rewrite KThread to be more accurate
2021-01-29 23:06:40 -08:00
bunnei
5861bacafd Merge pull request #5795 from ReinUsesLisp/bytes-to-map-end
video_core/memory_manager: Add BytesToMapEnd
2021-01-29 22:56:29 -08:00
Morph
f67cfebada Merge pull request #5838 from german77/prepostub
prepo: Stub GetSystemSessionId
2021-01-30 14:19:59 +08:00
bunnei
aaf834ffa6 Merge pull request #5847 from bunnei/update-dynarmic
externals: Dynarmic: Update to latest to include A32 ISB hook.
2021-01-29 21:52:25 -08:00
german
9150b8972e Stub GetSystemSessionId 2021-01-29 18:41:28 -06:00
bunnei
650734cc3e Merge pull request #5805 from german77/HandheldFix
Fix connect and disconnect controller events
2021-01-29 16:39:01 -08:00
bunnei
15d3376399 externals: Dynarmic: Update to latest to include A32 ISB hook.
- Fixes perf. issues with Megadimension Neptunia VII.
2021-01-29 16:36:45 -08:00
ameerj
ef8b3623f5 analog_from_button: Fix update_thread.join exception
This commit aims to address an exception that occurs when trying to join the Analog object's update_thread.

By using an atomic bool for the status of the update thread, we ensure its value is consistent across the threads accessing it.
2021-01-29 18:41:22 -05:00
bunnei
b786568c5a Merge pull request #5809 from ogniK5377/FlushAudioOutBuffers
audout: FlushAudioOutBuffers
2021-01-28 21:54:46 -08:00
bunnei
543e212554 hle: kernel: KLightLock: Fix several bugs. 2021-01-28 21:53:21 -08:00
bunnei
8d1afcb90f common: common_funcs: Change R_UNLESS to LOG_ERROR. 2021-01-28 21:51:16 -08:00
bunnei
c8fe8247ee arm: dynarmic: Reintroduce JIT checks on SaveContext/LoadContext. 2021-01-28 21:50:39 -08:00
bunnei
e24c6dab93 hle: kernel: KThread: Release thread resource on thread exit. 2021-01-28 21:49:47 -08:00
bunnei
10738839ad yuzu: debugger: Ignore HLE threads. 2021-01-28 21:42:27 -08:00
bunnei
3856564727 hle: kernel: process: Add state lock. 2021-01-28 21:42:26 -08:00
bunnei
ff46ef7ea3 hle: kernel: threading: Fix bug with host thread naming. 2021-01-28 21:42:26 -08:00
bunnei
6ee8340a6b hle: kernel: k_scheduler_lock: Cleanup. 2021-01-28 21:42:26 -08:00
bunnei
055194d2ab core: arm: Remove unnecessary JIT checks. 2021-01-28 21:42:26 -08:00
bunnei
091e9e8c41 common: common_funcs: Log error on R_UNLESS. 2021-01-28 21:42:26 -08:00
bunnei
6e953f7f02 hle: kernel: Allocate a dummy KThread for each host thread, and use it for scheduling. 2021-01-28 21:42:26 -08:00
bunnei
37f74d8741 hle: kernel: k_scheduler: Use atomics for current_thread, etc. 2021-01-28 21:42:26 -08:00
bunnei
f6b10fad63 hle: kernel: k_scheduler: Fix for single core mode. 2021-01-28 21:42:26 -08:00
bunnei
0a1449e04b kernel: Fix build errors. 2021-01-28 21:42:26 -08:00
bunnei
89a5ae92bd core: cpu_manager: Remove unused variable. 2021-01-28 21:42:26 -08:00
bunnei
ca78f77827 hle: kernel: KScheduler: Introduce thread context_guard. 2021-01-28 21:42:26 -08:00
bunnei
cdd14b03e5 hle: kernel: Recode implementation of KThread to be more accurate. 2021-01-28 21:42:26 -08:00
bunnei
1470338458 kernel: svc_types: Add ThreadActivity. 2021-01-28 21:42:26 -08:00
bunnei
1772ebeb1e kernel: KSchedulerPriorityQueue: Lowest priority should be LowestThreadPriority. 2021-01-28 21:42:26 -08:00
bunnei
1f99f5473c kernel: k_light_lock: Simplify EmuThreadHandle implementation. 2021-01-28 21:42:26 -08:00
bunnei
c0f5830323 hle: kernel: TimeManager: Simplify to not rely on previous EmuThreadHandle implementation. 2021-01-28 21:42:26 -08:00
bunnei
bb966d3e33 common: common_funcs: Add useful kernel macro R_SUCCEED_IF. 2021-01-28 21:42:26 -08:00
bunnei
ff186b2498 core: hle: kernel: object: Implement Finalize() virtual method. 2021-01-28 21:42:26 -08:00
bunnei
33b4930280 core: hle: kernel: svc_results: Populate with several missing error codes. 2021-01-28 21:42:26 -08:00
bunnei
5a4fc4a529 core: hle: kernel: Implement KLightLock. 2021-01-28 21:42:26 -08:00
bunnei
97129bc742 core: hle: kernel: Implement KThreadQueue. 2021-01-28 21:42:25 -08:00
bunnei
2fb77adb9f common: common_funcs: Add a few more useful macros for kernel code. 2021-01-28 21:42:25 -08:00
bunnei
4dbf3f4880 hle: kernel: KThread: Clean up thread priorities. 2021-01-28 21:42:25 -08:00
bunnei
1e55498110 hle: kernel: KThread: Reorganize thread priority defaults. 2021-01-28 21:42:25 -08:00
bunnei
0530292b97 hle: kernel: KThread: Fix ThreadType definition. 2021-01-28 21:42:25 -08:00
bunnei
4782985013 hle: kernel: Move single core "phantom mode" out of KThread.
- This is a workaround that does not belong in a kernel primitive.
2021-01-28 21:42:25 -08:00
bunnei
eea346ba8e hle: kernel: KThread: Remove thread types that do not exist. 2021-01-28 21:42:25 -08:00
bunnei
9a4e148f9e arm: arm_dynarmic: Skip calls when JIT is invalid.
- This can happen if called from an idle or suspension thread.
2021-01-28 21:42:25 -08:00
bunnei
c0d3aef28c core: hle: kernel: Rename Thread to KThread. 2021-01-28 21:42:25 -08:00
german
8ba0cac71c Fix user changing to 0 if valid 2021-01-28 21:02:25 -06:00
bunnei
df41e78205 Merge pull request #5837 from german77/socketstub
sockets: Stub GetSockOpt
2021-01-28 16:28:11 -08:00
german
7b8fa78c65 Avoid overwritting username 2021-01-28 16:58:53 -06:00
LC
c21ce728c2 Merge pull request #5831 from MerryMage/isb
dynarmic: Do not flush cache when executing an ISB instruction
2021-01-28 10:54:48 -05:00
LC
16818e952c Merge pull request #5836 from ReinUsesLisp/unaligned-constr-sched
vk_scheduler: Fix unaligned placement new expressions
2021-01-28 10:53:15 -05:00
LC
9f6290d207 Merge pull request #5840 from Morph1984/prepo-fix
prepo: Fix BufferDescriptorX invalid buffer index errors and add New variants of SaveReport
2021-01-28 10:52:44 -05:00
LC
0e125dfd43 Merge pull request #5835 from Morph1984/cleanup-sixaxis-fusion
hid: Cleanup SixAxisFusionParameters
2021-01-28 10:51:04 -05:00
Morph
4921ba05db hid: Add static_assert for Parameter size 2021-01-28 09:41:43 -05:00
Morph
ae6b3bdfbf prepo: Fix BufferDescriptorX invalid buffer errors and add "New" variants of SaveReport
The second input buffer could be optional when prepo/srepo is called, test for the availability of the second buffer prior to reading from it.
2021-01-28 01:32:24 -05:00
Morph
008afa5d59 hle_ipc: Add Can(Read, Write)Buffer
Allows us to test whether a buffer can be read from or written to memory
2021-01-28 01:32:24 -05:00
german
659b5f8088 Stub GetSockOpt 2021-01-27 23:18:20 -06:00
ReinUsesLisp
9e88ad8da9 vk_scheduler: Fix unaligned placement new expressions
We were accidentaly creating an object in an unaligned memory address.
Fix this by manually aligning the offset.
2021-01-27 22:28:22 -03:00
bunnei
45b13c3037 Merge pull request #5786 from ReinUsesLisp/glsl-cbuf
gl_shader_decompiler: Fix constant buffer size calculation
2021-01-27 15:27:53 -08:00
Rodrigo Locatti
ef6cc3aa1d vulkan_device: Blacklist Intel from float16 math (#5798)
Astral Chain crashes Intel's SPIR-V compiler when using fp16.
Disable this while the vendor works on a fix.
2021-01-27 13:31:32 -08:00
bunnei
28b822fe38 Merge pull request #5778 from ReinUsesLisp/shader-dir
renderer_opengl: Avoid precompiled cache and force NV GL cache directory
2021-01-27 11:34:21 -08:00
Morph
d4d39aa4c7 npad: Remove unused device handle parameter 2021-01-27 13:05:31 -05:00
bunnei
fb0fe3b8c3 Merge pull request #5812 from german77/StubSixaxisFusion
HID: Stub Set/Get/Reset SixaxisSensorFusionParameters
2021-01-26 15:58:04 -08:00
MerryMage
2350b76a91 externals: Update dynarmic to 0f27368f 2021-01-26 23:46:49 +00:00
bunnei
09b6f03592 Merge pull request #5810 from ogniK5377/stereo-vision
hle: Implement remaining services for Stereo Vision
2021-01-26 15:01:01 -08:00
bunnei
72c1cb85f1 Merge pull request #5824 from ogniK5377/IPsmSession
psm: IPsmSession
2021-01-26 11:11:29 -08:00
bunnei
64a5548454 Merge pull request #5774 from ogniK5377/mii-raw-random
mii: Fix BuildRandomStoreData & Cleanup raw_data
2021-01-25 16:41:20 -08:00
bunnei
81a037df9d Merge pull request #5771 from ogniK5377/lm-rework
lm: Recode LM service
2021-01-25 10:18:09 -08:00
Chloe Marcec
2c57f0fbd5 Omit system reference 2021-01-25 23:13:37 +11:00
Chloe Marcec
04e9486651 psm: IPsmSession
Used by homebrew menu
2021-01-25 21:37:51 +11:00
bunnei
2a2ee62cfd Merge pull request #5799 from ogniK5377/event-register-unregister
nvdrv: Unregister already registered events
2021-01-24 23:19:10 -08:00
bunnei
62766b1326 Merge pull request #5785 from ReinUsesLisp/buffer-dma
video_core/memory_manager: Flush destination buffer on CopyBlock
2021-01-24 22:57:00 -08:00
Rodrigo Locatti
5dc021d15b Merge pull request #5823 from ReinUsesLisp/revert-flags
Revert "Start of Integer flags implementation"
2021-01-25 03:35:48 -03:00
ReinUsesLisp
34c3ec2f8c Revert "Start of Integer flags implementation"
This reverts #4713. The implementation in that PR is not accurate.
It does not reflect the behavior seen in hardware.
2021-01-25 02:48:03 -03:00
bunnei
45e117b043 Merge pull request #5819 from ReinUsesLisp/cull-mode-cast
vk_graphics_pipeline: Fix narrowing conversion on MSVC
2021-01-24 16:58:38 -08:00
ReinUsesLisp
9dc4a80b17 vk_graphics_pipeline: Fix narrowing conversion on MSVC 2021-01-24 21:41:29 -03:00
LC
df0d8c45d2 Merge pull request #5807 from ReinUsesLisp/vc-warnings
video_core: Silence the remaining gcc warnings and enforce them
2021-01-24 17:36:43 -05:00
Rodrigo Locatti
b769b1be26 Merge pull request #5363 from ReinUsesLisp/vk-image-usage
vk_texture_cache: Support image store on sRGB images with VkImageViewUsageCreateInfo
2021-01-24 18:44:51 -03:00
bunnei
44c5ea3639 Merge pull request #5151 from comex/xx-vfs
vfs_real: When moving files or directories, don't assume file opening will succeed
2021-01-24 13:42:51 -08:00
ReinUsesLisp
6b00443bc1 vk_texture_cache: Support image store on sRGB images with VkImageViewUsageCreateInfo
Vulkan 1.0 didn't support creating sRGB image views on an ABGR8 VkImage
with storage usage bits. VK_KHR_maintenance2 addressed this allowing to
reduce the usage bits on a VkImageView.

To allow image store on non-sRGB image views when the VkImage is created
with sRGB, always create VkImages without sRGB and add the sRGB format
on the view.
2021-01-24 18:16:43 -03:00
LC
8959f3521f Merge pull request #5814 from ReinUsesLisp/remove-rdna-dynstate
vulkan_device: Lift VK_EXT_extended_dynamic_state blacklist on RDNA
2021-01-24 15:54:30 -05:00
ReinUsesLisp
6a0143400f vulkan_device: Lift VK_EXT_extended_dynamic_state blacklist on RDNA
It seems to be safe to use this on new drivers.
2021-01-24 20:21:11 -03:00
ReinUsesLisp
748551dafb cmake: Enforce -Warray-bounds and -Wmissing-field-initializers globally 2021-01-24 17:31:29 -03:00
bunnei
19c14589d3 Merge pull request #5796 from ReinUsesLisp/vertex-a-bypass-vk
vk_pipeline_cache: Properly bypass VertexA shaders
2021-01-24 11:22:58 -08:00
german
a8245cf2f1 Stub Set/Get/Reset SixaxisSensorFusionParameters 2021-01-24 11:28:52 -06:00
Chloe Marcec
2afc1060ef Print Process ID and Thread ID as hex 2021-01-25 02:47:40 +11:00
Chloe Marcec
5882cc0502 hle: Implement remaining services for Stereo Vision
Used by Zelda Breath of the Wild, Super Mario Odyssey and Nintendo Labo
2021-01-25 00:34:01 +11:00
LC
04dcada85f Merge pull request #5808 from ReinUsesLisp/glslang-quiet
host_shaders/cmake: Pass --quiet to glslang to keep it quiet
2021-01-24 05:01:10 -05:00
Chloe Marcec
b2b95e96c1 audout: FlushAudioOutBuffers
Fixes Devil May Cry
2021-01-24 19:13:34 +11:00
ReinUsesLisp
f81c783b5b host_shaders/cmake: Pass --quiet to glslang to keep it quiet
Silences noisy builds on toolchains.
2021-01-24 04:55:23 -03:00
ReinUsesLisp
cc4335a9c6 video_core/cmake: Enforce -Warray-bounds and -Wmissing-field-initializers 2021-01-24 04:42:41 -03:00
bunnei
f7ac4e1eb4 Merge pull request #5806 from bunnei/am-stub
hle: service: am: Stub ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero.
2021-01-23 23:37:05 -08:00
ReinUsesLisp
1b76e7e890 video_core: Silence -Wmissing-field-initializers warnings 2021-01-24 04:32:19 -03:00
ReinUsesLisp
80a673a27f maxwell_3d: Silence array bounds warnings 2021-01-24 04:31:41 -03:00
ReinUsesLisp
ad48259d7e maxwell_to_vk: Silence -Wextra warnings about using different enum types 2021-01-24 04:03:36 -03:00
german
3b4da2d7fa Fix connect and disconnect controller events 2021-01-23 22:59:44 -06:00
comex
e9bb95ae16 vfs_real: When moving files or directories, don't assume file opening will succeed
Found this via a warning, but it's a substantive fix.

Since this is only for a cache, it should be safe to silently drop the
entry if opening fails.  I think.
2021-01-23 16:19:29 -05:00
Levi Behunin
9477d23d70 shader_ir: Fix comment typo 2021-01-23 13:16:37 -05:00
LC
bfd2bcb068 Merge pull request #5800 from Morph1984/max-vibration-duration
sdl_impl: Set the maximum vibration duration to 1 second
2021-01-23 10:46:18 -05:00
Morph
5942d206c2 sdl_impl: Set the maximum vibration duration to 1 second 2021-01-23 08:06:07 -05:00
Chloe Marcec
822edff5bd Simplify condition 2021-01-23 22:12:05 +11:00
Chloe Marcec
3b0458a7a5 nvdrv: Unregister already registered events 2021-01-23 22:02:14 +11:00
LC
65f821850e Merge pull request #5797 from ReinUsesLisp/nsight-aftermath-build
nsight_aftermath_tracker: Fix build issues when enabled
2021-01-23 05:40:50 -05:00
Chloe Marcec
df42100320 Clamp string reads to buffer size 2021-01-23 18:24:57 +11:00
bunnei
12355cbf02 Merge pull request #5776 from ogniK5377/lbl
lbl: Implement most of lbl
2021-01-22 23:13:23 -08:00
ReinUsesLisp
37ef2ee595 vk_pipeline_cache: Properly bypass VertexA shaders
The VertexA stage is not yet implemented, but Vulkan is adding its
descriptors, causing a discrepancy in the pushed descriptors and the
template. This generally ends up in a driver side crash.

Bypass the VertexA stage for now.
2021-01-23 03:59:59 -03:00
ReinUsesLisp
bda177ef40 video_core/memory_manager: Add BytesToMapEnd
Track map address sizes in a flat ordered map and add a method to query
the number of bytes until the end of a map in a given address.
2021-01-22 18:31:12 -03:00
ReinUsesLisp
436457b6e7 gl_shader_decompiler: Fix constant buffer size calculation
The divide logic was wrong and can cause an uniform buffer size
overflow.
2021-01-21 19:47:41 -03:00
ReinUsesLisp
b7febb5625 video_core/memory_manager: Remove unused CopyBlockUnsafe
This function was not being used.
2021-01-21 19:16:06 -03:00
ReinUsesLisp
0e9a6759f9 video_core/memory_manager: Flush destination buffer on CopyBlock
When we copy into a buffer, it might contain data modified from the GPU
on the same pages. Because of this, we have to flush the contents before
writing new data.

An alternative approach would be to write the data in place, but games
can also write data in other ways, invalidating our contents.

Fixes geometry in Zombie Panic in Wonderland DX.
2021-01-21 19:16:06 -03:00
ReinUsesLisp
dd790abab0 video_core/memory_manager: Add GPU address based flush method
Allow flushing rasterizer contents based on a GPU address.
2021-01-21 19:16:05 -03:00
bunnei
f83ef80ebd hle: service: am: Stub ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero.
- Used by Monster Hunter Rise demo.
2021-01-20 20:35:12 -08:00
ReinUsesLisp
51512d01d8 renderer_opengl: Avoid precompiled cache and force NV GL cache directory
Setting __GL_SHADER_DISK_CACHE_PATH we can force the cache directory to
be in yuzu's user directory to stop commonly distributed malware from
deleting our driver shader cache. And by setting
__GL_SHADER_DISK_CACHE_SKIP_CLEANUP we can have an unbounded shader
cache size.

This has only been implemented on Windows, mostly because previous tests
didn't seem to work on Linux.

Disable the precompiled cache on Nvidia's driver. There's no need to
hide information the driver already has in its own cache.
2021-01-21 00:41:03 -03:00
Chloe Marcec
c795207fb2 lbl: Implement most of lbl
Pretty basic service, only thing left to do is handle setting applying once set:sys is implemented
2021-01-21 00:46:03 +11:00
Chloe Marcec
5b8bc56e65 mii: Fix BuildRandomStoreData & Cleanup raw_data
Cleaned up mii raw data to reflect the underlying values instead of just a chunk of bytes.
Fixed BuildRandomStoreData not actually generating random miis properly. "values" should be a u32, not a u8.
2021-01-20 21:53:57 +11:00
Chloe Marcec
dc18a1261c Mark DestinationToString as static 2021-01-20 18:42:39 +11:00
Chloe Marcec
dca2e2c8f1 Mark LogPacketHeaderEntry hash as noexcept 2021-01-20 18:35:58 +11:00
Chloe Marcec
83f8c1a25e lm: Recode LM service
Rework the service to spit out to logs instead of a seperate file as well as fix any crashes caused by lm.
2021-01-20 18:25:15 +11:00
141 changed files with 6566 additions and 5131 deletions

View File

@@ -64,8 +64,10 @@ if (MSVC)
else()
add_compile_options(
-Wall
-Werror=array-bounds
-Werror=implicit-fallthrough
-Werror=missing-declarations
-Werror=missing-field-initializers
-Werror=reorder
-Werror=switch
-Werror=uninitialized

View File

@@ -383,11 +383,14 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E
const auto channel_count = params.channel_count;
for (s32 i = 0; i < channel_count; i++) {
// TODO(ogniK): Actually implement reverb
/*
if (params.input[i] != params.output[i]) {
const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
}
}*/
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
std::memset(output, 0, worker_params.sample_count * sizeof(s32));
}
}

View File

@@ -51,6 +51,14 @@ void Stream::Stop() {
UNIMPLEMENTED();
}
bool Stream::Flush() {
const bool had_buffers = !queued_buffers.empty();
while (!queued_buffers.empty()) {
queued_buffers.pop();
}
return had_buffers;
}
void Stream::SetVolume(float volume) {
game_volume = volume;
}

View File

@@ -56,6 +56,9 @@ public:
/// Queues a buffer into the audio stream, returns true on success
bool QueueBuffer(BufferPtr&& buffer);
/// Flush audio buffers
bool Flush();
/// Returns true if the audio stream contains a buffer with the specified tag
[[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const;

View File

@@ -138,6 +138,8 @@ add_library(common STATIC
microprofile.h
microprofileui.h
misc.cpp
nvidia_flags.cpp
nvidia_flags.h
page_table.cpp
page_table.h
param_package.cpp

View File

@@ -97,10 +97,27 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
#define R_UNLESS(expr, res) \
{ \
if (!(expr)) { \
if (res.IsError()) { \
LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \
} \
return res; \
} \
}
#define R_SUCCEEDED(res) (res.IsSuccess())
/// Evaluates an expression that returns a result, and returns the result if it would fail.
#define R_TRY(res_expr) \
{ \
const auto _tmp_r_try_rc = (res_expr); \
if (_tmp_r_try_rc.IsError()) { \
return _tmp_r_try_rc; \
} \
}
/// Evaluates a boolean expression, and succeeds if that expression is true.
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), RESULT_SUCCESS)
namespace Common {
[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {

View File

@@ -0,0 +1,27 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <filesystem>
#include <stdlib.h>
#include <fmt/format.h>
#include "common/file_util.h"
#include "common/nvidia_flags.h"
namespace Common {
void ConfigureNvidiaEnvironmentFlags() {
#ifdef _WIN32
const std::string shader_path = Common::FS::SanitizePath(
fmt::format("{}/nvidia/", Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)));
const std::string windows_path =
Common::FS::SanitizePath(shader_path, Common::FS::DirectorySeparator::BackwardSlash);
void(Common::FS::CreateFullPath(shader_path + '/'));
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path).c_str()));
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
#endif
}
} // namespace Common

10
src/common/nvidia_flags.h Normal file
View File

@@ -0,0 +1,10 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
namespace Common {
/// Configure platform specific flags for Nvidia's driver
void ConfigureNvidiaEnvironmentFlags();
} // namespace Common

View File

@@ -160,7 +160,12 @@ add_library(core STATIC
hle/kernel/k_affinity_mask.h
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
hle/kernel/k_light_condition_variable.h
hle/kernel/k_light_lock.cpp
hle/kernel/k_light_lock.h
hle/kernel/k_priority_queue.h
hle/kernel/k_resource_limit.cpp
hle/kernel/k_resource_limit.h
hle/kernel/k_scheduler.cpp
hle/kernel/k_scheduler.h
hle/kernel/k_scheduler_lock.h
@@ -168,6 +173,9 @@ add_library(core STATIC
hle/kernel/k_scoped_scheduler_lock_and_sleep.h
hle/kernel/k_synchronization_object.cpp
hle/kernel/k_synchronization_object.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
hle/kernel/k_thread_queue.h
hle/kernel/kernel.cpp
hle/kernel/kernel.h
hle/kernel/memory/address_space_info.cpp
@@ -198,8 +206,6 @@ add_library(core STATIC
hle/kernel/process_capability.h
hle/kernel/readable_event.cpp
hle/kernel/readable_event.h
hle/kernel/resource_limit.cpp
hle/kernel/resource_limit.h
hle/kernel/server_port.cpp
hle/kernel/server_port.h
hle/kernel/server_session.cpp
@@ -216,8 +222,6 @@ add_library(core STATIC
hle/kernel/svc_results.h
hle/kernel/svc_types.h
hle/kernel/svc_wrap.h
hle/kernel/thread.cpp
hle/kernel/thread.h
hle/kernel/time_manager.cpp
hle/kernel/time_manager.h
hle/kernel/transfer_memory.cpp
@@ -400,8 +404,6 @@ add_library(core STATIC
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp
hle/service/lm/lm.h
hle/service/lm/manager.cpp
hle/service/lm/manager.h
hle/service/mig/mig.cpp
hle/service/mig/mig.h
hle/service/mii/manager.cpp

View File

@@ -71,8 +71,9 @@ public:
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
exception, pc, MemoryReadCode(pc));
LOG_CRITICAL(Core_ARM,
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
UNIMPLEMENTED();
}
@@ -255,6 +256,9 @@ void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) {
}
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
if (!jit) {
return;
}
Dynarmic::A32::Context context;
jit->SaveContext(context);
ctx.cpu_registers = context.Regs();
@@ -264,6 +268,9 @@ void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
}
void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
if (!jit) {
return;
}
Dynarmic::A32::Context context;
context.Regs() = ctx.cpu_registers;
context.ExtRegs() = ctx.extension_registers;

View File

@@ -50,6 +50,10 @@ public:
u64 GetTPIDR_EL0() const override;
void ChangeProcessorID(std::size_t new_core_id) override;
bool IsInThumbMode() const {
return (GetPSTATE() & 0x20) != 0;
}
void SaveContext(ThreadContext32& ctx) override;
void SaveContext(ThreadContext64& ctx) override {}
void LoadContext(const ThreadContext32& ctx) override;

View File

@@ -294,6 +294,9 @@ void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) {
}
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
if (!jit) {
return;
}
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
ctx.pc = jit->GetPC();
@@ -305,6 +308,9 @@ void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
}
void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
if (!jit) {
return;
}
jit->SetRegisters(ctx.cpu_registers);
jit->SetSP(ctx.sp);
jit->SetPC(ctx.pc);

View File

@@ -28,15 +28,14 @@
#include "core/hardware_interrupt_manager.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/apm/controller.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/manager.h"
#include "core/hle/service/lm/manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h"
@@ -293,8 +292,6 @@ struct System::Impl {
perf_stats->GetMeanFrametime());
}
lm_manager.Flush();
is_powered_on = false;
exit_lock = false;
@@ -398,7 +395,6 @@ struct System::Impl {
/// Service State
Service::Glue::ARPManager arp_manager;
Service::LM::Manager lm_manager{reporter};
Service::Time::TimeManager time_manager;
/// Service manager
@@ -720,14 +716,6 @@ const Service::APM::Controller& System::GetAPMController() const {
return impl->apm_controller;
}
Service::LM::Manager& System::GetLogManager() {
return impl->lm_manager;
}
const Service::LM::Manager& System::GetLogManager() const {
return impl->lm_manager;
}
Service::Time::TimeManager& System::GetTimeManager() {
return impl->time_manager;
}

View File

@@ -62,10 +62,6 @@ namespace Glue {
class ARPManager;
}
namespace LM {
class Manager;
} // namespace LM
namespace SM {
class ServiceManager;
} // namespace SM
@@ -351,9 +347,6 @@ public:
[[nodiscard]] Service::APM::Controller& GetAPMController();
[[nodiscard]] const Service::APM::Controller& GetAPMController() const;
[[nodiscard]] Service::LM::Manager& GetLogManager();
[[nodiscard]] const Service::LM::Manager& GetLogManager() const;
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;

View File

@@ -11,9 +11,9 @@
#include "core/core_timing.h"
#include "core/cpu_manager.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/thread.h"
#include "video_core/gpu.h"
namespace Core {
@@ -147,7 +147,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
while (true) {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
@@ -208,7 +208,6 @@ void CpuManager::SingleCoreRunGuestThread() {
void CpuManager::SingleCoreRunGuestLoop() {
auto& kernel = system.Kernel();
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
system.EnterDynarmicProfile();
@@ -217,9 +216,9 @@ void CpuManager::SingleCoreRunGuestLoop() {
physical_core = &kernel.CurrentPhysicalCore();
}
system.ExitDynarmicProfile();
thread->SetPhantomMode(true);
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
thread->SetPhantomMode(false);
kernel.SetIsPhantomModeForSingleCore(false);
physical_core->ArmInterface().ClearExclusiveState();
PreemptSingleCore();
auto& scheduler = kernel.Scheduler(current_core);
@@ -245,7 +244,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
while (true) {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
@@ -255,22 +254,23 @@ void CpuManager::SingleCoreRunSuspendThread() {
void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
{
auto& scheduler = system.Kernel().Scheduler(current_core);
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
auto& kernel = system.Kernel();
auto& scheduler = kernel.Scheduler(current_core);
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
if (idle_count >= 4 || from_running_enviroment) {
if (!from_running_enviroment) {
system.CoreTiming().Idle();
idle_count = 0;
}
current_thread->SetPhantomMode(true);
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
current_thread->SetPhantomMode(false);
kernel.SetIsPhantomModeForSingleCore(false);
}
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
system.CoreTiming().ResetTicks();
scheduler.Unload(scheduler.GetCurrentThread());
auto& next_scheduler = system.Kernel().Scheduler(current_core);
auto& next_scheduler = kernel.Scheduler(current_core);
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
}
@@ -278,8 +278,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
{
auto& scheduler = system.Kernel().Scheduler(current_core);
scheduler.Reload(scheduler.GetCurrentThread());
auto* currrent_thread2 = scheduler.GetCurrentThread();
if (!currrent_thread2->IsIdleThread()) {
if (!scheduler.IsIdle()) {
idle_count = 0;
}
}

View File

@@ -568,6 +568,11 @@ KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
if (!Common::FS::Exists(yuzu_keys_dir)) {
Common::FS::CreateDir(yuzu_keys_dir);
}
if (Settings::values.use_dev_keys) {
dev_mode = true;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);

View File

@@ -133,8 +133,11 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
}
cache.erase(old_path);
file->Open(new_path, "r+b");
cache.insert_or_assign(new_path, std::move(file));
if (file->Open(new_path, "r+b")) {
cache.insert_or_assign(new_path, std::move(file));
} else {
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
}
} else {
UNREACHABLE();
return nullptr;
@@ -214,9 +217,12 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
}
auto file = cached.lock();
file->Open(file_new_path, "r+b");
cache.erase(file_old_path);
cache.insert_or_assign(std::move(file_new_path), std::move(file));
if (file->Open(file_new_path, "r+b")) {
cache.insert_or_assign(std::move(file_new_path), std::move(file));
} else {
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path);
}
}
return OpenDirectory(new_path, Mode::ReadWrite);

View File

@@ -4,8 +4,10 @@
#pragma once
#include <array>
#include <tuple>
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Core {
@@ -18,34 +20,12 @@ constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch cpu frequency is 1020MHz u
constexpr u64 CNTFREQ = 19200000; // Switch's hardware clock speed
constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores
} // namespace Hardware
constexpr u32 INVALID_HOST_THREAD_ID = 0xFFFFFFFF;
struct EmuThreadHandle {
u32 host_handle;
u32 guest_handle;
u64 GetRaw() const {
return (static_cast<u64>(host_handle) << 32) | guest_handle;
}
bool operator==(const EmuThreadHandle& rhs) const {
return std::tie(host_handle, guest_handle) == std::tie(rhs.host_handle, rhs.guest_handle);
}
bool operator!=(const EmuThreadHandle& rhs) const {
return !operator==(rhs);
}
static constexpr EmuThreadHandle InvalidHandle() {
constexpr u32 invalid_handle = 0xFFFFFFFF;
return {invalid_handle, invalid_handle};
}
bool IsInvalid() const {
return (*this) == InvalidHandle();
}
// Virtual to Physical core map.
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
};
} // namespace Hardware
} // namespace Core

View File

@@ -51,6 +51,8 @@ public:
*/
void ConnectionClosed();
void Finalize() override {}
private:
std::shared_ptr<ServerPort> server_port; ///< ServerPort associated with this client port.
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have

View File

@@ -5,9 +5,9 @@
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
namespace Kernel {
@@ -38,7 +38,7 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern
return MakeResult(std::move(client_session));
}
ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread,
ResultCode ClientSession::SendSyncRequest(std::shared_ptr<KThread> thread,
Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing) {
// Keep ServerSession alive until we're done working with it.

View File

@@ -24,7 +24,7 @@ namespace Kernel {
class KernelCore;
class Session;
class Thread;
class KThread;
class ClientSession final : public KSynchronizationObject {
public:
@@ -46,11 +46,13 @@ public:
return HANDLE_TYPE;
}
ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
ResultCode SendSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing);
bool IsSignaled() const override;
void Finalize() override {}
private:
static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,
std::shared_ptr<Session> parent,

View File

@@ -17,12 +17,12 @@ GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel)
GlobalSchedulerContext::~GlobalSchedulerContext() = default;
void GlobalSchedulerContext::AddThread(std::shared_ptr<Thread> thread) {
void GlobalSchedulerContext::AddThread(std::shared_ptr<KThread> thread) {
std::scoped_lock lock{global_list_guard};
thread_list.push_back(std::move(thread));
}
void GlobalSchedulerContext::RemoveThread(std::shared_ptr<Thread> thread) {
void GlobalSchedulerContext::RemoveThread(std::shared_ptr<KThread> thread) {
std::scoped_lock lock{global_list_guard};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());

View File

@@ -12,7 +12,8 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/svc_types.h"
namespace Kernel {
@@ -20,8 +21,12 @@ class KernelCore;
class SchedulerLock;
using KSchedulerPriorityQueue =
KPriorityQueue<Thread, Core::Hardware::NUM_CPU_CORES, THREADPRIO_LOWEST, THREADPRIO_HIGHEST>;
constexpr s32 HighestCoreMigrationAllowedPriority = 2;
KPriorityQueue<KThread, Core::Hardware::NUM_CPU_CORES, Svc::LowestThreadPriority,
Svc::HighestThreadPriority>;
static constexpr s32 HighestCoreMigrationAllowedPriority = 2;
static_assert(Svc::LowestThreadPriority >= HighestCoreMigrationAllowedPriority);
static_assert(Svc::HighestThreadPriority <= HighestCoreMigrationAllowedPriority);
class GlobalSchedulerContext final {
friend class KScheduler;
@@ -33,13 +38,13 @@ public:
~GlobalSchedulerContext();
/// Adds a new thread to the scheduler
void AddThread(std::shared_ptr<Thread> thread);
void AddThread(std::shared_ptr<KThread> thread);
/// Removes a thread from the scheduler
void RemoveThread(std::shared_ptr<Thread> thread);
void RemoveThread(std::shared_ptr<KThread> thread);
/// Returns a list of all threads managed by the scheduler
[[nodiscard]] const std::vector<std::shared_ptr<Thread>>& GetThreadList() const {
[[nodiscard]] const std::vector<std::shared_ptr<KThread>>& GetThreadList() const {
return thread_list;
}
@@ -74,7 +79,7 @@ private:
LockType scheduler_lock;
/// Lists all thread ids that aren't deleted/etc.
std::vector<std::shared_ptr<Thread>> thread_list;
std::vector<std::shared_ptr<KThread>> thread_list;
Common::SpinLock global_list_guard{};
};

View File

@@ -9,9 +9,9 @@
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
namespace {
@@ -89,6 +89,10 @@ ResultCode HandleTable::Close(Handle handle) {
const u16 slot = GetSlot(handle);
if (objects[slot].use_count() == 1) {
objects[slot]->Finalize();
}
objects[slot] = nullptr;
generations[slot] = next_free_slot;

View File

@@ -19,12 +19,12 @@
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/kernel/writable_event.h"
#include "core/memory.h"
@@ -48,7 +48,7 @@ void SessionRequestHandler::ClientDisconnected(
HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
std::shared_ptr<ServerSession> server_session,
std::shared_ptr<Thread> thread)
std::shared_ptr<KThread> thread)
: server_session(std::move(server_session)),
thread(std::move(thread)), kernel{kernel}, memory{memory} {
cmd_buf[0] = 0;
@@ -182,7 +182,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
return RESULT_SUCCESS;
}
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& thread) {
auto& owner_process = *thread.GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable();
@@ -338,6 +338,28 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
return 0;
}
bool HLERequestContext::CanReadBuffer(std::size_t buffer_index) const {
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
if (is_buffer_a) {
return BufferDescriptorA().size() > buffer_index;
} else {
return BufferDescriptorX().size() > buffer_index;
}
}
bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const {
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size()};
if (is_buffer_b) {
return BufferDescriptorB().size() > buffer_index;
} else {
return BufferDescriptorC().size() > buffer_index;
}
}
std::string HLERequestContext::Description() const {
if (!command_header) {
return "No command header available";

View File

@@ -40,7 +40,7 @@ class HLERequestContext;
class KernelCore;
class Process;
class ServerSession;
class Thread;
class KThread;
class ReadableEvent;
class WritableEvent;
@@ -110,7 +110,7 @@ class HLERequestContext {
public:
explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
std::shared_ptr<ServerSession> session,
std::shared_ptr<Thread> thread);
std::shared_ptr<KThread> thread);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
@@ -126,15 +126,12 @@ public:
return server_session;
}
using WakeupCallback = std::function<void(
std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
/// Populates this context with data from the requesting process/thread.
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread.
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
ResultCode WriteToOutgoingCommandBuffer(KThread& thread);
u32_le GetCommand() const {
return command;
@@ -207,6 +204,12 @@ public:
/// Helper function to get the size of the output buffer
std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to test whether the input buffer at buffer_index can be read
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;
template <typename T>
std::shared_ptr<T> GetCopyObject(std::size_t index) {
return DynamicObjectCast<T>(copy_objects.at(index));
@@ -261,11 +264,11 @@ public:
std::string Description() const;
Thread& GetThread() {
KThread& GetThread() {
return *thread;
}
const Thread& GetThread() const {
const KThread& GetThread() const {
return *thread;
}
@@ -280,7 +283,7 @@ private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
std::shared_ptr<Kernel::ServerSession> server_session;
std::shared_ptr<Thread> thread;
std::shared_ptr<KThread> thread;
// TODO(yuriks): Check common usage of this and optimize size accordingly
boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects;
boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects;

View File

@@ -7,9 +7,9 @@
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
#include "core/memory.h"
@@ -96,7 +96,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
Thread* target_thread = std::addressof(*it);
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter());
@@ -125,7 +125,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
Thread* target_thread = std::addressof(*it);
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter());
@@ -215,7 +215,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
Thread* target_thread = std::addressof(*it);
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter());
@@ -231,11 +231,10 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
@@ -280,10 +279,7 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
}
// Cancel the timer wait.
if (timer != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{
@@ -302,11 +298,10 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
@@ -344,10 +339,7 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
}
// Cancel the timer wait.
if (timer != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{

View File

@@ -27,7 +27,7 @@ public:
}
[[nodiscard]] constexpr bool GetAffinity(s32 core) const {
return this->mask & GetCoreBit(core);
return (this->mask & GetCoreBit(core)) != 0;
}
constexpr void SetAffinity(s32 core, bool set) {

View File

@@ -10,11 +10,11 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/thread.h"
#include "core/memory.h"
namespace Kernel {
@@ -66,7 +66,7 @@ KConditionVariable::KConditionVariable(Core::System& system_)
KConditionVariable::~KConditionVariable() = default;
ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
Thread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
// Signal the address.
{
@@ -74,7 +74,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
// Remove waiter thread.
s32 num_waiters{};
Thread* next_owner_thread =
KThread* next_owner_thread =
owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Determine the next tag.
@@ -103,11 +103,11 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
}
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
// Wait for the address.
{
std::shared_ptr<Thread> owner_thread;
std::shared_ptr<KThread> owner_thread;
ASSERT(!owner_thread);
{
KScopedSchedulerLock sl(kernel);
@@ -126,7 +126,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS);
// Get the lock owner thread.
owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(handle);
owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(handle);
R_UNLESS(owner_thread, Svc::ResultInvalidHandle);
// Update the lock.
@@ -143,7 +143,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
// Remove the thread as a waiter from the lock owner.
{
KScopedSchedulerLock sl(kernel);
Thread* owner_thread = cur_thread->GetLockOwner();
KThread* owner_thread = cur_thread->GetLockOwner();
if (owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
}
@@ -154,7 +154,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
return cur_thread->GetWaitResult(std::addressof(dummy));
}
Thread* KConditionVariable::SignalImpl(Thread* thread) {
KThread* KConditionVariable::SignalImpl(KThread* thread) {
// Check pre-conditions.
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
@@ -174,7 +174,7 @@ Thread* KConditionVariable::SignalImpl(Thread* thread) {
}
}
Thread* thread_to_close = nullptr;
KThread* thread_to_close = nullptr;
if (can_access) {
if (prev_tag == InvalidHandle) {
// If nobody held the lock previously, we're all good.
@@ -182,7 +182,7 @@ Thread* KConditionVariable::SignalImpl(Thread* thread) {
thread->Wakeup();
} else {
// Get the previous owner.
auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(
auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(
prev_tag & ~Svc::HandleWaitMask);
if (owner_thread) {
@@ -210,8 +210,8 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
// TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using
// std::shared_ptr.
std::vector<std::shared_ptr<Thread>> thread_list;
std::array<Thread*, MaxThreads> thread_array;
std::vector<std::shared_ptr<KThread>> thread_list;
std::array<KThread*, MaxThreads> thread_array;
s32 num_to_close{};
// Perform signaling.
@@ -222,9 +222,9 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
auto it = thread_tree.nfind_light({cv_key, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetConditionVariableKey() == cv_key)) {
Thread* target_thread = std::addressof(*it);
KThread* target_thread = std::addressof(*it);
if (Thread* thread = SignalImpl(target_thread); thread != nullptr) {
if (KThread* thread = SignalImpl(target_thread); thread != nullptr) {
if (num_to_close < MaxThreads) {
thread_array[num_to_close++] = thread;
} else {
@@ -257,11 +257,10 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
@@ -276,7 +275,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
{
// Remove waiter thread.
s32 num_waiters{};
Thread* next_owner_thread =
KThread* next_owner_thread =
cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Update for the next owner thread.
@@ -322,16 +321,13 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
}
// Cancel the timer wait.
if (timer != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the condition variable.
{
KScopedSchedulerLock sl(kernel);
if (Thread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(cur_thread);
}

View File

@@ -8,8 +8,8 @@
#include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
namespace Core {
@@ -20,7 +20,7 @@ namespace Kernel {
class KConditionVariable {
public:
using ThreadTree = typename Thread::ConditionVariableThreadTreeType;
using ThreadTree = typename KThread::ConditionVariableThreadTreeType;
explicit KConditionVariable(Core::System& system_);
~KConditionVariable();
@@ -34,7 +34,7 @@ public:
[[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private:
[[nodiscard]] Thread* SignalImpl(Thread* thread);
[[nodiscard]] KThread* SignalImpl(KThread* thread);
ThreadTree thread_tree;
@@ -43,14 +43,14 @@ private:
};
inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
Thread* thread) {
KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->erase(tree->iterator_to(*thread));
}
inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
Thread* thread) {
KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->insert(*thread);

View File

@@ -0,0 +1,57 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
class KernelCore;
class KLightConditionVariable {
public:
explicit KLightConditionVariable(KernelCore& kernel) : thread_queue(kernel), kernel(kernel) {}
void Wait(KLightLock* lock, s64 timeout = -1) {
WaitImpl(lock, timeout);
lock->Lock();
}
void Broadcast() {
KScopedSchedulerLock lk{kernel};
while (thread_queue.WakeupFrontThread() != nullptr) {
// We want to signal all threads, and so should continue waking up until there's nothing
// to wake.
}
}
private:
void WaitImpl(KLightLock* lock, s64 timeout) {
KThread* owner = GetCurrentThreadPointer(kernel);
// Sleep the thread.
{
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
lock->Unlock();
if (!thread_queue.SleepThread(owner)) {
lk.CancelSleep();
return;
}
}
// Cancel the task that the sleep setup.
kernel.TimeManager().UnscheduleTimeEvent(owner);
}
KThreadQueue thread_queue;
KernelCore& kernel;
};
} // namespace Kernel

View File

@@ -0,0 +1,130 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
void KLightLock::Lock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
const uintptr_t cur_thread_tag = (cur_thread | 1);
while (true) {
uintptr_t old_tag = tag.load(std::memory_order_relaxed);
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1,
std::memory_order_acquire)) {
if ((old_tag | 1) == cur_thread_tag) {
return;
}
}
if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
break;
}
LockSlowPath(old_tag | 1, cur_thread);
}
}
void KLightLock::Unlock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
uintptr_t expected = cur_thread;
do {
if (expected != cur_thread) {
return UnlockSlowPath(cur_thread);
}
} while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
}
void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
// Pend the current thread waiting on the owner thread.
{
KScopedSchedulerLock sl{kernel};
// Ensure we actually have locking to do.
if (tag.load(std::memory_order_relaxed) != _owner) {
return;
}
// Add the current thread as a waiter on the owner.
KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
owner_thread->AddWaiter(cur_thread);
// Set thread states.
if (cur_thread->GetState() == ThreadState::Runnable) {
cur_thread->SetState(ThreadState::Waiting);
} else {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
if (owner_thread->IsSuspended()) {
owner_thread->ContinueIfHasKernelWaiters();
}
}
// We're no longer waiting on the lock owner.
{
KScopedSchedulerLock sl{kernel};
KThread* owner_thread = cur_thread->GetLockOwner();
if (owner_thread) {
owner_thread->RemoveWaiter(cur_thread);
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
}
}
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
KThread* owner_thread = reinterpret_cast<KThread*>(_cur_thread);
// Unlock.
{
KScopedSchedulerLock sl{kernel};
// Get the next owner.
s32 num_waiters = 0;
KThread* next_owner = owner_thread->RemoveWaiterByKey(
std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
// Pass the lock to the next owner.
uintptr_t next_tag = 0;
if (next_owner) {
next_tag = reinterpret_cast<uintptr_t>(next_owner);
if (num_waiters > 1) {
next_tag |= 0x1;
}
if (next_owner->GetState() == ThreadState::Waiting) {
next_owner->SetState(ThreadState::Runnable);
} else {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
if (next_owner->IsSuspended()) {
next_owner->ContinueIfHasKernelWaiters();
}
}
// We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if
// so.
if (owner_thread->IsSuspended()) {
owner_thread->TrySuspend();
}
// Write the new tag value.
tag.store(next_tag);
}
}
bool KLightLock::IsLockedByCurrentThread() const {
return (tag | 1ULL) == (reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)) | 1ULL);
}
} // namespace Kernel

View File

@@ -0,0 +1,41 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <atomic>
#include "common/common_types.h"
#include "core/hle/kernel/k_scoped_lock.h"
namespace Kernel {
class KernelCore;
class KLightLock {
public:
explicit KLightLock(KernelCore& kernel_) : kernel{kernel_} {}
void Lock();
void Unlock();
void LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
void UnlockSlowPath(uintptr_t cur_thread);
bool IsLocked() const {
return tag != 0;
}
bool IsLockedByCurrentThread() const;
private:
std::atomic<uintptr_t> tag{};
KernelCore& kernel;
};
using KScopedLightLock = KScopedLock<KLightLock>;
} // namespace Kernel

View File

@@ -18,7 +18,7 @@
namespace Kernel {
class Thread;
class KThread;
template <typename T>
concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) {
@@ -367,7 +367,7 @@ public:
this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member);
}
constexpr Thread* MoveToScheduledBack(Member* member) {
constexpr KThread* MoveToScheduledBack(Member* member) {
return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),
member);
}

View File

@@ -0,0 +1,152 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#include "common/assert.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
constexpr s64 DefaultTimeout = 10000000000; // 10 seconds
KResourceLimit::KResourceLimit(KernelCore& kernel, Core::System& system)
: Object{kernel}, lock{kernel}, cond_var{kernel}, kernel{kernel}, system(system) {}
KResourceLimit::~KResourceLimit() = default;
s64 KResourceLimit::GetLimitValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = limit_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetCurrentValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = current_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetPeakValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = peak_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetFreeValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk(lock);
ASSERT(current_values[index] >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
value = limit_values[index] - current_values[index];
}
return value;
}
ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
R_UNLESS(current_values[index] <= value, Svc::ResultInvalidState);
limit_values[index] = value;
return RESULT_SUCCESS;
}
bool KResourceLimit::Reserve(LimitableResource which, s64 value) {
return Reserve(which, value, system.CoreTiming().GetGlobalTimeNs().count() + DefaultTimeout);
}
bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
ASSERT(value >= 0);
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
ASSERT(current_hints[index] <= current_values[index]);
if (current_hints[index] >= limit_values[index]) {
return false;
}
// Loop until we reserve or run out of time.
while (true) {
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
// If we would overflow, don't allow to succeed.
if (current_values[index] + value <= current_values[index]) {
break;
}
if (current_values[index] + value <= limit_values[index]) {
current_values[index] += value;
current_hints[index] += value;
peak_values[index] = std::max(peak_values[index], current_values[index]);
return true;
}
if (current_hints[index] + value <= limit_values[index] &&
(timeout < 0 || system.CoreTiming().GetGlobalTimeNs().count() < timeout)) {
waiter_count++;
cond_var.Wait(&lock, timeout);
waiter_count--;
} else {
break;
}
}
return false;
}
void KResourceLimit::Release(LimitableResource which, s64 value) {
Release(which, value, value);
}
void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
ASSERT(value >= 0);
ASSERT(hint >= 0);
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
ASSERT(value <= current_values[index]);
ASSERT(hint <= current_hints[index]);
current_values[index] -= value;
current_hints[index] -= hint;
if (waiter_count != 0) {
cond_var.Broadcast();
}
}
} // namespace Kernel

View File

@@ -0,0 +1,81 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/k_light_condition_variable.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/object.h"
union ResultCode;
namespace Core {
class System;
}
namespace Kernel {
class KernelCore;
enum class LimitableResource : u32 {
PhysicalMemory = 0,
Threads = 1,
Events = 2,
TransferMemory = 3,
Sessions = 4,
Count,
};
constexpr bool IsValidResourceType(LimitableResource type) {
return type < LimitableResource::Count;
}
class KResourceLimit final : public Object {
public:
explicit KResourceLimit(KernelCore& kernel, Core::System& system);
~KResourceLimit();
s64 GetLimitValue(LimitableResource which) const;
s64 GetCurrentValue(LimitableResource which) const;
s64 GetPeakValue(LimitableResource which) const;
s64 GetFreeValue(LimitableResource which) const;
ResultCode SetLimitValue(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value, s64 timeout);
void Release(LimitableResource which, s64 value);
void Release(LimitableResource which, s64 value, s64 hint);
std::string GetTypeName() const override {
return "KResourceLimit";
}
std::string GetName() const override {
return GetTypeName();
}
static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
virtual void Finalize() override {}
private:
using ResourceArray = std::array<s64, static_cast<std::size_t>(LimitableResource::Count)>;
ResourceArray limit_values{};
ResourceArray current_values{};
ResourceArray current_hints{};
ResourceArray peak_values{};
mutable KLightLock lock;
s32 waiter_count{};
KLightConditionVariable cond_var;
KernelCore& kernel;
Core::System& system;
};
} // namespace Kernel

View File

@@ -17,25 +17,30 @@
#include "core/cpu_manager.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
static void IncrementScheduledCount(Kernel::Thread* thread) {
static void IncrementScheduledCount(Kernel::KThread* thread) {
if (auto process = thread->GetOwnerProcess(); process) {
process->IncrementScheduledCount();
}
}
void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule,
Core::EmuThreadHandle global_thread) {
const u32 current_core = global_thread.host_handle;
bool must_context_switch = global_thread.guest_handle != InvalidHandle &&
(current_core < Core::Hardware::NUM_CPU_CORES);
void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) {
auto scheduler = kernel.CurrentScheduler();
u32 current_core{0xF};
bool must_context_switch{};
if (scheduler) {
current_core = scheduler->core_id;
// TODO(bunnei): Should be set to true when we deprecate single core
must_context_switch = !kernel.IsPhantomModeForSingleCore();
}
while (cores_pending_reschedule != 0) {
const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
@@ -56,28 +61,27 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
}
}
u64 KScheduler::UpdateHighestPriorityThread(Thread* highest_thread) {
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
std::scoped_lock lock{guard};
if (Thread* prev_highest_thread = this->state.highest_priority_thread;
if (KThread* prev_highest_thread = state.highest_priority_thread;
prev_highest_thread != highest_thread) {
if (prev_highest_thread != nullptr) {
IncrementScheduledCount(prev_highest_thread);
prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks());
}
if (this->state.should_count_idle) {
if (state.should_count_idle) {
if (highest_thread != nullptr) {
// if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) {
// process->SetRunningThread(this->core_id, highest_thread,
// this->state.idle_count);
//}
if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) {
process->SetRunningThread(core_id, highest_thread, state.idle_count);
}
} else {
this->state.idle_count++;
state.idle_count++;
}
}
this->state.highest_priority_thread = highest_thread;
this->state.needs_scheduling = true;
return (1ULL << this->core_id);
state.highest_priority_thread = highest_thread;
state.needs_scheduling.store(true);
return (1ULL << core_id);
} else {
return 0;
}
@@ -90,16 +94,29 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
ClearSchedulerUpdateNeeded(kernel);
u64 cores_needing_scheduling = 0, idle_cores = 0;
Thread* top_threads[Core::Hardware::NUM_CPU_CORES];
KThread* top_threads[Core::Hardware::NUM_CPU_CORES];
auto& priority_queue = GetPriorityQueue(kernel);
/// We want to go over all cores, finding the highest priority thread and determining if
/// scheduling is needed for that core.
for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
Thread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
if (top_thread != nullptr) {
// If the thread has no waiters, we need to check if the process has a thread pinned.
// TODO(bunnei): Implement thread pinning
if (top_thread->GetNumKernelWaiters() == 0) {
if (Process* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
pinned != nullptr && pinned != top_thread) {
// We prefer our parent's pinned thread if possible. However, we also don't
// want to schedule un-runnable threads.
if (pinned->GetRawState() == ThreadState::Runnable) {
top_thread = pinned;
} else {
top_thread = nullptr;
}
}
}
}
} else {
idle_cores |= (1ULL << core_id);
}
@@ -112,7 +129,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
while (idle_cores != 0) {
const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
if (Thread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
size_t num_candidates = 0;
@@ -120,7 +137,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
if (Thread* top_thread =
if (KThread* top_thread =
(suggested_core >= 0) ? top_threads[suggested_core] : nullptr;
top_thread != suggested) {
// Make sure we're not dealing with threads too high priority for migration.
@@ -152,7 +169,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Check if there's some other thread that can run on the candidate core.
const s32 candidate_core = migration_candidates[i];
suggested = top_threads[candidate_core];
if (Thread* next_on_candidate_core =
if (KThread* next_on_candidate_core =
priority_queue.GetScheduledNext(candidate_core, suggested);
next_on_candidate_core != nullptr) {
// The candidate core can run some other thread! We'll migrate its current
@@ -182,7 +199,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
return cores_needing_scheduling;
}
void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state) {
void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) {
// Get an atomic reference to the core scheduler's previous thread.
std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
static_assert(std::atomic_ref<KThread*>::is_always_lock_free);
// Atomically clear the previous thread if it's our target.
KThread* compare = thread;
prev_thread.compare_exchange_strong(compare, nullptr);
}
}
void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Check if the state has changed, because if it hasn't there's nothing to do.
@@ -205,7 +235,7 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, Thread
}
}
void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority) {
void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// If the thread is runnable, we want to change its priority in the queue.
@@ -217,7 +247,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32
}
}
void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread,
void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
@@ -237,8 +267,8 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
auto& priority_queue = GetPriorityQueue(kernel);
// Rotate the front of the queue to the end.
Thread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
Thread* next_thread = nullptr;
KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
KThread* next_thread = nullptr;
if (top_thread != nullptr) {
next_thread = priority_queue.MoveToScheduledBack(top_thread);
if (next_thread != top_thread) {
@@ -249,11 +279,11 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
// While we have a suggested thread, try to migrate it!
{
Thread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
if (Thread* top_on_suggested_core =
if (KThread* top_on_suggested_core =
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr;
top_on_suggested_core != suggested) {
@@ -285,7 +315,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
// Now that we might have migrated a thread with the same priority, check if we can do better.
{
Thread* best_thread = priority_queue.GetScheduledFront(core_id);
KThread* best_thread = priority_queue.GetScheduledFront(core_id);
if (best_thread == GetCurrentThread()) {
best_thread = priority_queue.GetScheduledNext(core_id, best_thread);
}
@@ -293,7 +323,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
// If the best thread we can choose has a priority the same or worse than ours, try to
// migrate a higher priority thread.
if (best_thread != nullptr && best_thread->GetPriority() >= priority) {
Thread* suggested = priority_queue.GetSuggestedFront(core_id);
KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// If the suggestion's priority is the same as ours, don't bother.
if (suggested->GetPriority() >= best_thread->GetPriority()) {
@@ -302,7 +332,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
if (Thread* top_on_suggested_core =
if (KThread* top_on_suggested_core =
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr;
top_on_suggested_core != suggested) {
@@ -352,12 +382,14 @@ void KScheduler::DisableScheduling(KernelCore& kernel) {
}
}
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling,
Core::EmuThreadHandle global_thread) {
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
scheduler->GetCurrentThread()->EnableDispatch();
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
scheduler->GetCurrentThread()->EnableDispatch();
}
}
RescheduleCores(kernel, cores_needing_scheduling, global_thread);
RescheduleCores(kernel, cores_needing_scheduling);
}
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -372,15 +404,13 @@ KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().priority_queue;
}
void KScheduler::YieldWithoutCoreMigration() {
auto& kernel = system.Kernel();
void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
Thread& cur_thread = *GetCurrentThread();
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -398,7 +428,7 @@ void KScheduler::YieldWithoutCoreMigration() {
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
// Put the current thread at the back of the queue.
Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
IncrementScheduledCount(std::addressof(cur_thread));
// If the next thread is different, we have an update to perform.
@@ -413,15 +443,13 @@ void KScheduler::YieldWithoutCoreMigration() {
}
}
void KScheduler::YieldWithCoreMigration() {
auto& kernel = system.Kernel();
void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
Thread& cur_thread = *GetCurrentThread();
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -442,17 +470,17 @@ void KScheduler::YieldWithCoreMigration() {
const s32 core_id = cur_thread.GetActiveCore();
// Put the current thread at the back of the queue.
Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
IncrementScheduledCount(std::addressof(cur_thread));
// While we have a suggested thread, try to migrate it!
bool recheck = false;
Thread* suggested = priority_queue.GetSuggestedFront(core_id);
KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// Check if the suggested thread is the thread running on its core.
const s32 suggested_core = suggested->GetActiveCore();
if (Thread* running_on_suggested_core =
if (KThread* running_on_suggested_core =
(suggested_core >= 0)
? kernel.Scheduler(suggested_core).state.highest_priority_thread
: nullptr;
@@ -503,15 +531,13 @@ void KScheduler::YieldWithCoreMigration() {
}
}
void KScheduler::YieldToAnyThread() {
auto& kernel = system.Kernel();
void KScheduler::YieldToAnyThread(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
Thread& cur_thread = *GetCurrentThread();
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -539,11 +565,11 @@ void KScheduler::YieldToAnyThread() {
// If there's nothing scheduled, we can try to perform a migration.
if (priority_queue.GetScheduledFront(core_id) == nullptr) {
// While we have a suggested thread, try to migrate it!
Thread* suggested = priority_queue.GetSuggestedFront(core_id);
KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
if (Thread* top_on_suggested_core =
if (KThread* top_on_suggested_core =
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr;
top_on_suggested_core != suggested) {
@@ -581,22 +607,21 @@ void KScheduler::YieldToAnyThread() {
}
}
KScheduler::KScheduler(Core::System& system, std::size_t core_id)
: system(system), core_id(core_id) {
KScheduler::KScheduler(Core::System& system, s32 core_id) : system(system), core_id(core_id) {
switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this);
this->state.needs_scheduling = true;
this->state.interrupt_task_thread_runnable = false;
this->state.should_count_idle = false;
this->state.idle_count = 0;
this->state.idle_thread_stack = nullptr;
this->state.highest_priority_thread = nullptr;
state.needs_scheduling.store(true);
state.interrupt_task_thread_runnable = false;
state.should_count_idle = false;
state.idle_count = 0;
state.idle_thread_stack = nullptr;
state.highest_priority_thread = nullptr;
}
KScheduler::~KScheduler() = default;
Thread* KScheduler::GetCurrentThread() const {
if (current_thread) {
return current_thread;
KThread* KScheduler::GetCurrentThread() const {
if (auto result = current_thread.load(); result) {
return result;
}
return idle_thread;
}
@@ -613,7 +638,7 @@ void KScheduler::RescheduleCurrentCore() {
phys_core.ClearInterrupt();
}
guard.lock();
if (this->state.needs_scheduling) {
if (state.needs_scheduling.load()) {
Schedule();
} else {
guard.unlock();
@@ -624,66 +649,76 @@ void KScheduler::OnThreadStart() {
SwitchContextStep2();
}
void KScheduler::Unload(Thread* thread) {
void KScheduler::Unload(KThread* thread) {
LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
if (thread) {
thread->SetIsRunning(false);
if (thread->IsContinuousOnSVC() && !thread->IsHLEThread()) {
if (thread->IsCallingSvc()) {
system.ArmInterface(core_id).ExceptionalExit();
thread->SetContinuousOnSVC(false);
thread->ClearIsCallingSvc();
}
if (!thread->IsHLEThread() && !thread->HasExited()) {
if (!thread->IsTerminationRequested()) {
prev_thread = thread;
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.SaveContext(thread->GetContext32());
cpu_core.SaveContext(thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified.
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
} else {
prev_thread = nullptr;
}
thread->context_guard.unlock();
}
}
void KScheduler::Reload(Thread* thread) {
void KScheduler::Reload(KThread* thread) {
LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread ? thread->GetName() : "nullptr");
if (thread) {
ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
// Cancel any outstanding wakeup events for this thread
thread->SetIsRunning(true);
thread->SetWasRunning(false);
auto* const thread_owner_process = thread->GetOwnerProcess();
if (thread_owner_process != nullptr) {
system.Kernel().MakeCurrentProcess(thread_owner_process);
}
if (!thread->IsHLEThread()) {
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64());
cpu_core.SetTlsAddress(thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
}
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64());
cpu_core.SetTlsAddress(thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
}
}
void KScheduler::SwitchContextStep2() {
// Load context of new thread
Reload(current_thread);
Reload(current_thread.load());
RescheduleCurrentCore();
}
void KScheduler::ScheduleImpl() {
Thread* previous_thread = current_thread;
current_thread = state.highest_priority_thread;
KThread* previous_thread = current_thread.load();
KThread* next_thread = state.highest_priority_thread;
this->state.needs_scheduling = false;
state.needs_scheduling = false;
if (current_thread == previous_thread) {
// We never want to schedule a null thread, so use the idle thread if we don't have a next.
if (next_thread == nullptr) {
next_thread = idle_thread;
}
// If we're not actually switching thread, there's nothing to do.
if (next_thread == current_thread.load()) {
guard.unlock();
return;
}
current_thread.store(next_thread);
Process* const previous_process = system.Kernel().CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process);
@@ -714,28 +749,29 @@ void KScheduler::SwitchToCurrent() {
while (true) {
{
std::scoped_lock lock{guard};
current_thread = state.highest_priority_thread;
this->state.needs_scheduling = false;
current_thread.store(state.highest_priority_thread);
state.needs_scheduling.store(false);
}
const auto is_switch_pending = [this] {
std::scoped_lock lock{guard};
return state.needs_scheduling.load(std::memory_order_relaxed);
return state.needs_scheduling.load();
};
do {
if (current_thread != nullptr && !current_thread->IsHLEThread()) {
current_thread->context_guard.lock();
if (current_thread->GetRawState() != ThreadState::Runnable) {
current_thread->context_guard.unlock();
auto next_thread = current_thread.load();
if (next_thread != nullptr) {
next_thread->context_guard.lock();
if (next_thread->GetRawState() != ThreadState::Runnable) {
next_thread->context_guard.unlock();
break;
}
if (static_cast<u32>(current_thread->GetProcessorID()) != core_id) {
current_thread->context_guard.unlock();
if (next_thread->GetActiveCore() != core_id) {
next_thread->context_guard.unlock();
break;
}
}
std::shared_ptr<Common::Fiber>* next_context;
if (current_thread != nullptr) {
next_context = &current_thread->GetHostContext();
if (next_thread != nullptr) {
next_context = &next_thread->GetHostContext();
} else {
next_context = &idle_thread->GetHostContext();
}
@@ -744,13 +780,13 @@ void KScheduler::SwitchToCurrent() {
}
}
void KScheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
void KScheduler::UpdateLastContextSwitchTime(KThread* thread, Process* process) {
const u64 prev_switch_ticks = last_context_switch_time;
const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
if (thread != nullptr) {
thread->UpdateCPUTimeTicks(update_ticks);
thread->AddCpuTime(core_id, update_ticks);
}
if (process != nullptr) {
@@ -764,15 +800,10 @@ void KScheduler::Initialize() {
std::string name = "Idle Thread Id:" + std::to_string(core_id);
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE);
auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0,
nullptr, std::move(init_func), init_func_parameter);
auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
nullptr, std::move(init_func), init_func_parameter);
idle_thread = thread_res.Unwrap().get();
{
KScopedSchedulerLock lock{system.Kernel()};
idle_thread->SetState(ThreadState::Runnable);
}
}
KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)

View File

@@ -29,29 +29,33 @@ namespace Kernel {
class KernelCore;
class Process;
class SchedulerLock;
class Thread;
class KThread;
class KScheduler final {
public:
explicit KScheduler(Core::System& system, std::size_t core_id);
explicit KScheduler(Core::System& system, s32 core_id);
~KScheduler();
/// Reschedules to the next available thread (call after current thread is suspended)
void RescheduleCurrentCore();
/// Reschedules cores pending reschedule, to be called on EnableScheduling.
static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule,
Core::EmuThreadHandle global_thread);
static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule);
/// The next two are for SingleCore Only.
/// Unload current thread before preempting core.
void Unload(Thread* thread);
void Unload(KThread* thread);
/// Reload current thread after core preemption.
void Reload(Thread* thread);
void Reload(KThread* thread);
/// Gets the current running thread
[[nodiscard]] Thread* GetCurrentThread() const;
[[nodiscard]] KThread* GetCurrentThread() const;
/// Returns true if the scheduler is idle
[[nodiscard]] bool IsIdle() const {
return GetCurrentThread() == idle_thread;
}
/// Gets the timestamp for the last context switch in ticks.
[[nodiscard]] u64 GetLastContextSwitchTicks() const;
@@ -72,14 +76,14 @@ public:
return switch_fiber;
}
[[nodiscard]] u64 UpdateHighestPriorityThread(Thread* highest_thread);
[[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread);
/**
* Takes a thread and moves it to the back of the it's priority list.
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
void YieldWithoutCoreMigration();
static void YieldWithoutCoreMigration(KernelCore& kernel);
/**
* Takes a thread and moves it to the back of the it's priority list.
@@ -88,7 +92,7 @@ public:
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
void YieldWithCoreMigration();
static void YieldWithCoreMigration(KernelCore& kernel);
/**
* Takes a thread and moves it out of the scheduling queue.
@@ -97,16 +101,18 @@ public:
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
void YieldToAnyThread();
static void YieldToAnyThread(KernelCore& kernel);
static void ClearPreviousThread(KernelCore& kernel, KThread* thread);
/// Notify the scheduler a thread's status has changed.
static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state);
static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
/// Notify the scheduler a thread's priority has changed.
static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority);
static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority);
/// Notify the scheduler a thread's core and/or affinity mask has changed.
static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread,
static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core);
static bool CanSchedule(KernelCore& kernel);
@@ -114,8 +120,7 @@ public:
static void SetSchedulerUpdateNeeded(KernelCore& kernel);
static void ClearSchedulerUpdateNeeded(KernelCore& kernel);
static void DisableScheduling(KernelCore& kernel);
static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling,
Core::EmuThreadHandle global_thread);
static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
[[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
private:
@@ -163,13 +168,15 @@ private:
* most recent tick count retrieved. No special arithmetic is
* applied to it.
*/
void UpdateLastContextSwitchTime(Thread* thread, Process* process);
void UpdateLastContextSwitchTime(KThread* thread, Process* process);
static void OnSwitch(void* this_scheduler);
void SwitchToCurrent();
Thread* current_thread{};
Thread* idle_thread{};
KThread* prev_thread{};
std::atomic<KThread*> current_thread{};
KThread* idle_thread;
std::shared_ptr<Common::Fiber> switch_fiber{};
@@ -178,7 +185,7 @@ private:
bool interrupt_task_thread_runnable{};
bool should_count_idle{};
u64 idle_count{};
Thread* highest_priority_thread{};
KThread* highest_priority_thread{};
void* idle_thread_stack{};
};
@@ -186,7 +193,7 @@ private:
Core::System& system;
u64 last_context_switch_time{};
const std::size_t core_id;
const s32 core_id;
Common::SpinLock guard{};
};

View File

@@ -10,6 +10,7 @@
#include "common/assert.h"
#include "common/spin_lock.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
@@ -22,46 +23,45 @@ public:
explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {}
bool IsLockedByCurrentThread() const {
return this->owner_thread == kernel.GetCurrentEmuThreadID();
return owner_thread == GetCurrentThreadPointer(kernel);
}
void Lock() {
if (this->IsLockedByCurrentThread()) {
if (IsLockedByCurrentThread()) {
// If we already own the lock, we can just increment the count.
ASSERT(this->lock_count > 0);
this->lock_count++;
ASSERT(lock_count > 0);
lock_count++;
} else {
// Otherwise, we want to disable scheduling and acquire the spinlock.
SchedulerType::DisableScheduling(kernel);
this->spin_lock.lock();
spin_lock.lock();
// For debug, ensure that our state is valid.
ASSERT(this->lock_count == 0);
ASSERT(this->owner_thread == Core::EmuThreadHandle::InvalidHandle());
ASSERT(lock_count == 0);
ASSERT(owner_thread == nullptr);
// Increment count, take ownership.
this->lock_count = 1;
this->owner_thread = kernel.GetCurrentEmuThreadID();
lock_count = 1;
owner_thread = GetCurrentThreadPointer(kernel);
}
}
void Unlock() {
ASSERT(this->IsLockedByCurrentThread());
ASSERT(this->lock_count > 0);
ASSERT(IsLockedByCurrentThread());
ASSERT(lock_count > 0);
// Release an instance of the lock.
if ((--this->lock_count) == 0) {
if ((--lock_count) == 0) {
// We're no longer going to hold the lock. Take note of what cores need scheduling.
const u64 cores_needing_scheduling =
SchedulerType::UpdateHighestPriorityThreads(kernel);
Core::EmuThreadHandle leaving_thread = owner_thread;
// Note that we no longer hold the lock, and unlock the spinlock.
this->owner_thread = Core::EmuThreadHandle::InvalidHandle();
this->spin_lock.unlock();
owner_thread = nullptr;
spin_lock.unlock();
// Enable scheduling, and perform a rescheduling operation.
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling, leaving_thread);
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling);
}
}
@@ -69,7 +69,7 @@ private:
KernelCore& kernel;
Common::SpinLock spin_lock{};
s32 lock_count{};
Core::EmuThreadHandle owner_thread{Core::EmuThreadHandle::InvalidHandle()};
KThread* owner_thread{};
};
} // namespace Kernel

View File

@@ -9,27 +9,24 @@
#include "common/common_types.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
class KScopedSchedulerLockAndSleep {
public:
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, Thread* t,
s64 timeout)
: kernel(kernel), event_handle(event_handle), thread(t), timeout_tick(timeout) {
event_handle = InvalidHandle;
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, KThread* t, s64 timeout)
: kernel(kernel), thread(t), timeout_tick(timeout) {
// Lock the scheduler.
kernel.GlobalSchedulerContext().scheduler_lock.Lock();
}
~KScopedSchedulerLockAndSleep() {
// Register the sleep.
if (this->timeout_tick > 0) {
kernel.TimeManager().ScheduleTimeEvent(event_handle, this->thread, this->timeout_tick);
if (timeout_tick > 0) {
kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick);
}
// Unlock the scheduler.
@@ -37,13 +34,12 @@ public:
}
void CancelSleep() {
this->timeout_tick = 0;
timeout_tick = 0;
}
private:
KernelCore& kernel;
Handle& event_handle;
Thread* thread{};
KThread* thread{};
s64 timeout_tick{};
};

View File

@@ -7,9 +7,9 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
@@ -20,12 +20,11 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
std::vector<ThreadListNode> thread_nodes(num_objects);
// Prepare for wait.
Thread* thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
KThread* thread = kernel.CurrentScheduler()->GetCurrentThread();
{
// Setup the scheduling lock and sleep.
KScopedSchedulerLockAndSleep slp(kernel, timer, thread, timeout);
KScopedSchedulerLockAndSleep slp{kernel, thread, timeout};
// Check if any of the objects are already signaled.
for (auto i = 0; i < num_objects; ++i) {
@@ -90,10 +89,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
thread->SetWaitObjectsForDebugging({});
// Cancel the timer as needed.
if (timer != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
kernel.TimeManager().UnscheduleTimeEvent(thread);
// Get the wait result.
ResultCode wait_result{RESULT_SUCCESS};
@@ -136,7 +132,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {}
KSynchronizationObject ::~KSynchronizationObject() = default;
KSynchronizationObject::~KSynchronizationObject() = default;
void KSynchronizationObject::NotifyAvailable(ResultCode result) {
KScopedSchedulerLock lock(kernel);
@@ -148,7 +144,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
// Iterate over each thread.
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
Thread* thread = cur_node->thread;
KThread* thread = cur_node->thread;
if (thread->GetState() == ThreadState::Waiting) {
thread->SetSyncedObject(this, result);
thread->SetState(ThreadState::Runnable);
@@ -156,8 +152,8 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
}
}
std::vector<Thread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
std::vector<Thread*> threads;
std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
std::vector<KThread*> threads;
// If debugging, dump the list of waiters.
{

View File

@@ -13,14 +13,14 @@ namespace Kernel {
class KernelCore;
class Synchronization;
class Thread;
class KThread;
/// Class that represents a Kernel object that a thread can be waiting on
class KSynchronizationObject : public Object {
public:
struct ThreadListNode {
ThreadListNode* next{};
Thread* thread{};
KThread* thread{};
};
[[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index,
@@ -29,7 +29,7 @@ public:
[[nodiscard]] virtual bool IsSignaled() const = 0;
[[nodiscard]] std::vector<Thread*> GetWaitingThreadsForDebugging() const;
[[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
protected:
explicit KSynchronizationObject(KernelCore& kernel);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,768 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <span>
#include <string>
#include <utility>
#include <vector>
#include <boost/intrusive/list.hpp>
#include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
#include "common/spin_lock.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/k_affinity_mask.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
namespace Common {
class Fiber;
}
namespace Core {
class ARM_Interface;
class System;
} // namespace Core
namespace Kernel {
class GlobalSchedulerContext;
class KernelCore;
class Process;
class KScheduler;
class KThreadQueue;
using KThreadFunction = VAddr;
enum class ThreadType : u32 {
Main = 0,
Kernel = 1,
HighPriority = 2,
User = 3,
};
DECLARE_ENUM_FLAG_OPERATORS(ThreadType);
enum class SuspendType : u32 {
Process = 0,
Thread = 1,
Debug = 2,
Backtrace = 3,
Init = 4,
Count,
};
enum class ThreadState : u16 {
Initialized = 0,
Waiting = 1,
Runnable = 2,
Terminated = 3,
SuspendShift = 4,
Mask = (1 << SuspendShift) - 1,
ProcessSuspended = (1 << (0 + SuspendShift)),
ThreadSuspended = (1 << (1 + SuspendShift)),
DebugSuspended = (1 << (2 + SuspendShift)),
BacktraceSuspended = (1 << (3 + SuspendShift)),
InitSuspended = (1 << (4 + SuspendShift)),
SuspendFlagMask = ((1 << 5) - 1) << SuspendShift,
};
DECLARE_ENUM_FLAG_OPERATORS(ThreadState);
enum class DpcFlag : u32 {
Terminating = (1 << 0),
Terminated = (1 << 1),
};
enum class ThreadWaitReasonForDebugging : u32 {
None, ///< Thread is not waiting
Sleep, ///< Thread is waiting due to a SleepThread SVC
IPC, ///< Thread is waiting for the reply from an IPC request
Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC
ConditionVar, ///< Thread is waiting due to a WaitProcessWideKey SVC
Arbitration, ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC
Suspended, ///< Thread is waiting due to process suspension
};
[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
class KThread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> {
friend class KScheduler;
friend class Process;
public:
static constexpr s32 DefaultThreadPriority = 44;
static constexpr s32 IdleThreadPriority = Svc::LowestThreadPriority + 1;
explicit KThread(KernelCore& kernel);
~KThread() override;
public:
using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
using WaiterList = boost::intrusive::list<KThread>;
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
* @param arg User data to pass to the thread
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
* @param stack_top The address of the thread's stack top
* @param owner_process The parent process for the thread, if null, it's a kernel thread
* @return A shared pointer to the newly created thread
*/
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
* @param arg User data to pass to the thread
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
* @param stack_top The address of the thread's stack top
* @param owner_process The parent process for the thread, if null, it's a kernel thread
* @param thread_start_func The function where the host context will start.
* @param thread_start_parameter The parameter which will passed to host context on init
* @return A shared pointer to the newly created thread
*/
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
std::function<void(void*)>&& thread_start_func, void* thread_start_parameter);
[[nodiscard]] std::string GetName() const override {
return name;
}
void SetName(std::string new_name) {
name = std::move(new_name);
}
[[nodiscard]] std::string GetTypeName() const override {
return "Thread";
}
static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
[[nodiscard]] HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
/**
* Gets the thread's current priority
* @return The current thread's priority
*/
[[nodiscard]] s32 GetPriority() const {
return priority;
}
/**
* Sets the thread's current priority.
* @param priority The new priority.
*/
void SetPriority(s32 value) {
priority = value;
}
/**
* Gets the thread's nominal priority.
* @return The current thread's nominal priority.
*/
[[nodiscard]] s32 GetBasePriority() const {
return base_priority;
}
/**
* Gets the thread's thread ID
* @return The thread's ID
*/
[[nodiscard]] u64 GetThreadID() const {
return thread_id;
}
void ContinueIfHasKernelWaiters() {
if (GetNumKernelWaiters() > 0) {
Continue();
}
}
void Wakeup();
void SetBasePriority(s32 value);
[[nodiscard]] ResultCode Run();
void Exit();
[[nodiscard]] u32 GetSuspendFlags() const {
return suspend_allowed_flags & suspend_request_flags;
}
[[nodiscard]] bool IsSuspended() const {
return GetSuspendFlags() != 0;
}
[[nodiscard]] bool IsSuspendRequested(SuspendType type) const {
return (suspend_request_flags &
(1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type)))) !=
0;
}
[[nodiscard]] bool IsSuspendRequested() const {
return suspend_request_flags != 0;
}
void RequestSuspend(SuspendType type);
void Resume(SuspendType type);
void TrySuspend();
void Continue();
void Suspend();
void Finalize() override;
bool IsSignaled() const override;
void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) {
synced_object = obj;
wait_result = wait_res;
}
[[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const {
*out = synced_object;
return wait_result;
}
/*
* Returns the Thread Local Storage address of the current thread
* @returns VAddr of the thread's TLS
*/
[[nodiscard]] VAddr GetTLSAddress() const {
return tls_address;
}
/*
* Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
* @returns The value of the TPIDR_EL0 register.
*/
[[nodiscard]] u64 GetTPIDR_EL0() const {
return thread_context_64.tpidr;
}
/// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
void SetTPIDR_EL0(u64 value) {
thread_context_64.tpidr = value;
thread_context_32.tpidr = static_cast<u32>(value);
}
[[nodiscard]] ThreadContext32& GetContext32() {
return thread_context_32;
}
[[nodiscard]] const ThreadContext32& GetContext32() const {
return thread_context_32;
}
[[nodiscard]] ThreadContext64& GetContext64() {
return thread_context_64;
}
[[nodiscard]] const ThreadContext64& GetContext64() const {
return thread_context_64;
}
[[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
[[nodiscard]] ThreadState GetState() const {
return thread_state & ThreadState::Mask;
}
[[nodiscard]] ThreadState GetRawState() const {
return thread_state;
}
void SetState(ThreadState state);
[[nodiscard]] s64 GetLastScheduledTick() const {
return last_scheduled_tick;
}
void SetLastScheduledTick(s64 tick) {
last_scheduled_tick = tick;
}
void AddCpuTime([[maybe_unused]] s32 core_id_, s64 amount) {
cpu_time += amount;
// TODO(bunnei): Debug kernels track per-core tick counts. Should we?
}
[[nodiscard]] s64 GetCpuTime() const {
return cpu_time;
}
[[nodiscard]] s32 GetActiveCore() const {
return core_id;
}
void SetActiveCore(s32 core) {
core_id = core;
}
[[nodiscard]] s32 GetCurrentCore() const {
return current_core_id;
}
void SetCurrentCore(s32 core) {
current_core_id = core;
}
[[nodiscard]] Process* GetOwnerProcess() {
return parent;
}
[[nodiscard]] const Process* GetOwnerProcess() const {
return parent;
}
[[nodiscard]] bool IsUserThread() const {
return parent != nullptr;
}
[[nodiscard]] KThread* GetLockOwner() const {
return lock_owner;
}
void SetLockOwner(KThread* owner) {
lock_owner = owner;
}
[[nodiscard]] const KAffinityMask& GetAffinityMask() const {
return physical_affinity_mask;
}
[[nodiscard]] ResultCode GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
[[nodiscard]] ResultCode GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
[[nodiscard]] ResultCode SetCoreMask(s32 core_id, u64 v_affinity_mask);
[[nodiscard]] ResultCode SetActivity(Svc::ThreadActivity activity);
[[nodiscard]] ResultCode Sleep(s64 timeout);
[[nodiscard]] s64 GetYieldScheduleCount() const {
return schedule_count;
}
void SetYieldScheduleCount(s64 count) {
schedule_count = count;
}
void WaitCancel();
[[nodiscard]] bool IsWaitCancelled() const {
return wait_cancelled;
}
[[nodiscard]] void ClearWaitCancelled() {
wait_cancelled = false;
}
[[nodiscard]] bool IsCancellable() const {
return cancellable;
}
void SetCancellable() {
cancellable = true;
}
void ClearCancellable() {
cancellable = false;
}
[[nodiscard]] bool IsTerminationRequested() const {
return termination_requested || GetRawState() == ThreadState::Terminated;
}
struct StackParameters {
u8 svc_permission[0x10];
std::atomic<u8> dpc_flags;
u8 current_svc_id;
bool is_calling_svc;
bool is_in_exception_handler;
bool is_pinned;
s32 disable_count;
KThread* cur_thread;
};
[[nodiscard]] StackParameters& GetStackParameters() {
return stack_parameters;
}
[[nodiscard]] const StackParameters& GetStackParameters() const {
return stack_parameters;
}
class QueueEntry {
public:
constexpr QueueEntry() = default;
constexpr void Initialize() {
prev = nullptr;
next = nullptr;
}
constexpr KThread* GetPrev() const {
return prev;
}
constexpr KThread* GetNext() const {
return next;
}
constexpr void SetPrev(KThread* thread) {
prev = thread;
}
constexpr void SetNext(KThread* thread) {
next = thread;
}
private:
KThread* prev{};
KThread* next{};
};
[[nodiscard]] QueueEntry& GetPriorityQueueEntry(s32 core) {
return per_core_priority_queue_entry[core];
}
[[nodiscard]] const QueueEntry& GetPriorityQueueEntry(s32 core) const {
return per_core_priority_queue_entry[core];
}
void SetSleepingQueue(KThreadQueue* q) {
sleeping_queue = q;
}
[[nodiscard]] s32 GetDisableDispatchCount() const {
return this->GetStackParameters().disable_count;
}
void DisableDispatch() {
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
this->GetStackParameters().disable_count++;
}
void EnableDispatch() {
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
this->GetStackParameters().disable_count--;
}
void Pin();
void Unpin();
void SetInExceptionHandler() {
this->GetStackParameters().is_in_exception_handler = true;
}
void ClearInExceptionHandler() {
this->GetStackParameters().is_in_exception_handler = false;
}
[[nodiscard]] bool IsInExceptionHandler() const {
return this->GetStackParameters().is_in_exception_handler;
}
void SetIsCallingSvc() {
this->GetStackParameters().is_calling_svc = true;
}
void ClearIsCallingSvc() {
this->GetStackParameters().is_calling_svc = false;
}
[[nodiscard]] bool IsCallingSvc() const {
return this->GetStackParameters().is_calling_svc;
}
[[nodiscard]] u8 GetSvcId() const {
return this->GetStackParameters().current_svc_id;
}
void RegisterDpc(DpcFlag flag) {
this->GetStackParameters().dpc_flags |= static_cast<u8>(flag);
}
void ClearDpc(DpcFlag flag) {
this->GetStackParameters().dpc_flags &= ~static_cast<u8>(flag);
}
[[nodiscard]] u8 GetDpc() const {
return this->GetStackParameters().dpc_flags;
}
[[nodiscard]] bool HasDpc() const {
return this->GetDpc() != 0;
}
void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) {
wait_reason_for_debugging = reason;
}
[[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const {
return wait_reason_for_debugging;
}
[[nodiscard]] ThreadType GetThreadTypeForDebugging() const {
return thread_type_for_debugging;
}
void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) {
wait_objects_for_debugging.clear();
wait_objects_for_debugging.reserve(objects.size());
for (const auto& object : objects) {
wait_objects_for_debugging.emplace_back(object);
}
}
[[nodiscard]] const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const {
return wait_objects_for_debugging;
}
void SetMutexWaitAddressForDebugging(VAddr address) {
mutex_wait_address_for_debugging = address;
}
[[nodiscard]] VAddr GetMutexWaitAddressForDebugging() const {
return mutex_wait_address_for_debugging;
}
[[nodiscard]] s32 GetIdealCoreForDebugging() const {
return virtual_ideal_core_id;
}
void AddWaiter(KThread* thread);
void RemoveWaiter(KThread* thread);
[[nodiscard]] ResultCode GetThreadContext3(std::vector<u8>& out);
[[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key);
[[nodiscard]] VAddr GetAddressKey() const {
return address_key;
}
[[nodiscard]] u32 GetAddressKeyValue() const {
return address_key_value;
}
void SetAddressKey(VAddr key) {
address_key = key;
}
void SetAddressKey(VAddr key, u32 val) {
address_key = key;
address_key_value = val;
}
[[nodiscard]] bool HasWaiters() const {
return !waiter_list.empty();
}
[[nodiscard]] s32 GetNumKernelWaiters() const {
return num_kernel_waiters;
}
[[nodiscard]] u64 GetConditionVariableKey() const {
return condvar_key;
}
[[nodiscard]] u64 GetAddressArbiterKey() const {
return condvar_key;
}
private:
static constexpr size_t PriorityInheritanceCountMax = 10;
union SyncObjectBuffer {
std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
std::array<Handle,
Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))>
handles;
constexpr SyncObjectBuffer() {}
};
static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
struct ConditionVariableComparator {
struct LightCompareType {
u64 cv_key{};
s32 priority{};
[[nodiscard]] constexpr u64 GetConditionVariableKey() const {
return cv_key;
}
[[nodiscard]] constexpr s32 GetPriority() const {
return priority;
}
};
template <typename T>
requires(
std::same_as<T, KThread> ||
std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
const KThread& rhs) {
const u64 l_key = lhs.GetConditionVariableKey();
const u64 r_key = rhs.GetConditionVariableKey();
if (l_key < r_key) {
// Sort first by key
return -1;
} else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) {
// And then by priority.
return -1;
} else {
return 1;
}
}
};
void AddWaiterImpl(KThread* thread);
void RemoveWaiterImpl(KThread* thread);
void StartTermination();
[[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
s32 prio, s32 virt_core, Process* owner, ThreadType type);
[[nodiscard]] static ResultCode InitializeThread(KThread* thread, KThreadFunction func,
uintptr_t arg, VAddr user_stack_top, s32 prio,
s32 core, Process* owner, ThreadType type);
static void RestorePriority(KernelCore& kernel, KThread* thread);
// For core KThread implementation
ThreadContext32 thread_context_32{};
ThreadContext64 thread_context_64{};
Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{};
s32 priority{};
using ConditionVariableThreadTreeTraits =
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<
&KThread::condvar_arbiter_tree_node>;
using ConditionVariableThreadTree =
ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>;
ConditionVariableThreadTree* condvar_tree{};
u64 condvar_key{};
u64 virtual_affinity_mask{};
KAffinityMask physical_affinity_mask{};
u64 thread_id{};
std::atomic<s64> cpu_time{};
KSynchronizationObject* synced_object{};
VAddr address_key{};
Process* parent{};
VAddr kernel_stack_top{};
u32* light_ipc_data{};
VAddr tls_address{};
KLightLock activity_pause_lock;
s64 schedule_count{};
s64 last_scheduled_tick{};
std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
KThreadQueue* sleeping_queue{};
WaiterList waiter_list{};
WaiterList pinned_waiter_list{};
KThread* lock_owner{};
u32 address_key_value{};
u32 suspend_request_flags{};
u32 suspend_allowed_flags{};
ResultCode wait_result{RESULT_SUCCESS};
s32 base_priority{};
s32 physical_ideal_core_id{};
s32 virtual_ideal_core_id{};
s32 num_kernel_waiters{};
s32 current_core_id{};
s32 core_id{};
KAffinityMask original_physical_affinity_mask{};
s32 original_physical_ideal_core_id{};
s32 num_core_migration_disables{};
ThreadState thread_state{};
std::atomic<bool> termination_requested{};
bool wait_cancelled{};
bool cancellable{};
bool signaled{};
bool initialized{};
bool debug_attached{};
s8 priority_inheritance_count{};
bool resource_limit_release_hint{};
StackParameters stack_parameters{};
Common::SpinLock context_guard{};
// For emulation
std::shared_ptr<Common::Fiber> host_context{};
// For debugging
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
VAddr mutex_wait_address_for_debugging{};
ThreadWaitReasonForDebugging wait_reason_for_debugging{};
ThreadType thread_type_for_debugging{};
std::string name;
public:
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, u64 cv_key,
u32 value) {
condvar_tree = tree;
condvar_key = cv_key;
address_key = address;
address_key_value = value;
}
void ClearConditionVariable() {
condvar_tree = nullptr;
}
[[nodiscard]] bool IsWaitingForConditionVariable() const {
return condvar_tree != nullptr;
}
void SetAddressArbiter(ConditionVariableThreadTree* tree, u64 address) {
condvar_tree = tree;
condvar_key = address;
}
void ClearAddressArbiter() {
condvar_tree = nullptr;
}
[[nodiscard]] bool IsWaitingForAddressArbiter() const {
return condvar_tree != nullptr;
}
[[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const {
return condvar_tree;
}
};
} // namespace Kernel

View File

@@ -0,0 +1,81 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
class KThreadQueue {
public:
explicit KThreadQueue(KernelCore& kernel) : kernel{kernel} {}
bool IsEmpty() const {
return wait_list.empty();
}
KThread::WaiterList::iterator begin() {
return wait_list.begin();
}
KThread::WaiterList::iterator end() {
return wait_list.end();
}
bool SleepThread(KThread* t) {
KScopedSchedulerLock sl{kernel};
// If the thread needs terminating, don't enqueue it.
if (t->IsTerminationRequested()) {
return false;
}
// Set the thread's queue and mark it as waiting.
t->SetSleepingQueue(this);
t->SetState(ThreadState::Waiting);
// Add the thread to the queue.
wait_list.push_back(*t);
return true;
}
void WakeupThread(KThread* t) {
KScopedSchedulerLock sl{kernel};
// Remove the thread from the queue.
wait_list.erase(wait_list.iterator_to(*t));
// Mark the thread as no longer sleeping.
t->SetState(ThreadState::Runnable);
t->SetSleepingQueue(nullptr);
}
KThread* WakeupFrontThread() {
KScopedSchedulerLock sl{kernel};
if (wait_list.empty()) {
return nullptr;
} else {
// Remove the thread from the queue.
auto it = wait_list.begin();
KThread* thread = std::addressof(*it);
wait_list.erase(it);
ASSERT(thread->GetState() == ThreadState::Waiting);
// Mark the thread as no longer sleeping.
thread->SetState(ThreadState::Runnable);
thread->SetSleepingQueue(nullptr);
return thread;
}
}
private:
KernelCore& kernel;
KThread::WaiterList wait_list{};
};
} // namespace Kernel

View File

@@ -28,17 +28,17 @@
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/memory/memory_manager.h"
#include "core/hle/kernel/memory/slab_heap.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/service_thread.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
@@ -57,14 +57,16 @@ struct KernelCore::Impl {
}
void Initialize(KernelCore& kernel) {
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
RegisterHostThread();
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
service_thread_manager =
std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
is_phantom_mode_for_singlecore = false;
InitializePhysicalCores();
InitializeSystemResourceLimit(kernel);
InitializeSystemResourceLimit(kernel, system);
InitializeMemoryLayout();
InitializePreemption(kernel);
InitializeSchedulers();
@@ -116,32 +118,32 @@ struct KernelCore::Impl {
void InitializePhysicalCores() {
exclusive_monitor =
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i);
cores.emplace_back(i, system, *schedulers[i], interrupts);
}
}
void InitializeSchedulers() {
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
cores[i].Scheduler().Initialize();
}
}
// Creates the default system resource limit
void InitializeSystemResourceLimit(KernelCore& kernel) {
system_resource_limit = ResourceLimit::Create(kernel);
void InitializeSystemResourceLimit(KernelCore& kernel, Core::System& system) {
system_resource_limit = std::make_shared<KResourceLimit>(kernel, system);
// If setting the default system values fails, then something seriously wrong has occurred.
ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x100000000)
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, 0x100000000)
.IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 700).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200)
.IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 900).IsSuccess());
if (!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0) ||
!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0x60000)) {
if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 0x60000)) {
UNREACHABLE();
}
}
@@ -168,11 +170,9 @@ struct KernelCore::Impl {
std::string name = "Suspend Thread Id:" + std::to_string(i);
std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
const auto type =
static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND);
auto thread_res =
Thread::Create(system, type, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
nullptr, std::move(init_func), init_func_parameter);
auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0,
0, 0, static_cast<u32>(i), 0, nullptr,
std::move(init_func), init_func_parameter);
suspend_threads[i] = std::move(thread_res).Unwrap();
}
@@ -207,6 +207,17 @@ struct KernelCore::Impl {
return host_thread_id;
}
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread() {
const thread_local auto thread =
KThread::Create(
system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0,
KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr,
[]([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr)
.Unwrap();
return thread.get();
}
/// Registers a CPU core thread by allocating a host thread ID for it
void RegisterCoreThread(std::size_t core_id) {
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
@@ -219,6 +230,7 @@ struct KernelCore::Impl {
/// Registers a new host thread by allocating a host thread ID for it
void RegisterHostThread() {
[[maybe_unused]] const auto this_id = GetHostThreadId();
[[maybe_unused]] const auto dummy_thread = GetHostDummyThread();
}
[[nodiscard]] u32 GetCurrentHostThreadID() {
@@ -229,20 +241,21 @@ struct KernelCore::Impl {
return this_id;
}
[[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() {
Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();
result.host_handle = GetCurrentHostThreadID();
if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) {
return result;
bool IsPhantomModeForSingleCore() const {
return is_phantom_mode_for_singlecore;
}
void SetIsPhantomModeForSingleCore(bool value) {
ASSERT(!is_multicore);
is_phantom_mode_for_singlecore = value;
}
KThread* GetCurrentEmuThread() {
const auto thread_id = GetCurrentHostThreadID();
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
return GetHostDummyThread();
}
const Kernel::KScheduler& sched = cores[result.host_handle].Scheduler();
const Kernel::Thread* current = sched.GetCurrentThread();
if (current != nullptr && !current->IsPhantomMode()) {
result.guest_handle = current->GetGlobalHandle();
} else {
result.guest_handle = InvalidHandle;
}
return result;
return schedulers[thread_id]->GetCurrentThread();
}
void InitializeMemoryLayout() {
@@ -307,7 +320,7 @@ struct KernelCore::Impl {
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
Kernel::TimeManager time_manager;
std::shared_ptr<ResourceLimit> system_resource_limit;
std::shared_ptr<KResourceLimit> system_resource_limit;
std::shared_ptr<Core::Timing::EventType> preemption_event;
@@ -342,11 +355,12 @@ struct KernelCore::Impl {
// the release of itself
std::unique_ptr<Common::ThreadWorker> service_thread_manager;
std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
std::array<std::shared_ptr<KThread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
bool is_multicore{};
bool is_phantom_mode_for_singlecore{};
u32 single_core_thread_id{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
@@ -376,12 +390,12 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
std::shared_ptr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
std::shared_ptr<KResourceLimit> KernelCore::GetSystemResourceLimit() const {
return impl->system_resource_limit;
}
std::shared_ptr<Thread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
return impl->global_handle_table.Get<Thread>(handle);
std::shared_ptr<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
return impl->global_handle_table.Get<KThread>(handle);
}
void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
@@ -546,8 +560,8 @@ u32 KernelCore::GetCurrentHostThreadID() const {
return impl->GetCurrentHostThreadID();
}
Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const {
return impl->GetCurrentEmuThreadID();
KThread* KernelCore::GetCurrentEmuThread() const {
return impl->GetCurrentEmuThread();
}
Memory::MemoryManager& KernelCore::MemoryManager() {
@@ -645,4 +659,12 @@ void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> servi
});
}
bool KernelCore::IsPhantomModeForSingleCore() const {
return impl->IsPhantomModeForSingleCore();
}
void KernelCore::SetIsPhantomModeForSingleCore(bool value) {
impl->SetIsPhantomModeForSingleCore(value);
}
} // namespace Kernel

View File

@@ -38,14 +38,18 @@ class GlobalSchedulerContext;
class HandleTable;
class PhysicalCore;
class Process;
class ResourceLimit;
class KResourceLimit;
class KScheduler;
class SharedMemory;
class ServiceThread;
class Synchronization;
class Thread;
class KThread;
class TimeManager;
using EmuThreadHandle = uintptr_t;
constexpr EmuThreadHandle EmuThreadHandleInvalid{};
constexpr EmuThreadHandle EmuThreadHandleReserved{1ULL << 63};
/// Represents a single instance of the kernel.
class KernelCore {
private:
@@ -81,10 +85,10 @@ public:
void Shutdown();
/// Retrieves a shared pointer to the system resource limit instance.
std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const;
std::shared_ptr<KResourceLimit> GetSystemResourceLimit() const;
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
std::shared_ptr<Thread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
std::shared_ptr<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
/// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(std::shared_ptr<Process> process);
@@ -161,8 +165,8 @@ public:
/// Determines whether or not the given port is a valid named port.
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
/// Gets the current host_thread/guest_thread handle.
Core::EmuThreadHandle GetCurrentEmuThreadID() const;
/// Gets the current host_thread/guest_thread pointer.
KThread* GetCurrentEmuThread() const;
/// Gets the current host_thread handle.
u32 GetCurrentHostThreadID() const;
@@ -237,10 +241,14 @@ public:
*/
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread);
/// Workaround for single-core mode when preempting threads while idle.
bool IsPhantomModeForSingleCore() const;
void SetIsPhantomModeForSingleCore(bool value);
private:
friend class Object;
friend class Process;
friend class Thread;
friend class KThread;
/// Creates a new object ID, incrementing the internal object ID counter.
u32 CreateNewObjectID();

View File

@@ -7,6 +7,7 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/address_space_info.h"
#include "core/hle/kernel/memory/memory_block.h"
@@ -15,7 +16,6 @@
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/memory/system_control.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/memory.h"
namespace Kernel::Memory {
@@ -414,7 +414,7 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
const std::size_t remaining_pages{remaining_size / PageSize};
if (process->GetResourceLimit() &&
!process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, remaining_size)) {
!process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, remaining_size)) {
return ERR_RESOURCE_LIMIT_EXCEEDED;
}
@@ -422,7 +422,7 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
{
auto block_guard = detail::ScopeExit([&] {
system.Kernel().MemoryManager().Free(page_linked_list, remaining_pages, memory_pool);
process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, remaining_size);
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, remaining_size);
});
CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages,
@@ -474,7 +474,7 @@ ResultCode PageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
CASCADE_CODE(UnmapMemory(addr, size));
auto process{system.Kernel().CurrentProcess()};
process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, mapped_size);
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
physical_memory_usage -= mapped_size;
return RESULT_SUCCESS;
@@ -783,7 +783,7 @@ ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) {
auto process{system.Kernel().CurrentProcess()};
if (process->GetResourceLimit() && delta != 0 &&
!process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, delta)) {
!process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, delta)) {
return ERR_RESOURCE_LIMIT_EXCEEDED;
}

View File

@@ -61,6 +61,8 @@ public:
*/
bool IsWaitable() const;
virtual void Finalize() = 0;
protected:
/// The kernel instance this object was created under.
KernelCore& kernel;

View File

@@ -15,14 +15,14 @@
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_block_manager.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/memory/slab_heap.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/memory.h"
#include "core/settings.h"
@@ -38,11 +38,10 @@ namespace {
*/
void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
ThreadType type = THREADTYPE_USER;
auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0,
owner_process.GetIdealCore(), stack_top, &owner_process);
auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0,
owner_process.GetIdealCoreId(), stack_top, &owner_process);
std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap();
std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
@@ -117,7 +116,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
std::shared_ptr<Process> process = std::make_shared<Process>(system);
process->name = std::move(name);
process->resource_limit = ResourceLimit::Create(kernel);
process->resource_limit = std::make_shared<KResourceLimit>(kernel, system);
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
@@ -133,12 +132,29 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
return process;
}
std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const {
std::shared_ptr<KResourceLimit> Process::GetResourceLimit() const {
return resource_limit;
}
void Process::IncrementThreadCount() {
ASSERT(num_threads >= 0);
num_created_threads++;
if (const auto count = ++num_threads; count > peak_num_threads) {
peak_num_threads = count;
}
}
void Process::DecrementThreadCount() {
ASSERT(num_threads > 0);
if (const auto count = --num_threads; count == 0) {
UNIMPLEMENTED_MSG("Process termination is not implemented!");
}
}
u64 Process::GetTotalPhysicalMemoryAvailable() const {
const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) +
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
main_thread_stack_size};
@@ -162,11 +178,66 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
}
void Process::RegisterThread(const Thread* thread) {
bool Process::ReleaseUserException(KThread* thread) {
KScopedSchedulerLock sl{kernel};
if (exception_thread == thread) {
exception_thread = nullptr;
// Remove waiter thread.
s32 num_waiters{};
KThread* next = thread->RemoveWaiterByKey(
std::addressof(num_waiters),
reinterpret_cast<uintptr_t>(std::addressof(exception_thread)));
if (next != nullptr) {
if (next->GetState() == ThreadState::Waiting) {
next->SetState(ThreadState::Runnable);
} else {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
}
return true;
} else {
return false;
}
}
void Process::PinCurrentThread() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
const s32 core_id = GetCurrentCoreId(kernel);
KThread* cur_thread = GetCurrentThreadPointer(kernel);
// Pin it.
PinThread(core_id, cur_thread);
cur_thread->Pin();
// An update is needed.
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
void Process::UnpinCurrentThread() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
const s32 core_id = GetCurrentCoreId(kernel);
KThread* cur_thread = GetCurrentThreadPointer(kernel);
// Unpin it.
cur_thread->Unpin();
UnpinThread(core_id, cur_thread);
// An update is needed.
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
void Process::RegisterThread(const KThread* thread) {
thread_list.push_back(thread);
}
void Process::UnregisterThread(const Thread* thread) {
void Process::UnregisterThread(const KThread* thread) {
thread_list.remove(thread);
}
@@ -237,13 +308,13 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
// Set initial resource limits
resource_limit->SetLimitValue(
ResourceType::PhysicalMemory,
LimitableResource::PhysicalMemory,
kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application));
resource_limit->SetLimitValue(ResourceType::Threads, 608);
resource_limit->SetLimitValue(ResourceType::Events, 700);
resource_limit->SetLimitValue(ResourceType::TransferMemory, 128);
resource_limit->SetLimitValue(ResourceType::Sessions, 894);
ASSERT(resource_limit->Reserve(ResourceType::PhysicalMemory, code_size));
resource_limit->SetLimitValue(LimitableResource::Threads, 608);
resource_limit->SetLimitValue(LimitableResource::Events, 700);
resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128);
resource_limit->SetLimitValue(LimitableResource::Sessions, 894);
ASSERT(resource_limit->Reserve(LimitableResource::PhysicalMemory, code_size));
// Create TLS region
tls_region_address = CreateTLSRegion();
@@ -260,14 +331,14 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) {
ChangeStatus(ProcessStatus::Running);
SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top);
resource_limit->Reserve(ResourceType::Threads, 1);
resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size);
resource_limit->Reserve(LimitableResource::Threads, 1);
resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
}
void Process::PrepareForTermination() {
ChangeStatus(ProcessStatus::Exiting);
const auto stop_threads = [this](const std::vector<std::shared_ptr<Thread>>& thread_list) {
const auto stop_threads = [this](const std::vector<std::shared_ptr<KThread>>& thread_list) {
for (auto& thread : thread_list) {
if (thread->GetOwnerProcess() != this)
continue;
@@ -279,7 +350,7 @@ void Process::PrepareForTermination() {
ASSERT_MSG(thread->GetState() == ThreadState::Waiting,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
thread->Exit();
}
};
@@ -372,7 +443,7 @@ bool Process::IsSignaled() const {
Process::Process(Core::System& system)
: KSynchronizationObject{system.Kernel()},
page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()},
address_arbiter{system}, condition_var{system}, system{system} {}
address_arbiter{system}, condition_var{system}, state_lock{system.Kernel()}, system{system} {}
Process::~Process() = default;

View File

@@ -29,8 +29,8 @@ class ProgramMetadata;
namespace Kernel {
class KernelCore;
class ResourceLimit;
class Thread;
class KResourceLimit;
class KThread;
class TLSPage;
struct CodeSet;
@@ -170,13 +170,18 @@ public:
}
/// Gets the resource limit descriptor for this process
std::shared_ptr<ResourceLimit> GetResourceLimit() const;
std::shared_ptr<KResourceLimit> GetResourceLimit() const;
/// Gets the ideal CPU core ID for this process
u8 GetIdealCore() const {
u8 GetIdealCoreId() const {
return ideal_core;
}
/// Checks if the specified thread priority is valid.
bool CheckThreadPriority(s32 prio) const {
return ((1ULL << prio) & GetPriorityMask()) != 0;
}
/// Gets the bitmask of allowed cores that this process' threads can run on.
u64 GetCoreMask() const {
return capabilities.GetCoreMask();
@@ -212,6 +217,14 @@ public:
return is_64bit_process;
}
[[nodiscard]] bool IsSuspended() const {
return is_suspended;
}
void SetSuspended(bool suspended) {
is_suspended = suspended;
}
/// Gets the total running time of the process instance in ticks.
u64 GetCPUTimeTicks() const {
return total_process_running_time_ticks;
@@ -232,6 +245,33 @@ public:
++schedule_count;
}
void IncrementThreadCount();
void DecrementThreadCount();
void SetRunningThread(s32 core, KThread* thread, u64 idle_count) {
running_threads[core] = thread;
running_thread_idle_counts[core] = idle_count;
}
void ClearRunningThread(KThread* thread) {
for (size_t i = 0; i < running_threads.size(); ++i) {
if (running_threads[i] == thread) {
running_threads[i] = nullptr;
}
}
}
[[nodiscard]] KThread* GetRunningThread(s32 core) const {
return running_threads[core];
}
bool ReleaseUserException(KThread* thread);
[[nodiscard]] KThread* GetPinnedThread(s32 core_id) const {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
return pinned_threads[core_id];
}
/// Gets 8 bytes of random data for svcGetInfo RandomEntropy
u64 GetRandomEntropy(std::size_t index) const {
return random_entropy.at(index);
@@ -252,17 +292,17 @@ public:
u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
/// Gets the list of all threads created with this process as their owner.
const std::list<const Thread*>& GetThreadList() const {
const std::list<const KThread*>& GetThreadList() const {
return thread_list;
}
/// Registers a thread as being created under this process,
/// adding it to this process' thread list.
void RegisterThread(const Thread* thread);
void RegisterThread(const KThread* thread);
/// Unregisters a thread from this process, removing it
/// from this process' thread list.
void UnregisterThread(const Thread* thread);
void UnregisterThread(const KThread* thread);
/// Clears the signaled state of the process if and only if it's signaled.
///
@@ -303,6 +343,15 @@ public:
bool IsSignaled() const override;
void Finalize() override {}
void PinCurrentThread();
void UnpinCurrentThread();
KLightLock& GetStateLock() {
return state_lock;
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Thread-local storage management
@@ -313,6 +362,20 @@ public:
void FreeTLSRegion(VAddr tls_address);
private:
void PinThread(s32 core_id, KThread* thread) {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
ASSERT(thread != nullptr);
ASSERT(pinned_threads[core_id] == nullptr);
pinned_threads[core_id] = thread;
}
void UnpinThread(s32 core_id, KThread* thread) {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
ASSERT(thread != nullptr);
ASSERT(pinned_threads[core_id] == thread);
pinned_threads[core_id] = nullptr;
}
/// Changes the process status. If the status is different
/// from the current process status, then this will trigger
/// a process signal.
@@ -339,7 +402,7 @@ private:
u32 system_resource_size = 0;
/// Resource limit descriptor for this process
std::shared_ptr<ResourceLimit> resource_limit;
std::shared_ptr<KResourceLimit> resource_limit;
/// The ideal CPU core for this process, threads are scheduled on this core by default.
u8 ideal_core = 0;
@@ -380,7 +443,7 @@ private:
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
/// List of threads that are running with this process as their owner.
std::list<const Thread*> thread_list;
std::list<const KThread*> thread_list;
/// Address of the top of the main thread's stack
VAddr main_thread_stack_top{};
@@ -401,6 +464,19 @@ private:
s64 schedule_count{};
bool is_signaled{};
bool is_suspended{};
std::atomic<s32> num_created_threads{};
std::atomic<u16> num_threads{};
u16 peak_num_threads{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{};
KThread* exception_thread{};
KLightLock state_lock;
/// System context
Core::System& system;

View File

@@ -7,10 +7,10 @@
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {

View File

@@ -47,6 +47,8 @@ public:
bool IsSignaled() const override;
void Finalize() override {}
private:
explicit ReadableEvent(KernelCore& kernel);

View File

@@ -1,73 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/result.h"
namespace Kernel {
namespace {
constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
return static_cast<std::size_t>(type);
}
} // Anonymous namespace
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
ResourceLimit::~ResourceLimit() = default;
bool ResourceLimit::Reserve(ResourceType resource, s64 amount) {
return Reserve(resource, amount, 10000000000);
}
bool ResourceLimit::Reserve(ResourceType resource, s64 amount, u64 timeout) {
const std::size_t index{ResourceTypeToIndex(resource)};
s64 new_value = current[index] + amount;
if (new_value > limit[index] && available[index] + amount <= limit[index]) {
// TODO(bunnei): This is wrong for multicore, we should wait the calling thread for timeout
new_value = current[index] + amount;
}
if (new_value <= limit[index]) {
current[index] = new_value;
return true;
}
return false;
}
void ResourceLimit::Release(ResourceType resource, u64 amount) {
Release(resource, amount, amount);
}
void ResourceLimit::Release(ResourceType resource, u64 used_amount, u64 available_amount) {
const std::size_t index{ResourceTypeToIndex(resource)};
current[index] -= used_amount;
available[index] -= available_amount;
}
std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) {
return std::make_shared<ResourceLimit>(kernel);
}
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
return limit.at(ResourceTypeToIndex(resource)) - current.at(ResourceTypeToIndex(resource));
}
s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
return limit.at(ResourceTypeToIndex(resource));
}
ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
const std::size_t index{ResourceTypeToIndex(resource)};
if (current[index] <= value) {
limit[index] = value;
return RESULT_SUCCESS;
} else {
LOG_ERROR(Kernel, "Limit value is too large! resource={}, value={}, index={}", resource,
value, index);
return ERR_INVALID_STATE;
}
}
} // namespace Kernel

View File

@@ -1,104 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
union ResultCode;
namespace Kernel {
class KernelCore;
enum class ResourceType : u32 {
PhysicalMemory,
Threads,
Events,
TransferMemory,
Sessions,
// Used as a count, not an actual type.
ResourceTypeCount
};
constexpr bool IsValidResourceType(ResourceType type) {
return type < ResourceType::ResourceTypeCount;
}
class ResourceLimit final : public Object {
public:
explicit ResourceLimit(KernelCore& kernel);
~ResourceLimit() override;
/// Creates a resource limit object.
static std::shared_ptr<ResourceLimit> Create(KernelCore& kernel);
std::string GetTypeName() const override {
return "ResourceLimit";
}
std::string GetName() const override {
return GetTypeName();
}
static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
bool Reserve(ResourceType resource, s64 amount);
bool Reserve(ResourceType resource, s64 amount, u64 timeout);
void Release(ResourceType resource, u64 amount);
void Release(ResourceType resource, u64 used_amount, u64 available_amount);
/**
* Gets the current value for the specified resource.
* @param resource Requested resource type
* @returns The current value of the resource type
*/
s64 GetCurrentResourceValue(ResourceType resource) const;
/**
* Gets the max value for the specified resource.
* @param resource Requested resource type
* @returns The max value of the resource type
*/
s64 GetMaxResourceValue(ResourceType resource) const;
/**
* Sets the limit value for a given resource type.
*
* @param resource The resource type to apply the limit to.
* @param value The limit to apply to the given resource type.
*
* @return A result code indicating if setting the limit value
* was successful or not.
*
* @note The supplied limit value *must* be greater than or equal to
* the current resource value for the given resource type,
* otherwise ERR_INVALID_STATE will be returned.
*/
ResultCode SetLimitValue(ResourceType resource, s64 value);
private:
// TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
// functions
//
// Currently we have no way of distinguishing if a Create was called by the running application,
// or by a service module. Approach this once we have separated the service modules into their
// own processes
using ResourceArray =
std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>;
ResourceArray limit{};
ResourceArray current{};
ResourceArray available{};
};
} // namespace Kernel

View File

@@ -6,10 +6,10 @@
#include "common/assert.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {

View File

@@ -81,6 +81,8 @@ public:
bool IsSignaled() const override;
void Finalize() override {}
private:
/// ServerSessions waiting to be accepted by the port
std::vector<std::shared_ptr<ServerSession>> pending_sessions;

View File

@@ -15,11 +15,11 @@
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
#include "core/memory.h"
namespace Kernel {
@@ -116,7 +116,7 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
return RESULT_SUCCESS;
}
ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,
ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<KThread> thread,
Core::Memory::Memory& memory) {
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
auto context =
@@ -154,14 +154,14 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
KScopedSchedulerLock lock(kernel);
if (!context.IsThreadWaiting()) {
context.GetThread().Wakeup();
context.GetThread().SetSynchronizationResults(nullptr, result);
context.GetThread().SetSyncedObject(nullptr, result);
}
}
return result;
}
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<KThread> thread,
Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing) {
return QueueSyncRequest(std::move(thread), memory);

View File

@@ -29,7 +29,7 @@ class HLERequestContext;
class KernelCore;
class Session;
class SessionRequestHandler;
class Thread;
class KThread;
/**
* Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
@@ -95,7 +95,7 @@ public:
*
* @returns ResultCode from the operation.
*/
ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
ResultCode HandleSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing);
/// Called when a client disconnection occurs.
@@ -126,9 +126,11 @@ public:
bool IsSignaled() const override;
void Finalize() override {}
private:
/// Queues a sync request from the emulated application.
ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
ResultCode QueueSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory);
/// Completes a sync request from the emulated application.
ResultCode CompleteSyncRequest(HLERequestContext& context);
@@ -149,12 +151,12 @@ private:
/// List of threads that are pending a response after a sync request. This list is processed in
/// a LIFO manner, thus, the last request will be dispatched first.
/// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
std::vector<std::shared_ptr<Thread>> pending_requesting_threads;
std::vector<std::shared_ptr<KThread>> pending_requesting_threads;
/// Thread whose request is currently being handled. A request is considered "handled" when a
/// response is sent via svcReplyAndReceive.
/// TODO(Subv): Find a better name for this.
std::shared_ptr<Thread> currently_handling;
std::shared_ptr<KThread> currently_handling;
/// When set to True, converts the session to a domain at the end of the command
bool convert_to_domain{};

View File

@@ -39,6 +39,8 @@ public:
bool IsSignaled() const override;
void Finalize() override {}
std::shared_ptr<ClientSession> Client() {
if (auto result{client.lock()}) {
return result;

View File

@@ -71,6 +71,8 @@ public:
return device_memory.GetPointer(physical_address + offset);
}
void Finalize() override {}
private:
Core::DeviceMemory& device_memory;
Process* owner_process{};

View File

@@ -26,9 +26,11 @@
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/memory/memory_layout.h"
@@ -36,13 +38,11 @@
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/kernel/svc_wrap.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/kernel/transfer_memory.h"
#include "core/hle/kernel/writable_event.h"
@@ -141,7 +141,7 @@ enum class ResourceLimitValueType {
ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit,
u32 resource_type, ResourceLimitValueType value_type) {
std::lock_guard lock{HLE::g_hle_lock};
const auto type = static_cast<ResourceType>(resource_type);
const auto type = static_cast<LimitableResource>(resource_type);
if (!IsValidResourceType(type)) {
LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
return ERR_INVALID_ENUM_VALUE;
@@ -151,7 +151,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_
ASSERT(current_process != nullptr);
const auto resource_limit_object =
current_process->GetHandleTable().Get<ResourceLimit>(resource_limit);
current_process->GetHandleTable().Get<KResourceLimit>(resource_limit);
if (!resource_limit_object) {
LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
resource_limit);
@@ -159,10 +159,10 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_
}
if (value_type == ResourceLimitValueType::CurrentValue) {
return MakeResult(resource_limit_object->GetCurrentResourceValue(type));
return MakeResult(resource_limit_object->GetCurrentValue(type));
}
return MakeResult(resource_limit_object->GetMaxResourceValue(type));
return MakeResult(resource_limit_object->GetLimitValue(type));
}
} // Anonymous namespace
@@ -312,7 +312,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
return ERR_NOT_FOUND;
}
ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Sessions, 1));
ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(LimitableResource::Sessions, 1));
auto client_port = it->second;
@@ -351,7 +351,8 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());
}
return thread->GetSignalingResult();
KSynchronizationObject* dummy{};
return thread->GetWaitResult(std::addressof(dummy));
}
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
@@ -359,27 +360,26 @@ static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
}
/// Get the ID for the specified thread.
static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) {
static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
// Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle);
return ERR_INVALID_HANDLE;
}
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
R_UNLESS(thread, Svc::ResultInvalidHandle);
*thread_id = thread->GetThreadID();
// Get the thread's id.
*out_thread_id = thread->GetThreadID();
return RESULT_SUCCESS;
}
static ResultCode GetThreadId32(Core::System& system, u32* thread_id_low, u32* thread_id_high,
Handle thread_handle) {
u64 thread_id{};
const ResultCode result{GetThreadId(system, &thread_id, thread_handle)};
static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low,
u32* out_thread_id_high, Handle thread_handle) {
u64 out_thread_id{};
const ResultCode result{GetThreadId(system, &out_thread_id, thread_handle)};
*thread_id_low = static_cast<u32>(thread_id >> 32);
*thread_id_high = static_cast<u32>(thread_id & std::numeric_limits<u32>::max());
*out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
*out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
return result;
}
@@ -395,7 +395,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
return RESULT_SUCCESS;
}
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle);
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
if (thread) {
const Process* const owner_process = thread->GetOwnerProcess();
if (!owner_process) {
@@ -473,15 +473,13 @@ static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u
static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
// Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
return ERR_INVALID_HANDLE;
}
std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
R_UNLESS(thread, Svc::ResultInvalidHandle);
thread->CancelWait();
// Cancel the thread's wait.
thread->WaitCancel();
return RESULT_SUCCESS;
}
@@ -630,7 +628,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
handle_debug_buffer(info1, info2);
auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
const auto thread_processor_id = current_thread->GetProcessorID();
const auto thread_processor_id = current_thread->GetActiveCore();
system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
}
}
@@ -872,7 +870,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
return ERR_INVALID_COMBINATION;
}
const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<Thread>(
const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<KThread>(
static_cast<Handle>(handle));
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
@@ -888,7 +886,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
u64 out_ticks = 0;
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
const u64 thread_ticks = current_thread->GetCpuTime();
out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
@@ -1025,129 +1023,109 @@ static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size
return UnmapPhysicalMemory(system, addr, size);
}
/// Sets the thread activity
static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
if (activity > static_cast<u32>(ThreadActivity::Paused)) {
return ERR_INVALID_ENUM_VALUE;
constexpr bool IsValidThreadActivity(Svc::ThreadActivity thread_activity) {
switch (thread_activity) {
case Svc::ThreadActivity::Runnable:
case Svc::ThreadActivity::Paused:
return true;
default:
return false;
}
const auto* current_process = system.Kernel().CurrentProcess();
const std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
if (thread->GetOwnerProcess() != current_process) {
LOG_ERROR(Kernel_SVC,
"The current process does not own the current thread, thread_handle={:08X} "
"thread_pid={}, "
"current_process_pid={}",
handle, thread->GetOwnerProcess()->GetProcessID(),
current_process->GetProcessID());
return ERR_INVALID_HANDLE;
}
if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) {
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
return ERR_BUSY;
}
return thread->SetActivity(static_cast<ThreadActivity>(activity));
}
static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) {
return SetThreadActivity(system, handle, activity);
/// Sets the thread activity
static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle,
Svc::ThreadActivity thread_activity) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
thread_activity);
// Validate the activity.
R_UNLESS(IsValidThreadActivity(thread_activity), Svc::ResultInvalidEnumValue);
// Get the thread from its handle.
auto& kernel = system.Kernel();
const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
R_UNLESS(thread, Svc::ResultInvalidHandle);
// Check that the activity is being set on a non-current thread for the current process.
R_UNLESS(thread->GetOwnerProcess() == kernel.CurrentProcess(), Svc::ResultInvalidHandle);
R_UNLESS(thread.get() != GetCurrentThreadPointer(kernel), Svc::ResultBusy);
// Set the activity.
R_TRY(thread->SetActivity(thread_activity));
return RESULT_SUCCESS;
}
static ResultCode SetThreadActivity32(Core::System& system, Handle thread_handle,
Svc::ThreadActivity thread_activity) {
return SetThreadActivity(system, thread_handle, thread_activity);
}
/// Gets the thread context
static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
thread_handle);
// Get the thread from its handle.
const auto* current_process = system.Kernel().CurrentProcess();
const std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
const std::shared_ptr<KThread> thread =
current_process->GetHandleTable().Get<KThread>(thread_handle);
R_UNLESS(thread, Svc::ResultInvalidHandle);
if (thread->GetOwnerProcess() != current_process) {
LOG_ERROR(Kernel_SVC,
"The current process does not own the current thread, thread_handle={:08X} "
"thread_pid={}, "
"current_process_pid={}",
handle, thread->GetOwnerProcess()->GetProcessID(),
current_process->GetProcessID());
return ERR_INVALID_HANDLE;
}
// Require the handle be to a non-current thread in the current process.
R_UNLESS(thread->GetOwnerProcess() == current_process, Svc::ResultInvalidHandle);
R_UNLESS(thread.get() != system.Kernel().CurrentScheduler()->GetCurrentThread(),
Svc::ResultBusy);
if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) {
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
return ERR_BUSY;
}
// Get the thread context.
std::vector<u8> context;
R_TRY(thread->GetThreadContext3(context));
Core::ARM_Interface::ThreadContext64 ctx = thread->GetContext64();
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
ctx.pstate &= 0xFF0FFE20;
// Copy the thread context to user space.
system.Memory().WriteBlock(out_context, context.data(), context.size());
// If 64-bit, we can just write the context registers directly and we're good.
// However, if 32-bit, we have to ensure some registers are zeroed out.
if (!current_process->Is64BitProcess()) {
std::fill(ctx.cpu_registers.begin() + 15, ctx.cpu_registers.end(), 0);
std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{});
}
system.Memory().WriteBlock(thread_context, &ctx, sizeof(ctx));
return RESULT_SUCCESS;
}
static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) {
return GetThreadContext(system, thread_context, handle);
static ResultCode GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
return GetThreadContext(system, out_context, thread_handle);
}
/// Gets the priority for the specified thread
static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) {
static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
LOG_TRACE(Kernel_SVC, "called");
// Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle);
if (!thread) {
*priority = 0;
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
R_UNLESS(thread, Svc::ResultInvalidHandle);
*priority = thread->GetPriority();
// Get the thread's priority.
*out_priority = thread->GetPriority();
return RESULT_SUCCESS;
}
static ResultCode GetThreadPriority32(Core::System& system, u32* priority, Handle handle) {
return GetThreadPriority(system, priority, handle);
static ResultCode GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
return GetThreadPriority(system, out_priority, handle);
}
/// Sets the priority for the specified thread
static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) {
LOG_TRACE(Kernel_SVC, "called");
if (priority > THREADPRIO_LOWEST) {
LOG_ERROR(
Kernel_SVC,
"An invalid priority was specified, expected {} but got {} for thread_handle={:08X}",
THREADPRIO_LOWEST, priority, handle);
return ERR_INVALID_THREAD_PRIORITY;
}
// Validate the priority.
R_UNLESS(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority,
Svc::ResultInvalidPriority);
const auto* const current_process = system.Kernel().CurrentProcess();
std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
// Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
R_UNLESS(thread, Svc::ResultInvalidHandle);
// Set the thread priority.
thread->SetBasePriority(priority);
return RESULT_SUCCESS;
}
@@ -1438,62 +1416,51 @@ static void ExitProcess(Core::System& system) {
current_process->PrepareForTermination();
// Kill the current thread
system.Kernel().CurrentScheduler()->GetCurrentThread()->Stop();
system.Kernel().CurrentScheduler()->GetCurrentThread()->Exit();
}
static void ExitProcess32(Core::System& system) {
ExitProcess(system);
}
static constexpr bool IsValidCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
}
/// Creates a new thread
static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
VAddr stack_top, u32 priority, s32 processor_id) {
VAddr stack_bottom, u32 priority, s32 core_id) {
LOG_DEBUG(Kernel_SVC,
"called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
entry_point, arg, stack_top, priority, processor_id, *out_handle);
auto* const current_process = system.Kernel().CurrentProcess();
if (processor_id == THREADPROCESSORID_IDEAL) {
// Set the target CPU to the one specified by the process.
processor_id = current_process->GetIdealCore();
ASSERT(processor_id != THREADPROCESSORID_IDEAL);
}
if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) {
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
return ERR_INVALID_PROCESSOR_ID;
}
const u64 core_mask = current_process->GetCoreMask();
if ((core_mask | (1ULL << processor_id)) != core_mask) {
LOG_ERROR(Kernel_SVC, "Invalid thread core specified ({})", processor_id);
return ERR_INVALID_PROCESSOR_ID;
}
if (priority > THREADPRIO_LOWEST) {
LOG_ERROR(Kernel_SVC,
"Invalid thread priority specified ({}). Must be within the range 0-64",
priority);
return ERR_INVALID_THREAD_PRIORITY;
}
if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority);
return ERR_INVALID_THREAD_PRIORITY;
}
"called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
"priority=0x{:08X}, core_id=0x{:08X}",
entry_point, arg, stack_bottom, priority, core_id);
// Adjust core id, if it's the default magic.
auto& kernel = system.Kernel();
auto& process = *kernel.CurrentProcess();
if (core_id == Svc::IdealCoreUseProcessValue) {
core_id = process.GetIdealCoreId();
}
ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1));
// Validate arguments.
R_UNLESS(IsValidCoreId(core_id), Svc::ResultInvalidCoreId);
R_UNLESS(((1ULL << core_id) & process.GetCoreMask()) != 0, Svc::ResultInvalidCoreId);
ThreadType type = THREADTYPE_USER;
CASCADE_RESULT(std::shared_ptr<Thread> thread,
Thread::Create(system, type, "", entry_point, priority, arg, processor_id,
stack_top, current_process));
R_UNLESS(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority,
Svc::ResultInvalidPriority);
R_UNLESS(process.CheckThreadPriority(priority), Svc::ResultInvalidPriority);
const auto new_thread_handle = current_process->GetHandleTable().Create(thread);
ASSERT(process.GetResourceLimit()->Reserve(
LimitableResource::Threads, 1, system.CoreTiming().GetGlobalTimeNs().count() + 100000000));
std::shared_ptr<KThread> thread;
{
KScopedLightLock lk{process.GetStateLock()};
CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority,
arg, core_id, stack_bottom, &process));
}
const auto new_thread_handle = process.GetHandleTable().Create(thread);
if (new_thread_handle.Failed()) {
LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}",
new_thread_handle.Code().raw);
@@ -1517,17 +1484,15 @@ static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 p
static ResultCode StartThread(Core::System& system, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
// Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
return ERR_INVALID_HANDLE;
}
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
R_UNLESS(thread, Svc::ResultInvalidHandle);
ASSERT(thread->GetState() == ThreadState::Initialized);
// Try to start the thread.
R_TRY(thread->Run());
return thread->Start();
return RESULT_SUCCESS;
}
static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
@@ -1540,7 +1505,7 @@ static void ExitThread(Core::System& system) {
auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
system.GlobalSchedulerContext().RemoveThread(SharedFrom(current_thread));
current_thread->Stop();
current_thread->Exit();
}
static void ExitThread32(Core::System& system) {
@@ -1549,34 +1514,28 @@ static void ExitThread32(Core::System& system) {
/// Sleep the current thread
static void SleepThread(Core::System& system, s64 nanoseconds) {
auto& kernel = system.Kernel();
const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
enum class SleepType : s64 {
YieldWithoutCoreMigration = 0,
YieldWithCoreMigration = -1,
YieldAndWaitForLoadBalancing = -2,
};
// When the input tick is positive, sleep.
if (nanoseconds > 0) {
// Convert the timeout from nanoseconds to ticks.
// NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
auto& scheduler = *system.Kernel().CurrentScheduler();
if (nanoseconds <= 0) {
switch (static_cast<SleepType>(nanoseconds)) {
case SleepType::YieldWithoutCoreMigration: {
scheduler.YieldWithoutCoreMigration();
break;
}
case SleepType::YieldWithCoreMigration: {
scheduler.YieldWithCoreMigration();
break;
}
case SleepType::YieldAndWaitForLoadBalancing: {
scheduler.YieldToAnyThread();
break;
}
default:
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
// Sleep.
// NOTE: Nintendo does not check the result of this sleep.
static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
} else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
KScheduler::YieldWithoutCoreMigration(kernel);
} else if (yield_type == Svc::YieldType::WithCoreMigration) {
KScheduler::YieldWithCoreMigration(kernel);
} else if (yield_type == Svc::YieldType::ToAnyThread) {
KScheduler::YieldToAnyThread(kernel);
} else {
scheduler.GetCurrentThread()->Sleep(nanoseconds);
// Nintendo does nothing at all if an otherwise invalid value is passed.
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
}
@@ -1839,95 +1798,72 @@ static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u
return CreateTransferMemory(system, handle, addr, size, permissions);
}
static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core,
u64* mask) {
static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
u64* out_affinity_mask) {
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
// Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
*core = 0;
*mask = 0;
return ERR_INVALID_HANDLE;
}
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
R_UNLESS(thread, Svc::ResultInvalidHandle);
*core = thread->GetIdealCore();
*mask = thread->GetAffinityMask().GetAffinityMask();
// Get the core mask.
R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
return RESULT_SUCCESS;
}
static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, u32* core,
u32* mask_low, u32* mask_high) {
u64 mask{};
const auto result = GetThreadCoreMask(system, thread_handle, core, &mask);
*mask_high = static_cast<u32>(mask >> 32);
*mask_low = static_cast<u32>(mask);
static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
u64 out_affinity_mask{};
const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
*out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
*out_affinity_mask_low = static_cast<u32>(out_affinity_mask);
return result;
}
static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
u64 affinity_mask) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}",
thread_handle, core, affinity_mask);
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core_id=0x{:X}, affinity_mask=0x{:016X}",
thread_handle, core_id, affinity_mask);
const auto* const current_process = system.Kernel().CurrentProcess();
const auto& current_process = *system.Kernel().CurrentProcess();
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
const u8 ideal_cpu_core = current_process->GetIdealCore();
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
// Set the target CPU to the ideal core specified by the process.
core = ideal_cpu_core;
affinity_mask = 1ULL << core;
// Determine the core id/affinity mask.
if (core_id == Svc::IdealCoreUseProcessValue) {
core_id = current_process.GetIdealCoreId();
affinity_mask = (1ULL << core_id);
} else {
const u64 core_mask = current_process->GetCoreMask();
// Validate the affinity mask.
const u64 process_core_mask = current_process.GetCoreMask();
R_UNLESS((affinity_mask | process_core_mask) == process_core_mask,
Svc::ResultInvalidCoreId);
R_UNLESS(affinity_mask != 0, Svc::ResultInvalidCombination);
if ((core_mask | affinity_mask) != core_mask) {
LOG_ERROR(
Kernel_SVC,
"Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})",
core_mask, affinity_mask);
return ERR_INVALID_PROCESSOR_ID;
}
if (affinity_mask == 0) {
LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero.");
return ERR_INVALID_COMBINATION;
}
if (core < Core::Hardware::NUM_CPU_CORES) {
if ((affinity_mask & (1ULL << core)) == 0) {
LOG_ERROR(Kernel_SVC,
"Core is not enabled for the current mask, core={}, mask={:016X}", core,
affinity_mask);
return ERR_INVALID_COMBINATION;
}
} else if (core != static_cast<u32>(THREADPROCESSORID_DONT_CARE) &&
core != static_cast<u32>(THREADPROCESSORID_DONT_UPDATE)) {
LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core);
return ERR_INVALID_PROCESSOR_ID;
// Validate the core id.
if (IsValidCoreId(core_id)) {
R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, Svc::ResultInvalidCombination);
} else {
R_UNLESS(core_id == Svc::IdealCoreNoUpdate || core_id == Svc::IdealCoreDontCare,
Svc::ResultInvalidCoreId);
}
}
const auto& handle_table = current_process->GetHandleTable();
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
return ERR_INVALID_HANDLE;
}
// Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
R_UNLESS(thread, Svc::ResultInvalidHandle);
return thread->SetCoreAndAffinityMask(core, affinity_mask);
// Set the core mask.
R_TRY(thread->SetCoreMask(core_id, affinity_mask));
return RESULT_SUCCESS;
}
static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core,
static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
u32 affinity_mask_low, u32 affinity_mask_high) {
const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
return SetThreadCoreMask(system, thread_handle, core, affinity_mask);
return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask);
}
static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) {
@@ -2037,7 +1973,7 @@ static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle)
LOG_DEBUG(Kernel_SVC, "called");
auto& kernel = system.Kernel();
auto resource_limit = ResourceLimit::Create(kernel);
auto resource_limit = std::make_shared<KResourceLimit>(kernel, system);
auto* const current_process = kernel.CurrentProcess();
ASSERT(current_process != nullptr);
@@ -2084,7 +2020,7 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour
LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit,
resource_type, value);
const auto type = static_cast<ResourceType>(resource_type);
const auto type = static_cast<LimitableResource>(resource_type);
if (!IsValidResourceType(type)) {
LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
return ERR_INVALID_ENUM_VALUE;
@@ -2094,7 +2030,7 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour
ASSERT(current_process != nullptr);
auto resource_limit_object =
current_process->GetHandleTable().Get<ResourceLimit>(resource_limit);
current_process->GetHandleTable().Get<KResourceLimit>(resource_limit);
if (!resource_limit_object) {
LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
resource_limit);
@@ -2106,8 +2042,8 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour
LOG_ERROR(
Kernel_SVC,
"Attempted to lower resource limit ({}) for category '{}' below its current value ({})",
resource_limit_object->GetMaxResourceValue(type), resource_type,
resource_limit_object->GetCurrentResourceValue(type));
resource_limit_object->GetLimitValue(type), resource_type,
resource_limit_object->GetCurrentValue(type));
return set_result;
}
@@ -2491,7 +2427,7 @@ void Call(Core::System& system, u32 immediate) {
kernel.EnterSVCProfile();
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
thread->SetContinuousOnSVC(true);
thread->SetIsCallingSvc();
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
: GetSVCInfo32(immediate);
@@ -2507,7 +2443,7 @@ void Call(Core::System& system, u32 immediate) {
kernel.ExitSVCProfile();
if (!thread->IsContinuousOnSVC()) {
if (!thread->IsCallingSvc()) {
auto* host_context = thread->GetHostContext().get();
host_context->Rewind();
}

View File

@@ -8,13 +8,18 @@
namespace Kernel::Svc {
constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59};
constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102};
constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106};
constexpr ResultCode ResultInvalidPriority{ErrorModule::Kernel, 112};
constexpr ResultCode ResultInvalidCoreId{ErrorModule::Kernel, 113};
constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114};
constexpr ResultCode ResultInvalidCombination{ErrorModule::Kernel, 116};
constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117};
constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118};
constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120};
constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122};
constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125};
} // namespace Kernel::Svc

View File

@@ -77,4 +77,22 @@ enum class ArbitrationType : u32 {
WaitIfEqual = 2,
};
enum class YieldType : s64 {
WithoutCoreMigration = 0,
WithCoreMigration = -1,
ToAnyThread = -2,
};
enum class ThreadActivity : u32 {
Runnable = 0,
Paused = 1,
};
constexpr inline s32 IdealCoreDontCare = -1;
constexpr inline s32 IdealCoreUseProcessValue = -2;
constexpr inline s32 IdealCoreNoUpdate = -3;
constexpr inline s32 LowestThreadPriority = 63;
constexpr inline s32 HighestThreadPriority = 0;
} // namespace Kernel::Svc

View File

@@ -58,6 +58,14 @@ void SvcWrap64(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
}
// Used by SetThreadActivity
template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
static_cast<Svc::ThreadActivity>(Param(system, 1)))
.raw);
}
template <ResultCode func(Core::System&, u32, u64, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
@@ -158,9 +166,18 @@ void SvcWrap64(Core::System& system) {
.raw);
}
template <ResultCode func(Core::System&, u32, u32*, u64*)>
// Used by SetThreadCoreMask
template <ResultCode func(Core::System&, Handle, s32, u64)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
static_cast<s32>(Param(system, 1)), Param(system, 2))
.raw);
}
// Used by GetThreadCoreMask
template <ResultCode func(Core::System&, Handle, s32*, u64*)>
void SvcWrap64(Core::System& system) {
s32 param_1 = 0;
u64 param_2 = 0;
const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
@@ -473,12 +490,35 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval);
}
// Used by GetThreadCoreMask32
template <ResultCode func(Core::System&, Handle, s32*, u32*, u32*)>
void SvcWrap32(Core::System& system) {
s32 param_1 = 0;
u32 param_2 = 0;
u32 param_3 = 0;
const u32 retval = func(system, Param32(system, 2), &param_1, &param_2, &param_3).raw;
system.CurrentArmInterface().SetReg(1, param_1);
system.CurrentArmInterface().SetReg(2, param_2);
system.CurrentArmInterface().SetReg(3, param_3);
FuncReturn(system, retval);
}
// Used by SignalProcessWideKey32
template <void func(Core::System&, u32, s32)>
void SvcWrap32(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1)));
}
// Used by SetThreadActivity32
template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)>
void SvcWrap32(Core::System& system) {
const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
static_cast<Svc::ThreadActivity>(Param(system, 1)))
.raw;
FuncReturn(system, retval);
}
// Used by SetThreadPriority32
template <ResultCode func(Core::System&, Handle, u32)>
void SvcWrap32(Core::System& system) {
@@ -487,7 +527,7 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval);
}
// Used by SetThreadCoreMask32
// Used by SetMemoryAttribute32
template <ResultCode func(Core::System&, Handle, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval =
@@ -497,6 +537,16 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval);
}
// Used by SetThreadCoreMask32
template <ResultCode func(Core::System&, Handle, s32, u32, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, static_cast<Handle>(Param(system, 0)), static_cast<s32>(Param(system, 1)),
static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
.raw;
FuncReturn(system, retval);
}
// Used by WaitProcessWideKeyAtomic32
template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)>
void SvcWrap32(Core::System& system) {

View File

@@ -1,460 +0,0 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include <optional>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/fiber.h"
#include "common/logging/log.h"
#include "common/thread_queue_list.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/result.h"
#include "core/memory.h"
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#endif
namespace Kernel {
bool Thread::IsSignaled() const {
return signaled;
}
Thread::Thread(KernelCore& kernel) : KSynchronizationObject{kernel} {}
Thread::~Thread() = default;
void Thread::Stop() {
{
KScopedSchedulerLock lock(kernel);
SetState(ThreadState::Terminated);
signaled = true;
NotifyAvailable();
kernel.GlobalHandleTable().Close(global_handle);
if (owner_process) {
owner_process->UnregisterThread(this);
// Mark the TLS slot in the thread's page as free.
owner_process->FreeTLSRegion(tls_address);
}
has_exited = true;
}
global_handle = 0;
}
void Thread::Wakeup() {
KScopedSchedulerLock lock(kernel);
SetState(ThreadState::Runnable);
}
ResultCode Thread::Start() {
KScopedSchedulerLock lock(kernel);
SetState(ThreadState::Runnable);
return RESULT_SUCCESS;
}
void Thread::CancelWait() {
KScopedSchedulerLock lock(kernel);
if (GetState() != ThreadState::Waiting || !is_cancellable) {
is_sync_cancelled = true;
return;
}
// TODO(Blinkhawk): Implement cancel of server session
is_sync_cancelled = false;
SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED);
SetState(ThreadState::Runnable);
}
static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top,
u32 entry_point, u32 arg) {
context = {};
context.cpu_registers[0] = arg;
context.cpu_registers[15] = entry_point;
context.cpu_registers[13] = stack_top;
}
static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top,
VAddr entry_point, u64 arg) {
context = {};
context.cpu_registers[0] = arg;
context.pc = entry_point;
context.sp = stack_top;
// TODO(merry): Perform a hardware test to determine the below value.
context.fpcr = 0;
}
std::shared_ptr<Common::Fiber>& Thread::GetHostContext() {
return host_context;
}
ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags,
std::string name, VAddr entry_point, u32 priority,
u64 arg, s32 processor_id, VAddr stack_top,
Process* owner_process) {
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
owner_process, std::move(init_func), init_func_parameter);
}
ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags,
std::string name, VAddr entry_point, u32 priority,
u64 arg, s32 processor_id, VAddr stack_top,
Process* owner_process,
std::function<void(void*)>&& thread_start_func,
void* thread_start_parameter) {
auto& kernel = system.Kernel();
// Check if priority is in ranged. Lowest priority -> highest priority id.
if (priority > THREADPRIO_LOWEST && ((type_flags & THREADTYPE_IDLE) == 0)) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
return ERR_INVALID_THREAD_PRIORITY;
}
if (processor_id > THREADPROCESSORID_MAX) {
LOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id);
return ERR_INVALID_PROCESSOR_ID;
}
if (owner_process) {
if (!system.Memory().IsValidVirtualAddress(*owner_process, entry_point)) {
LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
// TODO (bunnei): Find the correct error code to use here
return RESULT_UNKNOWN;
}
}
std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel);
thread->thread_id = kernel.CreateNewThreadID();
thread->thread_state = ThreadState::Initialized;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
thread->disable_count = 1;
thread->tpidr_el0 = 0;
thread->current_priority = priority;
thread->base_priority = priority;
thread->lock_owner = nullptr;
thread->schedule_count = -1;
thread->last_scheduled_tick = 0;
thread->processor_id = processor_id;
thread->ideal_core = processor_id;
thread->affinity_mask.SetAffinity(processor_id, true);
thread->name = std::move(name);
thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap();
thread->owner_process = owner_process;
thread->type = type_flags;
thread->signaled = false;
if ((type_flags & THREADTYPE_IDLE) == 0) {
auto& scheduler = kernel.GlobalSchedulerContext();
scheduler.AddThread(thread);
}
if (owner_process) {
thread->tls_address = thread->owner_process->CreateTLSRegion();
thread->owner_process->RegisterThread(thread.get());
} else {
thread->tls_address = 0;
}
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
if ((type_flags & THREADTYPE_HLE) == 0) {
ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
static_cast<u32>(entry_point), static_cast<u32>(arg));
ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
}
thread->host_context =
std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
}
void Thread::SetBasePriority(u32 priority) {
ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
"Invalid priority value.");
KScopedSchedulerLock lock(kernel);
// Change our base priority.
base_priority = priority;
// Perform a priority restoration.
RestorePriority(kernel, this);
}
void Thread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) {
signaling_object = object;
signaling_result = result;
}
VAddr Thread::GetCommandBufferAddress() const {
// Offset from the start of TLS at which the IPC command buffer begins.
constexpr u64 command_header_offset = 0x80;
return GetTLSAddress() + command_header_offset;
}
void Thread::SetState(ThreadState state) {
KScopedSchedulerLock sl(kernel);
// Clear debugging state
SetMutexWaitAddressForDebugging({});
SetWaitReasonForDebugging({});
const ThreadState old_state = thread_state;
thread_state =
static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask));
if (thread_state != old_state) {
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
}
void Thread::AddWaiterImpl(Thread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Find the right spot to insert the waiter.
auto it = waiter_list.begin();
while (it != waiter_list.end()) {
if (it->GetPriority() > thread->GetPriority()) {
break;
}
it++;
}
// Keep track of how many kernel waiters we have.
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters++) >= 0);
}
// Insert the waiter.
waiter_list.insert(it, *thread);
thread->SetLockOwner(this);
}
void Thread::RemoveWaiterImpl(Thread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Keep track of how many kernel waiters we have.
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters--) > 0);
}
// Remove the waiter.
waiter_list.erase(waiter_list.iterator_to(*thread));
thread->SetLockOwner(nullptr);
}
void Thread::RestorePriority(KernelCore& kernel, Thread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
while (true) {
// We want to inherit priority where possible.
s32 new_priority = thread->GetBasePriority();
if (thread->HasWaiters()) {
new_priority = std::min(new_priority, thread->waiter_list.front().GetPriority());
}
// If the priority we would inherit is not different from ours, don't do anything.
if (new_priority == thread->GetPriority()) {
return;
}
// Ensure we don't violate condition variable red black tree invariants.
if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
BeforeUpdatePriority(kernel, cv_tree, thread);
}
// Change the priority.
const s32 old_priority = thread->GetPriority();
thread->SetPriority(new_priority);
// Restore the condition variable, if relevant.
if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
AfterUpdatePriority(kernel, cv_tree, thread);
}
// Update the scheduler.
KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority);
// Keep the lock owner up to date.
Thread* lock_owner = thread->GetLockOwner();
if (lock_owner == nullptr) {
return;
}
// Update the thread in the lock owner's sorted list, and continue inheriting.
lock_owner->RemoveWaiterImpl(thread);
lock_owner->AddWaiterImpl(thread);
thread = lock_owner;
}
}
void Thread::AddWaiter(Thread* thread) {
AddWaiterImpl(thread);
RestorePriority(kernel, this);
}
void Thread::RemoveWaiter(Thread* thread) {
RemoveWaiterImpl(thread);
RestorePriority(kernel, this);
}
Thread* Thread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
s32 num_waiters{};
Thread* next_lock_owner{};
auto it = waiter_list.begin();
while (it != waiter_list.end()) {
if (it->GetAddressKey() == key) {
Thread* thread = std::addressof(*it);
// Keep track of how many kernel waiters we have.
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters--) > 0);
}
it = waiter_list.erase(it);
// Update the next lock owner.
if (next_lock_owner == nullptr) {
next_lock_owner = thread;
next_lock_owner->SetLockOwner(nullptr);
} else {
next_lock_owner->AddWaiterImpl(thread);
}
num_waiters++;
} else {
it++;
}
}
// Do priority updates, if we have a next owner.
if (next_lock_owner) {
RestorePriority(kernel, this);
RestorePriority(kernel, next_lock_owner);
}
// Return output.
*out_num_waiters = num_waiters;
return next_lock_owner;
}
ResultCode Thread::SetActivity(ThreadActivity value) {
KScopedSchedulerLock lock(kernel);
auto sched_status = GetState();
if (sched_status != ThreadState::Runnable && sched_status != ThreadState::Waiting) {
return ERR_INVALID_STATE;
}
if (IsTerminationRequested()) {
return RESULT_SUCCESS;
}
if (value == ThreadActivity::Paused) {
if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) != 0) {
return ERR_INVALID_STATE;
}
AddSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag);
} else {
if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) == 0) {
return ERR_INVALID_STATE;
}
RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag);
}
return RESULT_SUCCESS;
}
ResultCode Thread::Sleep(s64 nanoseconds) {
Handle event_handle{};
{
KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds);
SetState(ThreadState::Waiting);
SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);
}
if (event_handle != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(event_handle);
}
return RESULT_SUCCESS;
}
void Thread::AddSchedulingFlag(ThreadSchedFlags flag) {
const auto old_state = GetRawState();
pausing_state |= static_cast<u32>(flag);
const auto base_scheduling = GetState();
thread_state = base_scheduling | static_cast<ThreadState>(pausing_state);
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) {
const auto old_state = GetRawState();
pausing_state &= ~static_cast<u32>(flag);
const auto base_scheduling = GetState();
thread_state = base_scheduling | static_cast<ThreadState>(pausing_state);
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
KScopedSchedulerLock lock(kernel);
const auto HighestSetCore = [](u64 mask, u32 max_cores) {
for (s32 core = static_cast<s32>(max_cores - 1); core >= 0; core--) {
if (((mask >> core) & 1) != 0) {
return core;
}
}
return -1;
};
const bool use_override = affinity_override_count != 0;
if (new_core == THREADPROCESSORID_DONT_UPDATE) {
new_core = use_override ? ideal_core_override : ideal_core;
if ((new_affinity_mask & (1ULL << new_core)) == 0) {
LOG_ERROR(Kernel, "New affinity mask is incorrect! new_core={}, new_affinity_mask={}",
new_core, new_affinity_mask);
return ERR_INVALID_COMBINATION;
}
}
if (use_override) {
ideal_core_override = new_core;
} else {
const auto old_affinity_mask = affinity_mask;
affinity_mask.SetAffinityMask(new_affinity_mask);
ideal_core = new_core;
if (old_affinity_mask.GetAffinityMask() != new_affinity_mask) {
const s32 old_core = processor_id;
if (processor_id >= 0 && !affinity_mask.GetAffinity(processor_id)) {
if (static_cast<s32>(ideal_core) < 0) {
processor_id = HighestSetCore(affinity_mask.GetAffinityMask(),
Core::Hardware::NUM_CPU_CORES);
} else {
processor_id = ideal_core;
}
}
KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_affinity_mask, old_core);
}
}
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

@@ -1,782 +0,0 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <functional>
#include <span>
#include <string>
#include <utility>
#include <vector>
#include <boost/intrusive/list.hpp>
#include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
#include "common/spin_lock.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/k_affinity_mask.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/result.h"
namespace Common {
class Fiber;
}
namespace Core {
class ARM_Interface;
class System;
} // namespace Core
namespace Kernel {
class GlobalSchedulerContext;
class KernelCore;
class Process;
class KScheduler;
enum ThreadPriority : u32 {
THREADPRIO_HIGHEST = 0, ///< Highest thread priority
THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration
THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
THREADPRIO_LOWEST = 63, ///< Lowest thread priority
THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities.
};
enum ThreadType : u32 {
THREADTYPE_USER = 0x1,
THREADTYPE_KERNEL = 0x2,
THREADTYPE_HLE = 0x4,
THREADTYPE_IDLE = 0x8,
THREADTYPE_SUSPEND = 0x10,
};
enum ThreadProcessorId : s32 {
/// Indicates that no particular processor core is preferred.
THREADPROCESSORID_DONT_CARE = -1,
/// Run thread on the ideal core specified by the process.
THREADPROCESSORID_IDEAL = -2,
/// Indicates that the preferred processor ID shouldn't be updated in
/// a core mask setting operation.
THREADPROCESSORID_DONT_UPDATE = -3,
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
/// Allowed CPU mask
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
(1 << THREADPROCESSORID_2) | (1 << THREADPROCESSORID_3)
};
enum class ThreadState : u16 {
Initialized = 0,
Waiting = 1,
Runnable = 2,
Terminated = 3,
SuspendShift = 4,
Mask = (1 << SuspendShift) - 1,
ProcessSuspended = (1 << (0 + SuspendShift)),
ThreadSuspended = (1 << (1 + SuspendShift)),
DebugSuspended = (1 << (2 + SuspendShift)),
BacktraceSuspended = (1 << (3 + SuspendShift)),
InitSuspended = (1 << (4 + SuspendShift)),
SuspendFlagMask = ((1 << 5) - 1) << SuspendShift,
};
DECLARE_ENUM_FLAG_OPERATORS(ThreadState);
enum class ThreadWakeupReason {
Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal.
Timeout // The thread was woken up due to a wait timeout.
};
enum class ThreadActivity : u32 {
Normal = 0,
Paused = 1,
};
enum class ThreadSchedFlags : u32 {
ProcessPauseFlag = 1 << 4,
ThreadPauseFlag = 1 << 5,
ProcessDebugPauseFlag = 1 << 6,
KernelInitPauseFlag = 1 << 8,
};
enum class ThreadWaitReasonForDebugging : u32 {
None, ///< Thread is not waiting
Sleep, ///< Thread is waiting due to a SleepThread SVC
IPC, ///< Thread is waiting for the reply from an IPC request
Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC
ConditionVar, ///< Thread is waiting due to a WaitProcessWideKey SVC
Arbitration, ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC
Suspended, ///< Thread is waiting due to process suspension
};
class Thread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> {
friend class KScheduler;
friend class Process;
public:
explicit Thread(KernelCore& kernel);
~Thread() override;
using MutexWaitingThreads = std::vector<std::shared_ptr<Thread>>;
using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
* @param arg User data to pass to the thread
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
* @param stack_top The address of the thread's stack top
* @param owner_process The parent process for the thread, if null, it's a kernel thread
* @return A shared pointer to the newly created thread
*/
static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags,
std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id,
VAddr stack_top, Process* owner_process);
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
* @param arg User data to pass to the thread
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
* @param stack_top The address of the thread's stack top
* @param owner_process The parent process for the thread, if null, it's a kernel thread
* @param thread_start_func The function where the host context will start.
* @param thread_start_parameter The parameter which will passed to host context on init
* @return A shared pointer to the newly created thread
*/
static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags,
std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id,
VAddr stack_top, Process* owner_process,
std::function<void(void*)>&& thread_start_func,
void* thread_start_parameter);
std::string GetName() const override {
return name;
}
void SetName(std::string new_name) {
name = std::move(new_name);
}
std::string GetTypeName() const override {
return "Thread";
}
static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
/**
* Gets the thread's current priority
* @return The current thread's priority
*/
[[nodiscard]] s32 GetPriority() const {
return current_priority;
}
/**
* Sets the thread's current priority.
* @param priority The new priority.
*/
void SetPriority(s32 priority) {
current_priority = priority;
}
/**
* Gets the thread's nominal priority.
* @return The current thread's nominal priority.
*/
[[nodiscard]] s32 GetBasePriority() const {
return base_priority;
}
/**
* Sets the thread's nominal priority.
* @param priority The new priority.
*/
void SetBasePriority(u32 priority);
/// Changes the core that the thread is running or scheduled to run on.
[[nodiscard]] ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
/**
* Gets the thread's thread ID
* @return The thread's ID
*/
[[nodiscard]] u64 GetThreadID() const {
return thread_id;
}
/// Resumes a thread from waiting
void Wakeup();
ResultCode Start();
virtual bool IsSignaled() const override;
/// Cancels a waiting operation that this thread may or may not be within.
///
/// When the thread is within a waiting state, this will set the thread's
/// waiting result to signal a canceled wait. The function will then resume
/// this thread.
///
void CancelWait();
void SetSynchronizationResults(KSynchronizationObject* object, ResultCode result);
void SetSyncedObject(KSynchronizationObject* object, ResultCode result) {
SetSynchronizationResults(object, result);
}
ResultCode GetWaitResult(KSynchronizationObject** out) const {
*out = signaling_object;
return signaling_result;
}
ResultCode GetSignalingResult() const {
return signaling_result;
}
/**
* Stops a thread, invalidating it from further use
*/
void Stop();
/*
* Returns the Thread Local Storage address of the current thread
* @returns VAddr of the thread's TLS
*/
VAddr GetTLSAddress() const {
return tls_address;
}
/*
* Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
* @returns The value of the TPIDR_EL0 register.
*/
u64 GetTPIDR_EL0() const {
return tpidr_el0;
}
/// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
void SetTPIDR_EL0(u64 value) {
tpidr_el0 = value;
}
/*
* Returns the address of the current thread's command buffer, located in the TLS.
* @returns VAddr of the thread's command buffer.
*/
VAddr GetCommandBufferAddress() const;
ThreadContext32& GetContext32() {
return context_32;
}
const ThreadContext32& GetContext32() const {
return context_32;
}
ThreadContext64& GetContext64() {
return context_64;
}
const ThreadContext64& GetContext64() const {
return context_64;
}
bool IsHLEThread() const {
return (type & THREADTYPE_HLE) != 0;
}
bool IsSuspendThread() const {
return (type & THREADTYPE_SUSPEND) != 0;
}
bool IsIdleThread() const {
return (type & THREADTYPE_IDLE) != 0;
}
bool WasRunning() const {
return was_running;
}
void SetWasRunning(bool value) {
was_running = value;
}
std::shared_ptr<Common::Fiber>& GetHostContext();
ThreadState GetState() const {
return thread_state & ThreadState::Mask;
}
ThreadState GetRawState() const {
return thread_state;
}
void SetState(ThreadState state);
s64 GetLastScheduledTick() const {
return last_scheduled_tick;
}
void SetLastScheduledTick(s64 tick) {
last_scheduled_tick = tick;
}
u64 GetTotalCPUTimeTicks() const {
return total_cpu_time_ticks;
}
void UpdateCPUTimeTicks(u64 ticks) {
total_cpu_time_ticks += ticks;
}
s32 GetProcessorID() const {
return processor_id;
}
s32 GetActiveCore() const {
return GetProcessorID();
}
void SetProcessorID(s32 new_core) {
processor_id = new_core;
}
void SetActiveCore(s32 new_core) {
processor_id = new_core;
}
Process* GetOwnerProcess() {
return owner_process;
}
const Process* GetOwnerProcess() const {
return owner_process;
}
const MutexWaitingThreads& GetMutexWaitingThreads() const {
return wait_mutex_threads;
}
Thread* GetLockOwner() const {
return lock_owner;
}
void SetLockOwner(Thread* owner) {
lock_owner = owner;
}
u32 GetIdealCore() const {
return ideal_core;
}
const KAffinityMask& GetAffinityMask() const {
return affinity_mask;
}
ResultCode SetActivity(ThreadActivity value);
/// Sleeps this thread for the given amount of nanoseconds.
ResultCode Sleep(s64 nanoseconds);
s64 GetYieldScheduleCount() const {
return schedule_count;
}
void SetYieldScheduleCount(s64 count) {
schedule_count = count;
}
bool IsRunning() const {
return is_running;
}
void SetIsRunning(bool value) {
is_running = value;
}
bool IsWaitCancelled() const {
return is_sync_cancelled;
}
void ClearWaitCancelled() {
is_sync_cancelled = false;
}
Handle GetGlobalHandle() const {
return global_handle;
}
bool IsCancellable() const {
return is_cancellable;
}
void SetCancellable() {
is_cancellable = true;
}
void ClearCancellable() {
is_cancellable = false;
}
bool IsTerminationRequested() const {
return will_be_terminated || GetRawState() == ThreadState::Terminated;
}
bool IsPaused() const {
return pausing_state != 0;
}
bool IsContinuousOnSVC() const {
return is_continuous_on_svc;
}
void SetContinuousOnSVC(bool is_continuous) {
is_continuous_on_svc = is_continuous;
}
bool IsPhantomMode() const {
return is_phantom_mode;
}
void SetPhantomMode(bool phantom) {
is_phantom_mode = phantom;
}
bool HasExited() const {
return has_exited;
}
class QueueEntry {
public:
constexpr QueueEntry() = default;
constexpr void Initialize() {
prev = nullptr;
next = nullptr;
}
constexpr Thread* GetPrev() const {
return prev;
}
constexpr Thread* GetNext() const {
return next;
}
constexpr void SetPrev(Thread* thread) {
prev = thread;
}
constexpr void SetNext(Thread* thread) {
next = thread;
}
private:
Thread* prev{};
Thread* next{};
};
QueueEntry& GetPriorityQueueEntry(s32 core) {
return per_core_priority_queue_entry[core];
}
const QueueEntry& GetPriorityQueueEntry(s32 core) const {
return per_core_priority_queue_entry[core];
}
s32 GetDisableDispatchCount() const {
return disable_count;
}
void DisableDispatch() {
ASSERT(GetDisableDispatchCount() >= 0);
disable_count++;
}
void EnableDispatch() {
ASSERT(GetDisableDispatchCount() > 0);
disable_count--;
}
void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) {
wait_reason_for_debugging = reason;
}
[[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const {
return wait_reason_for_debugging;
}
void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) {
wait_objects_for_debugging.clear();
wait_objects_for_debugging.reserve(objects.size());
for (const auto& object : objects) {
wait_objects_for_debugging.emplace_back(object);
}
}
[[nodiscard]] const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const {
return wait_objects_for_debugging;
}
void SetMutexWaitAddressForDebugging(VAddr address) {
mutex_wait_address_for_debugging = address;
}
[[nodiscard]] VAddr GetMutexWaitAddressForDebugging() const {
return mutex_wait_address_for_debugging;
}
void AddWaiter(Thread* thread);
void RemoveWaiter(Thread* thread);
[[nodiscard]] Thread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key);
[[nodiscard]] VAddr GetAddressKey() const {
return address_key;
}
[[nodiscard]] u32 GetAddressKeyValue() const {
return address_key_value;
}
void SetAddressKey(VAddr key) {
address_key = key;
}
void SetAddressKey(VAddr key, u32 val) {
address_key = key;
address_key_value = val;
}
private:
static constexpr size_t PriorityInheritanceCountMax = 10;
union SyncObjectBuffer {
std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
std::array<Handle,
Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))>
handles;
constexpr SyncObjectBuffer() {}
};
static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
struct ConditionVariableComparator {
struct LightCompareType {
u64 cv_key{};
s32 priority{};
[[nodiscard]] constexpr u64 GetConditionVariableKey() const {
return cv_key;
}
[[nodiscard]] constexpr s32 GetPriority() const {
return priority;
}
};
template <typename T>
requires(
std::same_as<T, Thread> ||
std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
const Thread& rhs) {
const uintptr_t l_key = lhs.GetConditionVariableKey();
const uintptr_t r_key = rhs.GetConditionVariableKey();
if (l_key < r_key) {
// Sort first by key
return -1;
} else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) {
// And then by priority.
return -1;
} else {
return 1;
}
}
};
Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{};
using ConditionVariableThreadTreeTraits =
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&Thread::condvar_arbiter_tree_node>;
using ConditionVariableThreadTree =
ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>;
public:
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
[[nodiscard]] uintptr_t GetConditionVariableKey() const {
return condvar_key;
}
[[nodiscard]] uintptr_t GetAddressArbiterKey() const {
return condvar_key;
}
void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, uintptr_t cv_key,
u32 value) {
condvar_tree = tree;
condvar_key = cv_key;
address_key = address;
address_key_value = value;
}
void ClearConditionVariable() {
condvar_tree = nullptr;
}
[[nodiscard]] bool IsWaitingForConditionVariable() const {
return condvar_tree != nullptr;
}
void SetAddressArbiter(ConditionVariableThreadTree* tree, uintptr_t address) {
condvar_tree = tree;
condvar_key = address;
}
void ClearAddressArbiter() {
condvar_tree = nullptr;
}
[[nodiscard]] bool IsWaitingForAddressArbiter() const {
return condvar_tree != nullptr;
}
[[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const {
return condvar_tree;
}
[[nodiscard]] bool HasWaiters() const {
return !waiter_list.empty();
}
private:
void AddSchedulingFlag(ThreadSchedFlags flag);
void RemoveSchedulingFlag(ThreadSchedFlags flag);
void AddWaiterImpl(Thread* thread);
void RemoveWaiterImpl(Thread* thread);
static void RestorePriority(KernelCore& kernel, Thread* thread);
Common::SpinLock context_guard{};
ThreadContext32 context_32{};
ThreadContext64 context_64{};
std::shared_ptr<Common::Fiber> host_context{};
ThreadState thread_state = ThreadState::Initialized;
u64 thread_id = 0;
VAddr entry_point = 0;
VAddr stack_top = 0;
std::atomic_int disable_count = 0;
ThreadType type;
/// Nominal thread priority, as set by the emulated application.
/// The nominal priority is the thread priority without priority
/// inheritance taken into account.
s32 base_priority{};
/// Current thread priority. This may change over the course of the
/// thread's lifetime in order to facilitate priority inheritance.
s32 current_priority{};
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
s64 schedule_count{};
s64 last_scheduled_tick{};
s32 processor_id = 0;
VAddr tls_address = 0; ///< Virtual address of the Thread Local Storage of the thread
u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register.
/// Process that owns this thread
Process* owner_process;
/// Objects that the thread is waiting on, in the same order as they were
/// passed to WaitSynchronization. This is used for debugging only.
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
/// The current mutex wait address. This is used for debugging only.
VAddr mutex_wait_address_for_debugging{};
/// The reason the thread is waiting. This is used for debugging only.
ThreadWaitReasonForDebugging wait_reason_for_debugging{};
KSynchronizationObject* signaling_object;
ResultCode signaling_result{RESULT_SUCCESS};
/// List of threads that are waiting for a mutex that is held by this thread.
MutexWaitingThreads wait_mutex_threads;
/// Thread that owns the lock that this thread is waiting for.
Thread* lock_owner{};
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
Handle global_handle = 0;
KScheduler* scheduler = nullptr;
std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
u32 ideal_core{0xFFFFFFFF};
KAffinityMask affinity_mask{};
s32 ideal_core_override = -1;
u32 affinity_override_count = 0;
u32 pausing_state = 0;
bool is_running = false;
bool is_cancellable = false;
bool is_sync_cancelled = false;
bool is_continuous_on_svc = false;
bool will_be_terminated = false;
bool is_phantom_mode = false;
bool has_exited = false;
bool was_running = false;
bool signaled{};
ConditionVariableThreadTree* condvar_tree{};
uintptr_t condvar_key{};
VAddr address_key{};
u32 address_key_value{};
s32 num_kernel_waiters{};
using WaiterList = boost::intrusive::list<Thread>;
WaiterList waiter_list{};
WaiterList pinned_waiter_list{};
std::string name;
};
} // namespace Kernel

View File

@@ -8,8 +8,8 @@
#include "core/core_timing_util.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
@@ -18,50 +18,30 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
time_manager_event_type = Core::Timing::CreateEvent(
"Kernel::TimeManagerCallback",
[this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
std::shared_ptr<Thread> thread;
std::shared_ptr<KThread> thread;
{
std::lock_guard lock{mutex};
const auto proper_handle = static_cast<Handle>(thread_handle);
if (cancelled_events[proper_handle]) {
return;
}
thread = system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
}
if (thread) {
// Thread can be null if process has exited
thread->Wakeup();
thread = SharedFrom<KThread>(reinterpret_cast<KThread*>(thread_handle));
}
thread->Wakeup();
});
}
void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) {
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
std::lock_guard lock{mutex};
event_handle = timetask->GetGlobalHandle();
if (nanoseconds > 0) {
ASSERT(timetask);
ASSERT(timetask->GetState() != ThreadState::Runnable);
ASSERT(thread);
ASSERT(thread->GetState() != ThreadState::Runnable);
system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},
time_manager_event_type, event_handle);
} else {
event_handle = InvalidHandle;
time_manager_event_type,
reinterpret_cast<uintptr_t>(thread));
}
cancelled_events[event_handle] = false;
}
void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
void TimeManager::UnscheduleTimeEvent(KThread* thread) {
std::lock_guard lock{mutex};
if (event_handle == InvalidHandle) {
return;
}
system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle);
cancelled_events[event_handle] = true;
}
void TimeManager::CancelTimeEvent(Thread* time_task) {
std::lock_guard lock{mutex};
const Handle event_handle = time_task->GetGlobalHandle();
UnscheduleTimeEvent(event_handle);
system.CoreTiming().UnscheduleEvent(time_manager_event_type,
reinterpret_cast<uintptr_t>(thread));
}
} // namespace Kernel

View File

@@ -20,7 +20,7 @@ struct EventType;
namespace Kernel {
class Thread;
class KThread;
/**
* The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp
@@ -31,18 +31,14 @@ public:
explicit TimeManager(Core::System& system);
/// Schedule a time event on `timetask` thread that will expire in 'nanoseconds'
/// returns a non-invalid handle in `event_handle` if correctly scheduled
void ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds);
void ScheduleTimeEvent(KThread* time_task, s64 nanoseconds);
/// Unschedule an existing time event
void UnscheduleTimeEvent(Handle event_handle);
void CancelTimeEvent(Thread* time_task);
void UnscheduleTimeEvent(KThread* thread);
private:
Core::System& system;
std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
std::unordered_map<Handle, bool> cancelled_events;
std::mutex mutex;
};

View File

@@ -72,6 +72,8 @@ public:
/// is closed.
ResultCode Reset();
void Finalize() override {}
private:
/// The base address for the memory managed by this instance.
VAddr base_address{};

View File

@@ -4,10 +4,10 @@
#include <algorithm>
#include "common/assert.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h"
namespace Kernel {
@@ -38,8 +38,4 @@ void WritableEvent::Clear() {
readable->Clear();
}
bool WritableEvent::IsSignaled() const {
return readable->IsSignaled();
}
} // namespace Kernel

View File

@@ -46,7 +46,8 @@ public:
void Signal();
void Clear();
bool IsSignaled() const;
void Finalize() override {}
private:
explicit WritableEvent(KernelCore& kernel);

View File

@@ -41,12 +41,18 @@ constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/
ProfileManager::ProfileManager() {
ParseUserSaveFile();
if (user_count == 0)
// Create an user if none are present
if (user_count == 0) {
CreateNewUser(UUID::Generate(), "yuzu");
}
auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1);
if (UserExistsIndex(current))
// If user index don't exist. Load the first user and change the active user
if (!UserExistsIndex(current)) {
current = 0;
Settings::values.current_user = 0;
}
OpenUser(*GetUser(current));
}

View File

@@ -635,7 +635,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
{50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
{51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
{52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
{53, nullptr, "BeginVrModeEx"},
{53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"},
{54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
@@ -732,6 +732,13 @@ void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx
rb.Push(RESULT_SUCCESS);
}
void ICommonStateGetter::BeginVrModeEx(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ICommonStateGetter::EndVrModeEx(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -856,7 +863,7 @@ public:
{25, nullptr, "Terminate"},
{30, &ILibraryAppletAccessor::GetResult, "GetResult"},
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
{60, nullptr, "PresetLibraryAppletGpuTimeSliceZero"},
{60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"},
{100, &ILibraryAppletAccessor::PushInData, "PushInData"},
{101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
{102, nullptr, "PushExtraStorage"},
@@ -900,6 +907,13 @@ private:
rb.Push(applet->GetStatus());
}
void PresetLibraryAppletGpuTimeSliceZero(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Start(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
@@ -1178,7 +1192,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
{65, nullptr, "IsGamePlayRecordingSupported"},
{65, &IApplicationFunctions::IsGamePlayRecordingSupported, "IsGamePlayRecordingSupported"},
{66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"},
{67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"},
{68, nullptr, "RequestFlushGamePlayingMovieForDebug"},
@@ -1199,7 +1213,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
{130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
{140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
{141, nullptr, "TryPopFromFriendInvitationStorageChannel"},
{141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"},
{150, nullptr, "GetNotificationStorageChannelEvent"},
{151, nullptr, "TryPopFromNotificationStorageChannel"},
{160, nullptr, "GetHealthWarningDisappearedSystemEvent"},
@@ -1466,6 +1480,16 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
rb.Push(*res_code);
}
void IApplicationFunctions::IsGamePlayRecordingSupported(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
constexpr bool gameplay_recording_supported = false;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(gameplay_recording_supported);
}
void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -1617,6 +1641,14 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERe
rb.PushCopyObjects(friend_invitation_storage_channel_event.readable);
}
void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(
Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NO_DATA_IN_CHANNEL);
}
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
Core::System& system) {
auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel());

View File

@@ -189,6 +189,7 @@ private:
void IsVrModeEnabled(Kernel::HLERequestContext& ctx);
void SetVrModeEnabled(Kernel::HLERequestContext& ctx);
void SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx);
void BeginVrModeEx(Kernel::HLERequestContext& ctx);
void EndVrModeEx(Kernel::HLERequestContext& ctx);
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
@@ -265,6 +266,7 @@ private:
void SetTerminateResult(Kernel::HLERequestContext& ctx);
void GetDisplayVersion(Kernel::HLERequestContext& ctx);
void GetDesiredLanguage(Kernel::HLERequestContext& ctx);
void IsGamePlayRecordingSupported(Kernel::HLERequestContext& ctx);
void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx);
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
void NotifyRunning(Kernel::HLERequestContext& ctx);
@@ -287,6 +289,7 @@ private:
void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
bool launch_popped_application_specific = false;
bool launch_popped_account_preselect = false;

View File

@@ -58,7 +58,7 @@ public:
{8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, nullptr, "GetAudioOutPlayedSampleCount"},
{11, nullptr, "FlushAudioOutBuffers"},
{11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
{12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
{13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
};
@@ -185,6 +185,14 @@ private:
rb.Push(static_cast<u32>(stream->GetQueueSize()));
}
void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(stream->Flush());
}
void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const float volume = rp.Pop<float>();

View File

@@ -946,6 +946,23 @@ void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
sixaxis_sensors_enabled = six_axis_status;
}
void Controller_NPad::SetSixAxisFusionParameters(f32 parameter1, f32 parameter2) {
sixaxis_fusion_parameter1 = parameter1;
sixaxis_fusion_parameter2 = parameter2;
}
std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters() {
return {
sixaxis_fusion_parameter1,
sixaxis_fusion_parameter2,
};
}
void Controller_NPad::ResetSixAxisFusionParameters() {
sixaxis_fusion_parameter1 = 0.0f;
sixaxis_fusion_parameter2 = 0.0f;
}
void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
const auto npad_index_1 = NPadIdToIndex(npad_id_1);
const auto npad_index_2 = NPadIdToIndex(npad_id_2);

View File

@@ -202,6 +202,9 @@ public:
GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
bool IsSixAxisSensorAtRest() const;
void SetSixAxisEnabled(bool six_axis_status);
void SetSixAxisFusionParameters(f32 parameter1, f32 parameter2);
std::pair<f32, f32> GetSixAxisFusionParameters();
void ResetSixAxisFusionParameters();
LedPattern GetLedPattern(u32 npad_id);
bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
@@ -458,6 +461,8 @@ private:
std::array<bool, 10> unintended_home_button_input_protection{};
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
bool sixaxis_sensors_enabled{true};
f32 sixaxis_fusion_parameter1{};
f32 sixaxis_fusion_parameter2{};
bool sixaxis_at_rest{true};
std::array<ControllerPad, 10> npad_pad_states{};
bool is_in_lr_assignment_mode{false};

View File

@@ -126,14 +126,23 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
}
// If ns_late is higher than the update rate ignore the delay
if (ns_late > motion_update_ns) {
ns_late = {};
}
core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
}
void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
for (const auto& controller : controllers) {
controller->OnMotionUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(
core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
// If ns_late is higher than the update rate ignore the delay
if (ns_late > motion_update_ns) {
ns_late = {};
}
core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);
@@ -209,9 +218,9 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} {
{67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
{68, nullptr, "IsSixAxisSensorFusionEnabled"},
{69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
{70, nullptr, "SetSixAxisSensorFusionParameters"},
{71, nullptr, "GetSixAxisSensorFusionParameters"},
{72, nullptr, "ResetSixAxisSensorFusionParameters"},
{70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
{71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
{72, &Hid::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"},
{73, nullptr, "SetAccelerometerParameters"},
{74, nullptr, "GetAccelerometerParameters"},
{75, nullptr, "ResetAccelerometerParameters"},
@@ -520,6 +529,7 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
Controller_NPad::DeviceHandle sixaxis_handle;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -534,6 +544,83 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle;
f32 parameter1;
f32 parameter2;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetSixAxisFusionParameters(parameters.parameter1, parameters.parameter2);
LOG_WARNING(Service_HID,
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
"parameter2={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.parameter1,
parameters.parameter2, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
f32 parameter1 = 0;
f32 parameter2 = 0;
const auto parameters{rp.PopRaw<Parameters>()};
std::tie(parameter1, parameter2) =
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.GetSixAxisFusionParameters();
LOG_WARNING(
Service_HID,
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(parameter1);
rb.Push(parameter2);
}
void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.ResetSixAxisFusionParameters();
LOG_WARNING(
Service_HID,
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};

View File

@@ -97,6 +97,9 @@ private:
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
void ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);

View File

@@ -20,30 +20,30 @@ public:
static const FunctionInfo functions[] = {
{0, nullptr, "SaveCurrentSetting"},
{1, nullptr, "LoadCurrentSetting"},
{2, nullptr, "SetCurrentBrightnessSetting"},
{3, nullptr, "GetCurrentBrightnessSetting"},
{2, &LBL::SetCurrentBrightnessSetting, "SetCurrentBrightnessSetting"},
{3, &LBL::GetCurrentBrightnessSetting, "GetCurrentBrightnessSetting"},
{4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"},
{5, nullptr, "GetBrightnessSettingAppliedToBacklight"},
{6, nullptr, "SwitchBacklightOn"},
{7, nullptr, "SwitchBacklightOff"},
{8, nullptr, "GetBacklightSwitchStatus"},
{9, nullptr, "EnableDimming"},
{10, nullptr, "DisableDimming"},
{11, nullptr, "IsDimmingEnabled"},
{12, nullptr, "EnableAutoBrightnessControl"},
{13, nullptr, "DisableAutoBrightnessControl"},
{14, nullptr, "IsAutoBrightnessControlEnabled"},
{15, nullptr, "SetAmbientLightSensorValue"},
{16, nullptr, "GetAmbientLightSensorValue"},
{17, nullptr, "SetBrightnessReflectionDelayLevel"},
{18, nullptr, "GetBrightnessReflectionDelayLevel"},
{19, nullptr, "SetCurrentBrightnessMapping"},
{20, nullptr, "GetCurrentBrightnessMapping"},
{21, nullptr, "SetCurrentAmbientLightSensorMapping"},
{22, nullptr, "GetCurrentAmbientLightSensorMapping"},
{23, nullptr, "IsAmbientLightSensorAvailable"},
{24, nullptr, "SetCurrentBrightnessSettingForVrMode"},
{25, nullptr, "GetCurrentBrightnessSettingForVrMode"},
{6, &LBL::SwitchBacklightOn, "SwitchBacklightOn"},
{7, &LBL::SwitchBacklightOff, "SwitchBacklightOff"},
{8, &LBL::GetBacklightSwitchStatus, "GetBacklightSwitchStatus"},
{9, &LBL::EnableDimming, "EnableDimming"},
{10, &LBL::DisableDimming, "DisableDimming"},
{11, &LBL::IsDimmingEnabled, "IsDimmingEnabled"},
{12, &LBL::EnableAutoBrightnessControl, "EnableAutoBrightnessControl"},
{13, &LBL::DisableAutoBrightnessControl, "DisableAutoBrightnessControl"},
{14, &LBL::IsAutoBrightnessControlEnabled, "IsAutoBrightnessControlEnabled"},
{15, &LBL::SetAmbientLightSensorValue, "SetAmbientLightSensorValue"},
{16, &LBL::GetAmbientLightSensorValue, "GetAmbientLightSensorValue"},
{17, &LBL::SetBrightnessReflectionDelayLevel, "SetBrightnessReflectionDelayLevel"},
{18, &LBL::GetBrightnessReflectionDelayLevel, "GetBrightnessReflectionDelayLevel"},
{19, &LBL::SetCurrentBrightnessMapping, "SetCurrentBrightnessMapping"},
{20, &LBL::GetCurrentBrightnessMapping, "GetCurrentBrightnessMapping"},
{21, &LBL::SetCurrentAmbientLightSensorMapping, "SetCurrentAmbientLightSensorMapping"},
{22, &LBL::GetCurrentAmbientLightSensorMapping, "GetCurrentAmbientLightSensorMapping"},
{23, &LBL::IsAmbientLightSensorAvailable, "IsAmbientLightSensorAvailable"},
{24, &LBL::SetCurrentBrightnessSettingForVrMode, "SetCurrentBrightnessSettingForVrMode"},
{25, &LBL::GetCurrentBrightnessSettingForVrMode, "GetCurrentBrightnessSettingForVrMode"},
{26, &LBL::EnableVrMode, "EnableVrMode"},
{27, &LBL::DisableVrMode, "DisableVrMode"},
{28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
@@ -55,6 +55,237 @@ public:
}
private:
enum class BacklightSwitchStatus : u32 {
Off = 0,
On = 1,
};
void SetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto brightness = rp.Pop<float>();
if (!std::isfinite(brightness)) {
LOG_ERROR(Service_LBL, "Brightness is infinite!");
brightness = 0.0f;
}
LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
current_brightness = brightness;
update_instantly = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto brightness = current_brightness;
if (!std::isfinite(brightness)) {
LOG_ERROR(Service_LBL, "Brightness is infinite!");
brightness = 0.0f;
}
LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(brightness);
}
void SwitchBacklightOn(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto fade_time = rp.Pop<u64_le>();
LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time);
backlight_enabled = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SwitchBacklightOff(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto fade_time = rp.Pop<u64_le>();
LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time);
backlight_enabled = false;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetBacklightSwitchStatus(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LBL, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushEnum<BacklightSwitchStatus>(backlight_enabled ? BacklightSwitchStatus::On
: BacklightSwitchStatus::Off);
}
void EnableDimming(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LBL, "called");
dimming = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void DisableDimming(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LBL, "called");
dimming = false;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void IsDimmingEnabled(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LBL, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(dimming);
}
void EnableAutoBrightnessControl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LBL, "called");
auto_brightness = true;
update_instantly = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void DisableAutoBrightnessControl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LBL, "called");
auto_brightness = false;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void IsAutoBrightnessControlEnabled(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LBL, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(auto_brightness);
}
void SetAmbientLightSensorValue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto light_value = rp.Pop<float>();
LOG_DEBUG(Service_LBL, "called light_value={}", light_value);
ambient_light_value = light_value;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetAmbientLightSensorValue(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LBL, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(ambient_light_value);
}
void SetBrightnessReflectionDelayLevel(Kernel::HLERequestContext& ctx) {
// This is Intentional, this function does absolutely nothing
LOG_DEBUG(Service_LBL, "called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetBrightnessReflectionDelayLevel(Kernel::HLERequestContext& ctx) {
// This is intentional, the function is hard coded to return 0.0f on hardware
LOG_DEBUG(Service_LBL, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(0.0f);
}
void SetCurrentBrightnessMapping(Kernel::HLERequestContext& ctx) {
// This is Intentional, this function does absolutely nothing
LOG_DEBUG(Service_LBL, "called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetCurrentBrightnessMapping(Kernel::HLERequestContext& ctx) {
// This is Intentional, this function does absolutely nothing
LOG_DEBUG(Service_LBL, "called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
// This function is suppose to return something but it seems like it doesn't
}
void SetCurrentAmbientLightSensorMapping(Kernel::HLERequestContext& ctx) {
// This is Intentional, this function does absolutely nothing
LOG_DEBUG(Service_LBL, "called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetCurrentAmbientLightSensorMapping(Kernel::HLERequestContext& ctx) {
// This is Intentional, this function does absolutely nothing
LOG_DEBUG(Service_LBL, "called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
// This function is suppose to return something but it seems like it doesn't
}
void IsAmbientLightSensorAvailable(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LBL, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
// TODO(ogniK): Only return true if there's no device error
rb.Push(true);
}
void SetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto brightness = rp.Pop<float>();
if (!std::isfinite(brightness)) {
LOG_ERROR(Service_LBL, "Brightness is infinite!");
brightness = 0.0f;
}
LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
current_vr_brightness = brightness;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto brightness = current_vr_brightness;
if (!std::isfinite(brightness)) {
LOG_ERROR(Service_LBL, "Brightness is infinite!");
brightness = 0.0f;
}
LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(brightness);
}
void EnableVrMode(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LBL, "called");
@@ -82,6 +313,14 @@ private:
}
bool vr_mode_enabled = false;
float current_brightness = 1.0f;
float backlight_brightness = 1.0f;
float ambient_light_value = 0.0f;
float current_vr_brightness = 1.0f;
bool dimming = true;
bool backlight_enabled = true;
bool update_instantly = false;
bool auto_brightness = false; // TODO(ogniK): Move to system settings
};
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {

View File

@@ -5,22 +5,71 @@
#include <sstream>
#include <string>
#include <optional>
#include <unordered_map>
#include <boost/container_hash/hash.hpp>
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/lm/lm.h"
#include "core/hle/service/lm/manager.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
namespace Service::LM {
enum class LogSeverity : u8 {
Trace = 0,
Info = 1,
Warning = 2,
Error = 3,
Fatal = 4,
};
// To keep flags out of hashing as well as the payload size
struct LogPacketHeaderEntry {
u64_le pid{};
u64_le tid{};
LogSeverity severity{};
u8 verbosity{};
auto operator<=>(const LogPacketHeaderEntry&) const = default;
};
} // namespace Service::LM
namespace std {
template <>
struct hash<Service::LM::LogPacketHeaderEntry> {
std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const noexcept {
std::size_t seed{};
boost::hash_combine(seed, k.pid);
boost::hash_combine(seed, k.tid);
boost::hash_combine(seed, k.severity);
boost::hash_combine(seed, k.verbosity);
return seed;
};
};
} // namespace std
namespace Service::LM {
enum class LogDestination : u32 {
TargetManager = 1 << 0,
Uart = 1 << 1,
UartSleep = 1 << 2,
All = 0xffff,
};
DECLARE_ENUM_FLAG_OPERATORS(LogDestination);
enum class LogPacketFlags : u8 {
Head = 1 << 0,
Tail = 1 << 1,
LittleEndian = 1 << 2,
};
DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags);
class ILogger final : public ServiceFramework<ILogger> {
public:
explicit ILogger(Core::System& system_)
: ServiceFramework{system_, "ILogger"}, manager{system_.GetLogManager()},
memory{system_.Memory()} {
explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} {
static const FunctionInfo functions[] = {
{0, &ILogger::Log, "Log"},
{1, &ILogger::SetDestination, "SetDestination"},
@@ -30,54 +79,262 @@ public:
private:
void Log(Kernel::HLERequestContext& ctx) {
std::size_t offset{};
const auto data = ctx.ReadBuffer();
// This function only succeeds - Get that out of the way
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
// Read MessageHeader, despite not doing anything with it right now
MessageHeader header{};
VAddr addr{ctx.BufferDescriptorX()[0].Address()};
const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size};
memory.ReadBlock(addr, &header, sizeof(MessageHeader));
addr += sizeof(MessageHeader);
FieldMap fields;
while (addr < end_addr) {
const auto field = static_cast<Field>(memory.Read8(addr++));
const auto length = memory.Read8(addr++);
if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) {
++addr;
}
SCOPE_EXIT({ addr += length; });
if (field == Field::Skip) {
continue;
}
std::vector<u8> data(length);
memory.ReadBlock(addr, data.data(), length);
fields.emplace(field, std::move(data));
if (data.size() < sizeof(LogPacketHeader)) {
LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size());
return;
}
manager.Log({header, std::move(fields)});
LogPacketHeader header{};
std::memcpy(&header, data.data(), sizeof(LogPacketHeader));
offset += sizeof(LogPacketHeader);
LogPacketHeaderEntry entry{
.pid = header.pid,
.tid = header.tid,
.severity = header.severity,
.verbosity = header.verbosity,
};
if (True(header.flags & LogPacketFlags::Head)) {
std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
std::memcpy(tmp.data(), data.data() + offset, tmp.size());
entries[entry] = std::move(tmp);
} else {
// Append to existing entry
if (!entries.contains(entry)) {
LOG_ERROR(Service_LM, "Log entry does not exist!");
return;
}
std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
auto& existing_entry = entries[entry];
const auto base = existing_entry.size();
existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader)));
std::memcpy(existing_entry.data() + base, data.data() + offset,
(data.size() - sizeof(LogPacketHeader)));
}
if (True(header.flags & LogPacketFlags::Tail)) {
auto it = entries.find(entry);
if (it == entries.end()) {
LOG_ERROR(Service_LM, "Log entry does not exist!");
return;
}
ParseLog(it->first, it->second);
entries.erase(it);
}
}
void SetDestination(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto destination = rp.PopEnum<DestinationFlag>();
const auto log_destination = rp.PopEnum<LogDestination>();
LOG_DEBUG(Service_LM, "called, destination={:08X}", destination);
manager.SetDestination(destination);
LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination));
destination = log_destination;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
Manager& manager;
Core::Memory::Memory& memory;
u32 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) {
u32 result{};
u32 shift{};
do {
result |= (data[offset] & 0x7f) << shift;
shift += 7;
offset++;
if (offset >= data.size()) {
break;
}
} while ((data[offset] & 0x80) != 0);
return result;
}
std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset,
std::size_t length) {
if (length == 0) {
return std::nullopt;
}
const auto length_to_read = std::min(length, data.size() - offset);
std::string output(length_to_read, '\0');
std::memcpy(output.data(), data.data() + offset, length_to_read);
offset += length_to_read;
return output;
}
u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
ASSERT(length == sizeof(u32));
u32_le output{};
std::memcpy(&output, data.data() + offset, sizeof(u32));
offset += length;
return output;
}
u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
ASSERT(length == sizeof(u64));
u64_le output{};
std::memcpy(&output, data.data() + offset, sizeof(u64));
offset += length;
return output;
}
void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) {
// Possible entries
std::optional<std::string> text_log;
std::optional<u32> line_number;
std::optional<std::string> file_name;
std::optional<std::string> function_name;
std::optional<std::string> module_name;
std::optional<std::string> thread_name;
std::optional<u64> log_pack_drop_count;
std::optional<s64> user_system_clock;
std::optional<std::string> process_name;
std::size_t offset{};
while (offset < log_data.size()) {
const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset));
const auto chunk_size = ReadLeb128(log_data, offset);
switch (key) {
case LogDataChunkKey::LogSessionBegin:
case LogDataChunkKey::LogSessionEnd:
break;
case LogDataChunkKey::TextLog:
text_log = ReadString(log_data, offset, chunk_size);
break;
case LogDataChunkKey::LineNumber:
line_number = ReadAsU32(log_data, offset, chunk_size);
break;
case LogDataChunkKey::FileName:
file_name = ReadString(log_data, offset, chunk_size);
break;
case LogDataChunkKey::FunctionName:
function_name = ReadString(log_data, offset, chunk_size);
break;
case LogDataChunkKey::ModuleName:
module_name = ReadString(log_data, offset, chunk_size);
break;
case LogDataChunkKey::ThreadName:
thread_name = ReadString(log_data, offset, chunk_size);
break;
case LogDataChunkKey::LogPacketDropCount:
log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size);
break;
case LogDataChunkKey::UserSystemClock:
user_system_clock = ReadAsU64(log_data, offset, chunk_size);
break;
case LogDataChunkKey::ProcessName:
process_name = ReadString(log_data, offset, chunk_size);
break;
}
}
std::string output_log{};
if (process_name) {
output_log += fmt::format("Process: {}\n", *process_name);
}
if (module_name) {
output_log += fmt::format("Module: {}\n", *module_name);
}
if (file_name) {
output_log += fmt::format("File: {}\n", *file_name);
}
if (function_name) {
output_log += fmt::format("Function: {}\n", *function_name);
}
if (line_number && *line_number != 0) {
output_log += fmt::format("Line: {}\n", *line_number);
}
output_log += fmt::format("ProcessID: {:X}\n", entry.pid);
output_log += fmt::format("ThreadID: {:X}\n", entry.tid);
if (text_log) {
output_log += fmt::format("Log Text: {}\n", *text_log);
}
switch (entry.severity) {
case LogSeverity::Trace:
LOG_DEBUG(Service_LM, "LogManager DEBUG ({}):\n{}", DestinationToString(destination),
output_log);
break;
case LogSeverity::Info:
LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination),
output_log);
break;
case LogSeverity::Warning:
LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}",
DestinationToString(destination), output_log);
break;
case LogSeverity::Error:
LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination),
output_log);
break;
case LogSeverity::Fatal:
LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination),
output_log);
break;
default:
LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}",
DestinationToString(destination), output_log);
break;
}
}
static std::string DestinationToString(LogDestination destination) {
if (True(destination & LogDestination::All)) {
return "TargetManager | Uart | UartSleep";
}
std::string output{};
if (True(destination & LogDestination::TargetManager)) {
output += "| TargetManager";
}
if (True(destination & LogDestination::Uart)) {
output += "| Uart";
}
if (True(destination & LogDestination::UartSleep)) {
output += "| UartSleep";
}
if (output.length() > 0) {
return output.substr(2);
}
return "No Destination";
}
enum class LogDataChunkKey : u32 {
LogSessionBegin = 0,
LogSessionEnd = 1,
TextLog = 2,
LineNumber = 3,
FileName = 4,
FunctionName = 5,
ModuleName = 6,
ThreadName = 7,
LogPacketDropCount = 8,
UserSystemClock = 9,
ProcessName = 10,
};
struct LogPacketHeader {
u64_le pid{};
u64_le tid{};
LogPacketFlags flags{};
INSERT_PADDING_BYTES(1);
LogSeverity severity{};
u8 verbosity{};
u32_le payload_size{};
};
static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size");
std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{};
LogDestination destination{LogDestination::All};
};
class LM final : public ServiceFramework<LM> {

View File

@@ -1,134 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/hle/service/lm/manager.h"
#include "core/reporter.h"
namespace Service::LM {
std::ostream& operator<<(std::ostream& os, DestinationFlag dest) {
std::vector<std::string> array;
const auto check_single_flag = [dest, &array](DestinationFlag check, std::string name) {
if ((static_cast<u32>(check) & static_cast<u32>(dest)) != 0) {
array.emplace_back(std::move(name));
}
};
check_single_flag(DestinationFlag::Default, "Default");
check_single_flag(DestinationFlag::UART, "UART");
check_single_flag(DestinationFlag::UARTSleeping, "UART (Sleeping)");
os << "[";
for (const auto& entry : array) {
os << entry << ", ";
}
return os << "]";
}
std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity) {
switch (severity) {
case MessageHeader::Severity::Trace:
return os << "Trace";
case MessageHeader::Severity::Info:
return os << "Info";
case MessageHeader::Severity::Warning:
return os << "Warning";
case MessageHeader::Severity::Error:
return os << "Error";
case MessageHeader::Severity::Critical:
return os << "Critical";
default:
return os << fmt::format("{:08X}", static_cast<u32>(severity));
}
}
std::ostream& operator<<(std::ostream& os, Field field) {
switch (field) {
case Field::Skip:
return os << "Skip";
case Field::Message:
return os << "Message";
case Field::Line:
return os << "Line";
case Field::Filename:
return os << "Filename";
case Field::Function:
return os << "Function";
case Field::Module:
return os << "Module";
case Field::Thread:
return os << "Thread";
default:
return os << fmt::format("{:08X}", static_cast<u32>(field));
}
}
std::string FormatField(Field type, const std::vector<u8>& data) {
switch (type) {
case Field::Skip:
return "";
case Field::Line:
if (data.size() >= sizeof(u32)) {
u32 line;
std::memcpy(&line, data.data(), sizeof(u32));
return fmt::format("{}", line);
}
return "[ERROR DECODING LINE NUMBER]";
case Field::Message:
case Field::Filename:
case Field::Function:
case Field::Module:
case Field::Thread:
return Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(data.data()), data.size());
default:
UNIMPLEMENTED_MSG("Unimplemented field type={}", type);
return "";
}
}
Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {}
Manager::~Manager() = default;
void Manager::SetEnabled(bool enabled) {
this->enabled = enabled;
}
void Manager::SetDestination(DestinationFlag destination) {
this->destination = destination;
}
void Manager::Log(LogMessage message) {
if (message.header.IsHeadLog()) {
InitializeLog();
}
current_log.emplace_back(std::move(message));
if (current_log.back().header.IsTailLog()) {
FinalizeLog();
}
}
void Manager::Flush() {
FinalizeLog();
}
void Manager::InitializeLog() {
current_log.clear();
LOG_INFO(Service_LM, "Initialized new log session");
}
void Manager::FinalizeLog() {
reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log));
LOG_INFO(Service_LM, "Finalized current log session");
}
} // namespace Service::LM

View File

@@ -1,106 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <ostream>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
namespace Core {
class Reporter;
}
namespace Service::LM {
enum class DestinationFlag : u32 {
Default = 1,
UART = 2,
UARTSleeping = 4,
All = 0xFFFF,
};
struct MessageHeader {
enum Flags : u32_le {
IsHead = 1,
IsTail = 2,
};
enum Severity : u32_le {
Trace,
Info,
Warning,
Error,
Critical,
};
u64_le pid;
u64_le thread_context;
union {
BitField<0, 16, Flags> flags;
BitField<16, 8, Severity> severity;
BitField<24, 8, u32> verbosity;
};
u32_le payload_size;
bool IsHeadLog() const {
return flags & IsHead;
}
bool IsTailLog() const {
return flags & IsTail;
}
};
static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
enum class Field : u8 {
Skip = 1,
Message = 2,
Line = 3,
Filename = 4,
Function = 5,
Module = 6,
Thread = 7,
};
std::ostream& operator<<(std::ostream& os, DestinationFlag dest);
std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity);
std::ostream& operator<<(std::ostream& os, Field field);
using FieldMap = std::map<Field, std::vector<u8>>;
struct LogMessage {
MessageHeader header;
FieldMap fields;
};
std::string FormatField(Field type, const std::vector<u8>& data);
class Manager {
public:
explicit Manager(Core::Reporter& reporter);
~Manager();
void SetEnabled(bool enabled);
void SetDestination(DestinationFlag destination);
void Log(LogMessage message);
void Flush();
private:
void InitializeLog();
void FinalizeLog();
bool enabled = true;
DestinationFlag destination = DestinationFlag::All;
std::vector<LogMessage> current_log;
Core::Reporter& reporter;
};
} // namespace Service::LM

View File

@@ -21,7 +21,7 @@ namespace {
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
constexpr std::size_t DefaultMiiCount{sizeof(RawData::DefaultMii) / sizeof(DefaultMii)};
constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'};
constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7};
@@ -141,13 +141,6 @@ T GetRandomValue(T max) {
return GetRandomValue<T>({}, max);
}
template <typename T>
T GetArrayValue(const u8* data, std::size_t index) {
T result{};
std::memcpy(&result, &data[index * sizeof(T)], sizeof(T));
return result;
}
MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) {
MiiStoreBitFields bf{};
@@ -193,32 +186,20 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo
const std::size_t index{3 * static_cast<std::size_t>(age) +
9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
const auto faceline_type_info{
GetArrayValue<RandomMiiData4>(&RawData::RandomMiiFaceline[0], index)};
const auto faceline_color_info{GetArrayValue<RandomMiiData3>(
RawData::RandomMiiFacelineColor.data(),
const auto faceline_type_info{RawData::RandomMiiFaceline.at(index)};
const auto faceline_color_info{RawData::RandomMiiFacelineColor.at(
3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))};
const auto faceline_wrinkle_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineWrinkle.data(), index)};
const auto faceline_makeup_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineMakeup.data(), index)};
const auto hair_type_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiHairType.data(), index)};
const auto hair_color_info{GetArrayValue<RandomMiiData3>(RawData::RandomMiiHairColor.data(),
3 * static_cast<std::size_t>(race) +
static_cast<std::size_t>(age))};
const auto eye_type_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyeType.data(), index)};
const auto eye_color_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiEyeColor.data(),
static_cast<std::size_t>(race))};
const auto eyebrow_type_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyebrowType.data(), index)};
const auto nose_type_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiNoseType.data(), index)};
const auto mouth_type_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiMouthType.data(), index)};
const auto glasses_type_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiGlassType.data(),
static_cast<std::size_t>(age))};
const auto faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)};
const auto faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)};
const auto hair_type_info{RawData::RandomMiiHairType.at(index)};
const auto hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) +
static_cast<std::size_t>(age))};
const auto eye_type_info{RawData::RandomMiiEyeType.at(index)};
const auto eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))};
const auto eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)};
const auto nose_type_info{RawData::RandomMiiNoseType.at(index)};
const auto mouth_type_info{RawData::RandomMiiMouthType.at(index)};
const auto glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))};
bf.faceline_type.Assign(
faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
@@ -455,8 +436,7 @@ MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
}
MiiInfo MiiManager::BuildDefault(std::size_t index) {
return ConvertStoreDataToInfo(BuildDefaultStoreData(
GetArrayValue<DefaultMii>(RawData::DefaultMii.data(), index), user_id));
return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id));
}
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {

View File

@@ -233,7 +233,7 @@ struct RandomMiiData4 {
Age age{};
Race race{};
u32 values_count{};
std::array<u8, 0xbc> values{};
std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
@@ -241,14 +241,14 @@ struct RandomMiiData3 {
u32 arg_1;
u32 arg_2;
u32 values_count;
std::array<u8, 0xbc> values{};
std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
struct RandomMiiData2 {
u32 arg_1;
u32 values_count;
std::array<u8, 0xbc> values{};
std::array<u32, 47> values{};
};
static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");

File diff suppressed because it is too large Load Diff

View File

@@ -7,21 +7,22 @@
#include <array>
#include "common/common_types.h"
#include "core/hle/service/mii/manager.h"
namespace Service::Mii::RawData {
extern const std::array<u8, 1728> DefaultMii;
extern const std::array<u8, 3672> RandomMiiFaceline;
extern const std::array<u8, 1200> RandomMiiFacelineColor;
extern const std::array<u8, 3672> RandomMiiFacelineWrinkle;
extern const std::array<u8, 3672> RandomMiiFacelineMakeup;
extern const std::array<u8, 3672> RandomMiiHairType;
extern const std::array<u8, 1800> RandomMiiHairColor;
extern const std::array<u8, 3672> RandomMiiEyeType;
extern const std::array<u8, 588> RandomMiiEyeColor;
extern const std::array<u8, 3672> RandomMiiEyebrowType;
extern const std::array<u8, 3672> RandomMiiNoseType;
extern const std::array<u8, 3672> RandomMiiMouthType;
extern const std::array<u8, 588> RandomMiiGlassType;
extern const std::array<Service::Mii::DefaultMii, 8> DefaultMii;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline;
extern const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType;
extern const std::array<Service::Mii::RandomMiiData3, 9> RandomMiiHairColor;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType;
extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType;
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType;
extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType;
} // namespace Service::Mii::RawData

View File

@@ -8,9 +8,9 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/lock.h"
#include "core/hle/service/nfp/nfp.h"

View File

@@ -21,6 +21,93 @@ enum class RequestState : u32 {
Connected = 3,
};
struct IpAddressSetting {
bool is_automatic{};
Network::IPv4Address current_address{};
Network::IPv4Address subnet_mask{};
Network::IPv4Address gateway{};
};
static_assert(sizeof(IpAddressSetting) == 0xD, "IpAddressSetting has incorrect size.");
struct DnsSetting {
bool is_automatic{};
Network::IPv4Address primary_dns{};
Network::IPv4Address secondary_dns{};
};
static_assert(sizeof(DnsSetting) == 0x9, "DnsSetting has incorrect size.");
struct ProxySetting {
bool enabled{};
INSERT_PADDING_BYTES(1);
u16 port{};
std::array<char, 0x64> proxy_server{};
bool automatic_auth_enabled{};
std::array<char, 0x20> user{};
std::array<char, 0x20> password{};
INSERT_PADDING_BYTES(1);
};
static_assert(sizeof(ProxySetting) == 0xAA, "ProxySetting has incorrect size.");
struct IpSettingData {
IpAddressSetting ip_address_setting{};
DnsSetting dns_setting{};
ProxySetting proxy_setting{};
u16 mtu{};
};
static_assert(sizeof(IpSettingData) == 0xC2, "IpSettingData has incorrect size.");
struct SfWirelessSettingData {
u8 ssid_length{};
std::array<char, 0x20> ssid{};
u8 unknown_1{};
u8 unknown_2{};
u8 unknown_3{};
std::array<char, 0x41> passphrase{};
};
static_assert(sizeof(SfWirelessSettingData) == 0x65, "SfWirelessSettingData has incorrect size.");
struct NifmWirelessSettingData {
u8 ssid_length{};
std::array<char, 0x21> ssid{};
u8 unknown_1{};
INSERT_PADDING_BYTES(1);
u32 unknown_2{};
u32 unknown_3{};
std::array<char, 0x41> passphrase{};
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(NifmWirelessSettingData) == 0x70,
"NifmWirelessSettingData has incorrect size.");
#pragma pack(push, 1)
struct SfNetworkProfileData {
IpSettingData ip_setting_data{};
u128 uuid{};
std::array<char, 0x40> network_name{};
u8 unknown_1{};
u8 unknown_2{};
u8 unknown_3{};
u8 unknown_4{};
SfWirelessSettingData wireless_setting_data{};
INSERT_PADDING_BYTES(1);
};
static_assert(sizeof(SfNetworkProfileData) == 0x17C, "SfNetworkProfileData has incorrect size.");
struct NifmNetworkProfileData {
u128 uuid{};
std::array<char, 0x40> network_name{};
u32 unknown_1{};
u32 unknown_2{};
u8 unknown_3{};
u8 unknown_4{};
INSERT_PADDING_BYTES(2);
NifmWirelessSettingData wireless_setting_data{};
IpSettingData ip_setting_data{};
};
static_assert(sizeof(NifmNetworkProfileData) == 0x18E,
"NifmNetworkProfileData has incorrect size.");
#pragma pack(pop)
class IScanRequest final : public ServiceFramework<IScanRequest> {
public:
explicit IScanRequest(Core::System& system_) : ServiceFramework{system_, "IScanRequest"} {
@@ -128,7 +215,11 @@ private:
void GetAppletInfo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 8};
std::vector<u8> out_buffer(ctx.GetWriteBufferSize());
ctx.WriteBuffer(out_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
rb.Push<u32>(0);
@@ -179,6 +270,46 @@ private:
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IRequest>(system);
}
void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
const SfNetworkProfileData network_profile_data{
.ip_setting_data{
.ip_address_setting{
.is_automatic{true},
.current_address{192, 168, 1, 100},
.subnet_mask{255, 255, 255, 0},
.gateway{192, 168, 1, 1},
},
.dns_setting{
.is_automatic{true},
.primary_dns{1, 1, 1, 1},
.secondary_dns{1, 0, 0, 1},
},
.proxy_setting{
.enabled{false},
.port{},
.proxy_server{},
.automatic_auth_enabled{},
.user{},
.password{},
},
.mtu{1500},
},
.uuid{0xdeadbeef, 0xdeadbeef},
.network_name{"yuzu Network"},
.wireless_setting_data{
.ssid_length{12},
.ssid{"yuzu Network"},
.passphrase{"yuzupassword"},
},
};
ctx.WriteBuffer(network_profile_data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -210,6 +341,34 @@ private:
rb.PushIpcInterface<INetworkProfile>(system);
rb.PushRaw<u128>(uuid);
}
void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
struct IpConfigInfo {
IpAddressSetting ip_address_setting;
DnsSetting dns_setting;
};
static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
"IpConfigInfo has incorrect size.");
const IpConfigInfo ip_config_info{
.ip_address_setting{
.is_automatic{true},
.current_address{192, 168, 1, 100},
.subnet_mask{255, 255, 255, 0},
.gateway{192, 168, 1, 1},
},
.dns_setting{
.is_automatic{true},
.primary_dns{1, 1, 1, 1},
.secondary_dns{1, 0, 0, 1},
},
};
IPC::ResponseBuilder rb{ctx, 2 + sizeof(IpConfigInfo) / sizeof(u32)};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<IpConfigInfo>(ip_config_info);
}
void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -248,7 +407,7 @@ IGeneralService::IGeneralService(Core::System& system_)
{1, &IGeneralService::GetClientId, "GetClientId"},
{2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
{4, &IGeneralService::CreateRequest, "CreateRequest"},
{5, nullptr, "GetCurrentNetworkProfile"},
{5, &IGeneralService::GetCurrentNetworkProfile, "GetCurrentNetworkProfile"},
{6, nullptr, "EnumerateNetworkInterfaces"},
{7, nullptr, "EnumerateNetworkProfiles"},
{8, nullptr, "GetNetworkProfile"},
@@ -258,7 +417,7 @@ IGeneralService::IGeneralService(Core::System& system_)
{12, &IGeneralService::GetCurrentIpAddress, "GetCurrentIpAddress"},
{13, nullptr, "GetCurrentAccessPointOld"},
{14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
{15, nullptr, "GetCurrentIpConfigInfo"},
{15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"},
{16, nullptr, "SetWirelessCommunicationEnabled"},
{17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
{18, nullptr, "GetInternetConnectionStatus"},

View File

@@ -65,13 +65,18 @@ static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMem
void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output) {
ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");
if (input.size() < 2) {
LOG_ERROR(Service_NS, "Input font is empty");
return;
}
const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor
std::vector<u32> transformed_font(input.size());
// TODO(ogniK): Figure out a better way to do this
std::transform(input.begin(), input.end(), transformed_font.begin(),
[&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); });
transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size
std::memcpy(output.data(), transformed_font.data() + 2, transformed_font.size() * sizeof(u32));
std::memcpy(output.data(), transformed_font.data() + 2,
(transformed_font.size() - 2) * sizeof(u32));
}
void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output,

View File

@@ -155,7 +155,13 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::ve
return NvResult::BadParameter;
}
if (events_interface.registered[event_id]) {
return NvResult::BadParameter;
const auto event_state = events_interface.status[event_id];
if (event_state != EventState::Free) {
LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event");
events_interface.UnregisterEvent(event_id);
} else {
return NvResult::BadParameter;
}
}
events_interface.RegisterEvent(event_id);
return NvResult::Success;

View File

@@ -6,9 +6,9 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/interface.h"
#include "core/hle/service/nvdrv/nvdata.h"

View File

@@ -50,11 +50,11 @@ public:
{1046, nullptr, "DisableFeaturesForReset"},
{1047, nullptr, "NotifyApplicationDownloadStarted"},
{1048, nullptr, "NotifyNetworkProfileCreated"},
{1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"},
{1062, nullptr, "GetStereoVisionRestriction"},
{1063, nullptr, "SetStereoVisionRestriction"},
{1064, nullptr, "ResetConfirmedStereoVisionPermission"},
{1065, nullptr, "IsStereoVisionPermitted"},
{1061, &IParentalControlService::ConfirmStereoVisionRestrictionConfigurable, "ConfirmStereoVisionRestrictionConfigurable"},
{1062, &IParentalControlService::GetStereoVisionRestriction, "GetStereoVisionRestriction"},
{1063, &IParentalControlService::SetStereoVisionRestriction, "SetStereoVisionRestriction"},
{1064, &IParentalControlService::ResetConfirmedStereoVisionPermission, "ResetConfirmedStereoVisionPermission"},
{1065, &IParentalControlService::IsStereoVisionPermitted, "IsStereoVisionPermitted"},
{1201, nullptr, "UnlockRestrictionTemporarily"},
{1202, nullptr, "UnlockSystemSettingsRestriction"},
{1203, nullptr, "SetPinCode"},
@@ -114,6 +114,7 @@ public:
{2015, nullptr, "FinishSynchronizeParentalControlSettingsWithLastUpdated"},
{2016, nullptr, "RequestUpdateExemptionListAsync"},
};
// clang-format on
RegisterHandlers(functions);
}
@@ -131,6 +132,49 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(true);
}
void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto can_use = rp.Pop<bool>();
LOG_WARNING(Service_PCTL, "(STUBBED) called, can_use={}", can_use);
can_use_stereo_vision = can_use;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(can_use_stereo_vision);
}
void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
bool can_use_stereo_vision = true;
};
void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
@@ -149,7 +193,8 @@ void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext
rb.PushIpcInterface<IParentalControlService>(system);
}
Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, const char* name)
Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
const char* name)
: ServiceFramework{system_, name}, module{std::move(module_)} {}
Module::Interface::~Interface() = default;

View File

@@ -23,11 +23,11 @@ public:
{10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"},
{10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old2>, "SaveReportOld2"},
{10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old2>, "SaveReportWithUserOld2"},
{10104, nullptr, "SaveReport"},
{10105, nullptr, "SaveReportWithUser"},
{10200, nullptr, "RequestImmediateTransmission"},
{10300, nullptr, "GetTransmissionStatus"},
{10400, nullptr, "GetSystemSessionId"},
{10104, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"},
{10105, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"},
{10200, &PlayReport::RequestImmediateTransmission, "RequestImmediateTransmission"},
{10300, &PlayReport::GetTransmissionStatus, "GetTransmissionStatus"},
{10400, &PlayReport::GetSystemSessionId, "GetSystemSessionId"},
{20100, &PlayReport::SaveSystemReport, "SaveSystemReport"},
{20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"},
{20200, nullptr, "SetOperationMode"},
@@ -59,16 +59,22 @@ private:
IPC::RequestParser rp{ctx};
const auto process_id = rp.PopRaw<u64>();
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
data.emplace_back(ctx.ReadBuffer(1));
}
const auto data1 = ctx.ReadBuffer(0);
const auto data2 = [ctx] {
if (ctx.CanReadBuffer(1)) {
return ctx.ReadBuffer(1);
}
LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}",
Type, process_id, data[0].size());
return std::vector<u8>{};
}();
LOG_DEBUG(Service_PREPO,
"called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}",
Type, process_id, data1.size(), data2.size());
const auto& reporter{system.GetReporter()};
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id);
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
process_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -79,35 +85,67 @@ private:
IPC::RequestParser rp{ctx};
const auto user_id = rp.PopRaw<u128>();
const auto process_id = rp.PopRaw<u64>();
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
const auto read_buffer_count =
ctx.BufferDescriptorX().size() + ctx.BufferDescriptorA().size();
if (read_buffer_count > 1) {
data.emplace_back(ctx.ReadBuffer(1));
const auto data1 = ctx.ReadBuffer(0);
const auto data2 = [ctx] {
if (ctx.CanReadBuffer(1)) {
return ctx.ReadBuffer(1);
}
}
LOG_DEBUG(
Service_PREPO,
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}",
Type, user_id[1], user_id[0], process_id, data[0].size());
return std::vector<u8>{};
}();
LOG_DEBUG(Service_PREPO,
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, "
"data1_size={:016X}, data2_size={:016X}",
Type, user_id[1], user_id[0], process_id, data1.size(), data2.size());
const auto& reporter{system.GetReporter()};
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id,
user_id);
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
process_id, user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void RequestImmediateTransmission(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_PREPO, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetTransmissionStatus(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_PREPO, "(STUBBED) called");
constexpr s32 status = 0;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(status);
}
void GetSystemSessionId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_PREPO, "(STUBBED) called");
constexpr u64 system_session_id = 0;
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(system_session_id);
}
void SaveSystemReport(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
const auto data1 = ctx.ReadBuffer(0);
const auto data2 = ctx.ReadBuffer(1);
const auto data2 = [ctx] {
if (ctx.CanReadBuffer(1)) {
return ctx.ReadBuffer(1);
}
return std::vector<u8>{};
}();
LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
title_id, data1.size(), data2.size());
@@ -125,7 +163,13 @@ private:
const auto title_id = rp.PopRaw<u64>();
const auto data1 = ctx.ReadBuffer(0);
const auto data2 = ctx.ReadBuffer(1);
const auto data2 = [ctx] {
if (ctx.CanReadBuffer(1)) {
return ctx.ReadBuffer(1);
}
return std::vector<u8>{};
}();
LOG_DEBUG(Service_PREPO,
"called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, "

View File

@@ -5,13 +5,116 @@
#include <memory>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/ptm/psm.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::PSM {
class IPsmSession final : public ServiceFramework<IPsmSession> {
public:
explicit IPsmSession(Core::System& system_) : ServiceFramework{system_, "IPsmSession"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IPsmSession::BindStateChangeEvent, "BindStateChangeEvent"},
{1, &IPsmSession::UnbindStateChangeEvent, "UnbindStateChangeEvent"},
{2, &IPsmSession::SetChargerTypeChangeEventEnabled, "SetChargerTypeChangeEventEnabled"},
{3, &IPsmSession::SetPowerSupplyChangeEventEnabled, "SetPowerSupplyChangeEventEnabled"},
{4, &IPsmSession::SetBatteryVoltageStateChangeEventEnabled, "SetBatteryVoltageStateChangeEventEnabled"},
};
// clang-format on
RegisterHandlers(functions);
state_change_event = Kernel::WritableEvent::CreateEventPair(
system_.Kernel(), "IPsmSession::state_change_event");
}
~IPsmSession() override = default;
void SignalChargerTypeChanged() {
if (should_signal && should_signal_charger_type) {
state_change_event.writable->Signal();
}
}
void SignalPowerSupplyChanged() {
if (should_signal && should_signal_power_supply) {
state_change_event.writable->Signal();
}
}
void SignalBatteryVoltageStateChanged() {
if (should_signal && should_signal_battery_voltage) {
state_change_event.writable->Signal();
}
}
private:
void BindStateChangeEvent(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_PSM, "called");
should_signal = true;
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(state_change_event.readable);
}
void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_PSM, "called");
should_signal = false;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
LOG_DEBUG(Service_PSM, "called, state={}", state);
should_signal_charger_type = state;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
LOG_DEBUG(Service_PSM, "called, state={}", state);
should_signal_power_supply = state;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
LOG_DEBUG(Service_PSM, "called, state={}", state);
should_signal_battery_voltage = state;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
bool should_signal_charger_type{};
bool should_signal_power_supply{};
bool should_signal_battery_voltage{};
bool should_signal{};
Kernel::EventPair state_change_event;
};
class PSM final : public ServiceFramework<PSM> {
public:
explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
@@ -24,7 +127,7 @@ public:
{4, nullptr, "IsBatteryChargingEnabled"},
{5, nullptr, "AcquireControllerPowerSupply"},
{6, nullptr, "ReleaseControllerPowerSupply"},
{7, nullptr, "OpenSession"},
{7, &PSM::OpenSession, "OpenSession"},
{8, nullptr, "EnableEnoughPowerChargeEmulation"},
{9, nullptr, "DisableEnoughPowerChargeEmulation"},
{10, nullptr, "EnableFastBatteryCharging"},
@@ -61,6 +164,14 @@ private:
rb.PushEnum(charger_type);
}
void OpenSession(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_PSM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IPsmSession>(system);
}
enum class ChargerType : u32 {
Unplugged = 0,
RegularCharger = 1,

View File

@@ -11,10 +11,10 @@
#include "core/hle/ipc.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/acc/acc.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/aoc/aoc_u.h"

View File

@@ -13,7 +13,7 @@
#include "common/microprofile.h"
#include "common/thread.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/sockets/bsd.h"
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/network/network.h"
@@ -255,6 +255,25 @@ void BSD::GetSockName(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
void BSD::GetSockOpt(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const u32 level = rp.Pop<u32>();
const auto optname = static_cast<OptName>(rp.Pop<u32>());
LOG_WARNING(Service, "(STUBBED) called. fd={} level={} optname=0x{:x}", fd, level, optname);
std::vector<u8> optval(ctx.GetWriteBufferSize());
ctx.WriteBuffer(optval);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
rb.Push<s32>(-1);
rb.PushEnum(Errno::NOTCONN);
rb.Push<u32>(static_cast<u32>(optval.size()));
}
void BSD::Listen(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
@@ -401,6 +420,16 @@ void BSD::Close(Kernel::HLERequestContext& ctx) {
BuildErrnoResponse(ctx, CloseImpl(fd));
}
void BSD::EventFd(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 initval = rp.Pop<u64>();
const u32 flags = rp.Pop<u32>();
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
BuildErrnoResponse(ctx, Errno::SUCCESS);
}
template <typename Work>
void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, Work work) {
work.Execute(this);
@@ -812,7 +841,7 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
{14, &BSD::Connect, "Connect"},
{15, &BSD::GetPeerName, "GetPeerName"},
{16, &BSD::GetSockName, "GetSockName"},
{17, nullptr, "GetSockOpt"},
{17, &BSD::GetSockOpt, "GetSockOpt"},
{18, &BSD::Listen, "Listen"},
{19, nullptr, "Ioctl"},
{20, &BSD::Fcntl, "Fcntl"},
@@ -826,7 +855,7 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
{28, nullptr, "GetResourceStatistics"},
{29, nullptr, "RecvMMsg"},
{30, nullptr, "SendMMsg"},
{31, nullptr, "EventFd"},
{31, &BSD::EventFd, "EventFd"},
{32, nullptr, "RegisterResourceStatisticsName"},
{33, nullptr, "Initialize2"},
};

View File

@@ -125,6 +125,7 @@ private:
void Connect(Kernel::HLERequestContext& ctx);
void GetPeerName(Kernel::HLERequestContext& ctx);
void GetSockName(Kernel::HLERequestContext& ctx);
void GetSockOpt(Kernel::HLERequestContext& ctx);
void Listen(Kernel::HLERequestContext& ctx);
void Fcntl(Kernel::HLERequestContext& ctx);
void SetSockOpt(Kernel::HLERequestContext& ctx);
@@ -135,6 +136,7 @@ private:
void SendTo(Kernel::HLERequestContext& ctx);
void Write(Kernel::HLERequestContext& ctx);
void Close(Kernel::HLERequestContext& ctx);
void EventFd(Kernel::HLERequestContext& ctx);
template <typename Work>
void ExecuteWork(Kernel::HLERequestContext& ctx, Work work);

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