Compare commits

..

131 Commits

Author SHA1 Message Date
bunnei
14d28a043d hle: kernel: Re-create memory layout at initialization.
- As this can only be derived once.
2022-02-27 18:00:09 -08:00
bunnei
16e5954fcb hle: kernel: Remove unused pool locals. 2022-02-27 18:00:09 -08:00
bunnei
f87f076162 hle: kernel: k_memory_manager: Rework for latest kernel behavior.
- Updates the KMemoryManager implementation against latest documentation.
- Reworks KMemoryLayout to be accessed throughout the kernel.
- Fixes an issue with pool sizes being incorrectly reported.
2022-02-27 18:00:09 -08:00
bunnei
adbb9c2b00 hle: kernel: k_page_heap: GetPhysicalAddr can be const. 2022-02-27 10:34:02 -08:00
bunnei
f7e65eb971 hle: kernel: k_page_heap: Remove superfluous consexpr. 2022-02-27 10:34:02 -08:00
bunnei
06e2b76c75 hle: kernel: k_page_heap: Various updates and improvements.
- KPageHeap tracks physical addresses, not virtual addresses.
- Various updates and improvements to match latest documentation for this type.
2022-02-27 10:34:02 -08:00
bunnei
5d1a81520c hle: kernel: Add initial_process.h header. 2022-02-27 10:34:02 -08:00
bunnei
a6496deeed hle: kernel: board: nx: Add k_memory_layout.h header. 2022-02-27 10:34:02 -08:00
bunnei
9b5e7971dc hle: kernel: k_system_control: Add GetRealMemorySize and update GetKernelPhysicalBaseAddress. 2022-02-27 10:34:02 -08:00
bunnei
18e77a54c3 hle: kernel: k_memory_layout: Add GetPhysicalLinearRegion. 2022-02-27 10:34:02 -08:00
bunnei
06a21ac229 hle: kernel: k_memory_region_types: Update for new regions. 2022-02-27 10:34:02 -08:00
bunnei
96d90be59f Merge pull request #7955 from bunnei/update-dynarmic
externals: Dynarmic: Update to latest rev.
2022-02-26 10:52:47 -08:00
bunnei
ce46aa4721 externals: Dynarmic: Update to latest rev.
- Fixes inaccurate size reporting in SpaceRemaining, which caused crashes in yuzu with SSBU.
2022-02-26 09:51:41 -08:00
bunnei
20e9501b0d Merge pull request #7932 from bunnei/extended-mem-layout
Add extended memory layout (6GB) support and improve KResourceLimit management
2022-02-26 01:41:08 -08:00
bunnei
56d9052b73 Merge pull request #7953 from ameerj/radv-rdna2-crash
vulkan_device: Blacklist RADV on RDNA2 from VK_EXT_vertex_input_dynamic_state
2022-02-26 01:19:13 -08:00
Mai M
91d802f68d Merge pull request #7948 from Morph1984/11-11-10-float
maxwell_to_(gl/vk): Add 11_11_10 float vertex format
2022-02-26 00:09:44 -05:00
Ameer J
5f8d6bc504 vulkan_device: Blacklist RADV on RDNA2 from VK_EXT_vertex_input_dynamic_state
RDNA2 devices running under the RADV driver were crashing when VK_EXT_vertex_input_dynamic_state was enabled.

Blacklisting these devices until a proper fix is established.
2022-02-25 23:09:03 -05:00
Morph
e292b2d991 maxwell_to_(gl/vk): Add 11_11_10 float vertex format
- Used by パワプロクンポケットR
2022-02-25 17:11:17 -05:00
bunnei
6c9b9b72da Merge pull request #7939 from asLody/fb-format-gbra8
vk_blit_screen: Add missing framebuffer format
2022-02-25 00:58:28 -08:00
bunnei
f582a4482d Merge pull request #7927 from german77/amiibo
yuzu: Disconnect amiibos on drag and drop
2022-02-24 23:13:32 -08:00
bunnei
1079215871 Merge pull request #7859 from german77/battery_again
input_common: Remove battery duplicated struct and update every button press
2022-02-24 11:42:51 -08:00
Lody
6978093404 vk_blit_screen: Add missing format bgra8 2022-02-24 19:25:39 +08:00
Mai M
2f45e999d8 Merge pull request #7933 from german77/am_update
service: am: Update enum names to match documentation
2022-02-21 20:42:01 -05:00
Narr the Reg
d44464829b service: am: Update enum names to match documentation 2022-02-21 18:00:50 -06:00
bunnei
efe50d88ec Merge pull request #7913 from voidanix/anv-fix
vulkan_device: fix missing format in ANV
2022-02-21 14:34:27 -07:00
bunnei
71f62a346d hle: kernel: KSystemControl: Use 6GB memory layout when "use_extended_memory_layout" setting is enabled.
- This uses a larger 6GB DRAM memory layout, which is useful for some mods that require more memory.
2022-02-21 13:07:19 -08:00
bunnei
c0e45a3c78 core: device_memory: Use memory size reported by KSystemControl.
- That way, we can consolidate the memory layout to one place.
2022-02-21 13:07:19 -08:00
bunnei
70482e6b26 settings: Add a new "use_extended_memory_layout" setting.
- This will be used to enable emulation of a larger memory arrangement.
2022-02-21 13:07:19 -08:00
bunnei
ca5e843bf6 core: hle: kernel: Remove resource limit hack for PhysicalMemory.
- With prior changes, we now report the correct amount of physical memory available to the emulated process.
2022-02-21 12:41:31 -08:00
bunnei
a74fddc98f core: hle: kernel: KProcess: Pass in KResourceLimit on process creation.
- This allows us to have a resource limit per process, rather than use the global system resource limit.
2022-02-21 12:41:31 -08:00
bunnei
57ebcbf2c4 core: hle: kernel: KEvent: Pass in owner KProcess on event creation.
- This is necessary to ensure resource limits are freed from the right process.
2022-02-21 12:41:06 -08:00
bunnei
c7019db6f4 core: hle: kernel: KResourceLimit: Add a helper function for creating a KResourceLimit for a process. 2022-02-21 12:40:09 -08:00
bunnei
21f5912ec9 Merge pull request #7919 from bunnei/phys-mem-updates
core: hle: kernel: KPageTable: Improve Un/MapPhysicalMemory.
2022-02-21 13:39:05 -07:00
bunnei
8d46c3cc66 Merge pull request #7920 from bunnei/fix-unmap-pages
core: hle: kernel: KPageTable: Fix UnmapPages.
2022-02-21 13:38:52 -07:00
voidanix
7712e46d64 vulkan_device: fix missing format in ANV
Currently Mesa's ANV driver does not support
VK_FORMAT_B5G6R5_UNORM_PACK16, implement an alternative for it.
2022-02-21 09:21:41 +01:00
german77
b504df9d02 yuzu: Remove amiibos on drag and drop 2022-02-20 14:28:21 -06:00
bunnei
92b2e92620 fixup! core: hle: kernel: KPageTable: Improve Un/MapPhysicalMemory. 2022-02-19 00:14:27 -08:00
bunnei
2984695265 Merge pull request #7867 from german77/amiibo
nfp: Improve amiibo support
2022-02-19 00:57:47 -07:00
bunnei
c9260a75f6 core: hle: kernel: KPageTable: Fix UnmapPages.
- Fixes a logic bug in KPageTable::UnmapPages.
2022-02-18 23:48:16 -08:00
bunnei
1a16d055df core: hle: kernel: KPageTable: Improve Un/MapPhysicalMemory.
- Improves the implementations of MapPhysicalMemory and UnmapPhysicalMemory to more closely reflect latest HOS.
2022-02-18 23:42:27 -08:00
bunnei
83a84f1c2d Merge pull request #7900 from german77/enter
yuzu: config: Fix mapping issues with the enter key
2022-02-18 15:47:34 -07:00
Mai M
90a4591563 Merge pull request #7909 from Wunkolo/null-visit-ctor
common: Add NullVisitor default constructor
2022-02-18 17:44:02 -05:00
Wunkolo
768fdb269e common: Add NullVisitor default constructor
Addresses https://github.com/yuzu-emu/yuzu/issues/7881 to fix linux
builds.

`YUZU_NON_COPYABLE` deletes the `T(const T&)` constructor which will
cause the implicitly defined default ctor/dtor to no-longer generate.
2022-02-17 06:28:19 -08:00
Mai M
c48b9668f0 Merge pull request #7866 from xerpi/svc-OutputDebugString32-CreateCodeMemory32-ControlCodeMemory32
kernel: svc: Add OutputDebugString32, CreateCodeMemory32, ControlCodeMemory32
2022-02-16 22:49:56 -05:00
bunnei
027ff7847c Merge pull request #7878 from german77/mnpp
service/mnpp: Stub mnpp_app
2022-02-16 18:42:49 -07:00
Morph
4514325b9c Merge pull request #7899 from Kelebek1/test
file_sys: Dump patched exefs rather than base
2022-02-16 16:37:09 -05:00
bunnei
b5fd9c58cd Merge pull request #7877 from lat9nq/upd_rev
audio_core: Update current process revision
2022-02-15 13:08:40 -07:00
bunnei
910a0fa58e Merge pull request #7891 from Morph1984/buffer_to_string_view
common: fs_util: Add buffer to string view utility functions
2022-02-15 12:02:23 -07:00
Narr the Reg
1e21f5f872 yuzu: config: Fix mapping issues with the enter key 2022-02-15 11:08:11 -06:00
Kelebek1
e1201abc1e Dump patched exefs rather than base 2022-02-15 04:52:28 +00:00
Sergi Granell
c3242abe95 kernel: svc: Add OutputDebugString32, CreateCodeMemory32, ControlCodeMemory32
Very straightforward, they are just wrappers to the 64-bit version of
the SVC.
2022-02-15 00:45:19 +01:00
bunnei
d81cdd9114 Merge pull request #7871 from german77/svc2
svc: Set unique names for function tables
2022-02-14 16:32:54 -07:00
Mai M
45bcd7f9b8 Merge pull request #7890 from Morph1984/utf8-console
debugger: console: Set console output codepage to UTF-8
2022-02-13 19:54:05 -05:00
Morph
4390370a19 common: fs_util: Add buffer to string view utility functions
These functions allow to construct a string view from an input buffer, avoiding the copy done by the non string view counterparts. However, callers must be cognizant of the viewed buffer's lifetime to avoid a use-after-free.
2022-02-13 18:53:21 -05:00
Morph
54c7af9902 debugger: console: Set console output codepage to UTF-8
This allows the console to display multi-byte encoded characters.
2022-02-13 18:18:05 -05:00
german77
b57d61010f nfp: Allow files without password data 2022-02-13 13:52:34 -06:00
Narr the Reg
eaca010ee9 Merge pull request #7887 from lat9nq/stub-is-usb-full-key
hid: Stub IsUsbFullKeyControllerEnabled
2022-02-12 22:22:38 -06:00
lat9nq
c5d22952bf hid: Stub IsUsbFullKeyControllerEnabled
Used by Splatoon 2, when opening the inventory from a LAN battle lobby.

Reference: https://switchbrew.org/wiki/HID_services
2022-02-12 15:42:50 -05:00
lat9nq
81806603eb audio_core: Update current process revision
Update CURRENT_PROCESS_REVISION from REV9 to REVA.

Used by Nintendo Entertainment System - Nintendo Switch Online 6.0.0 and
Super Nintendo Entertainment System - Nintendo Switch Online 3.0.0.
2022-02-11 00:56:13 -05:00
bunnei
ca9da569ce Merge pull request #7852 from Morph1984/new-uuid
common: Revise and fix the UUID implementation
2022-02-10 21:52:13 -07:00
Narr the Reg
6705439cf3 service/mnpp: Stub mnpp_app
Used in Super Nintendo Entertainment System™ - Nintendo Switch Online
2022-02-10 21:55:28 -06:00
Morph
3799c820ca common: uuid: Use sizeof(u64) instead of 8 in Hash() 2022-02-10 15:03:49 -05:00
bunnei
1105614b86 Merge pull request #7861 from german77/user_features
yuzu: New hotkeys and mute audio on background
2022-02-10 10:06:58 -07:00
Narr the Reg
6a1ad03153 nfp: Separate nfc tag from amiibo data 2022-02-10 10:58:37 -06:00
bunnei
fc01074f89 Merge pull request #7860 from german77/no-more-drift
yuzu: Add auto center on right click
2022-02-09 17:52:39 -07:00
bunnei
bcd666b86b Merge pull request #7870 from yuzu-emu/fix-codememory-size
hle: kernel: KCodeMemory: Correct m_page_group number of pages.
2022-02-09 11:29:32 -07:00
Narr the Reg
d0c7c3f64f svc: Set unique names for function tables 2022-02-08 21:03:31 -06:00
bunnei
862dddf8c9 hle: kernel: KCodeMemory: Remove unused QueryMemory. 2022-02-08 18:49:41 -08:00
bunnei
d134ca68c6 hle: kernel: KCodeMemory: Correct m_page_group number of pages.
Credits to @xerpi for finding this issue and pointing it out on #7519.
2022-02-08 18:47:11 -08:00
german77
e35c2fd5d0 nfp: Address compiler issues 2022-02-08 18:52:44 -06:00
Narr the Reg
29f9a454eb nfp: Validate amiibo files 2022-02-08 14:09:30 -06:00
german77
41b65d38fa yuzu: Allow to open and remove the amiibo 2022-02-08 10:08:04 -06:00
german77
fc9abd3c62 nfp: Improve implementation 2022-02-08 10:08:04 -06:00
german77
c001a2af25 nfp: Move IUser class to header and add missing enum and structs 2022-02-07 09:18:22 -06:00
german77
3d24eb54ec nfp: Sort functions by command number 2022-02-07 09:18:22 -06:00
german77
ab93b4c66d yuzu: Mute audio when in background 2022-02-06 20:46:58 -06:00
german77
49eb78497b yuzu: Add docked, GPU accuracy and adapting filter hotkeys 2022-02-06 20:46:58 -06:00
german77
6a4ab3e0d2 yuzu: Add auto center on right click 2022-02-06 19:56:03 -06:00
german77
21742f0096 input_common: Remove battery duplicated struct and update every button press 2022-02-06 18:33:55 -06:00
Morph
b720009dc0 Merge pull request #7847 from tech-ticks/master
service: pm: Implement AtmosphereGetProcessInfo
2022-02-05 19:22:45 -05:00
Morph
36910e9020 Merge pull request #7851 from lat9nq/cmd-add-motion
yuzu-cmd: config: Support motion inputs
2022-02-05 19:22:18 -05:00
bunnei
0456ed6b4e Merge pull request #7849 from Morph1984/qt-frameless-window
main: Always remove the frameless window flag when restoring UI state
2022-02-05 15:18:48 -07:00
Morph
ec4d7f71fe common: uuid: Return an invalid UUID if conversion from string fails
The string constructor of UUID states:
Should the input string not meet the above requirements, an assert will be triggered and an invalid UUID is set instead.
2022-02-05 13:56:21 -05:00
Morph
25db62ce15 general: Rename NewUUID to UUID, and remove the previous UUID impl
This completes the removal of the old UUID implementation.
2022-02-05 13:56:21 -05:00
Morph
dfe11d72e3 profile: Migrate to the new UUID implementation 2022-02-05 13:56:21 -05:00
Morph
d94dcaefa0 common: uuid: Add AsU128()
This copies the internal bytes of the UUID into a u128 for backwards compatibility. This should not be used.
2022-02-05 13:56:21 -05:00
Morph
f0340b8d22 hle: ipc_helpers: Ignore -Wclass-memaccess
This warning is triggered by GCC when copying into non-trivially default constructible types, as it uses the more restrictive std::is_trivial (which includes std::is_trivially_default_constructible) to determine whether memcpy is safe instead of std::is_trivially_copyable.
2022-02-05 13:56:20 -05:00
Morph
ee0547e4c4 service: Migrate to the new UUID implementation 2022-02-05 13:18:46 -05:00
Morph
cb30fe50cd input/hid: Migrate to the new UUID implementation 2022-02-05 13:18:41 -05:00
Morph
3271099fea common: Implement NewUUID
This is a fixed and revised implementation of UUID that uses an array of bytes as its internal representation of a UUID instead of a u128 (which was an array of 2 u64s).
In addition to this, the generation of RFC 4122 Version 4 compliant UUIDs is also implemented.
2022-02-05 13:18:31 -05:00
bunnei
5cb1c2ad84 Merge pull request #7842 from german77/vibration_test
yuzu: config: Vibrate the controller while configuring vibration strength
2022-02-05 02:57:26 -07:00
lat9nq
928380ebf9 config: Support motion inputs
Motion inputs were not being read in by the config when yuzu-cmd boots
up. This adds support for those.

While we're at it, make a reference to the current player controls to
improve readability. Also updates the if statements in the Analog and
Button loops with curly braces to keep the style consistent.
2022-02-05 02:31:55 -05:00
bunnei
0ec5b9bff2 Merge pull request #7839 from german77/battery
yuzu: ui: Improve battery symbols
2022-02-04 18:23:35 -07:00
Mai M
7ad17ae397 Merge pull request #7848 from Morph1984/unused-core-include
input_common: Remove unused core include
2022-02-03 23:21:32 -05:00
Morph
edbfbf2f2f main: Always remove the frameless window flag when restoring UI state
For unknown reasons, this flag may persist after the application has been closed.
Removing this flag when restoring the UI state ensures that a frameless window will not be shown on startup.
2022-02-03 23:12:49 -05:00
Morph
fbefcf7280 input_common: Remove unused core include 2022-02-03 22:44:28 -05:00
tech-ticks
16bf50e610 service: pm: Implement AtmosphereGetProcessInfo 2022-02-04 01:41:36 +01:00
bunnei
fc8aef7323 Merge pull request #7811 from german77/analog-mod
input_common: Use attributes for analog range modifiers
2022-02-03 12:21:11 -07:00
bunnei
8ec32e9755 Merge pull request #7814 from FernandoS27/another-bug-in-my-schedule
Vulkan: Fix Scheduler Chunks when their FuncType is 0.
2022-02-02 19:15:06 -07:00
bunnei
03186af6a1 Merge pull request #7835 from bunnei/page-table-lock
hle: kernel: KPageTable: Migrate locks to KScopedLightLock.
2022-02-02 17:58:55 -07:00
Morph
d68eb751c5 Merge pull request #7838 from lioncash/noncopy
common_types: Remove NonCopyable struct
2022-02-02 16:04:26 -05:00
Narr the Reg
694c078655 yuzu: config: Vibrate the controller while configuring vibration strength 2022-02-02 14:54:24 -06:00
bunnei
9484519ec3 Merge pull request #7834 from german77/repeat
yuzu: Disable auto repeat on hotkeys again
2022-02-02 13:20:18 -07:00
Narr the Reg
064aa3de11 yuzu: ui: Improve battery symbols 2022-02-02 13:18:06 -06:00
Lioncash
ade44ac2ea common_types: Remove NonCopyable struct
Now that we're moved over to the YUZU_ defines, we can get rid of this
struct.
2022-02-02 13:19:01 -05:00
Lioncash
f785f73e92 general: Replace NonCopyable struct with equivalents 2022-02-02 13:17:12 -05:00
bunnei
09400e4f4e Merge pull request #7806 from ameerj/atomic64-fallbacks
shaders: Implement U32x2 atomic fallbacks when device does not support int64
2022-02-02 10:57:22 -07:00
Lioncash
76d83ffbec general: Move deleted copy/move constructor/assignment operators to public interface
This allows for better compiler errors, where the compiler will state a
copy or move couldn't occur due to the relevant function being deleted.

Previously a compiler would warn about the relevant function not being
accessible (which, while true, isn't as informative as it could be).
2022-02-02 12:17:08 -05:00
bunnei
72add82ee9 Merge pull request #7807 from german77/moar-buttons
input_common: Add home and hard touch press buttons to UDP controllers
2022-02-01 22:37:13 -07:00
bunnei
995e27e9b7 hle: kernel: KPageTable: Migrate locks to KScopedLightLock.
- More accurately reflects real kernel behavior by using guest locks.
2022-02-01 19:34:24 -08:00
Morph
79e8f4abe8 Merge pull request #7833 from lioncash/file-sys
configure_filesystem: Add missing changeEvent() override
2022-02-01 22:15:37 -05:00
bunnei
404a94a199 Merge pull request #7792 from german77/translate
hotkeys: Don't translate hotkey buttons
2022-02-01 20:07:52 -07:00
Lioncash
40d31b8b8a configure_filesystem: Add missing changeEvent() override
This allows the dialog to be retranslated during runtime if the language
is changed.
2022-02-01 22:03:12 -05:00
Lioncash
b9f06bd599 configure_filesystem: Normalize member function casing
All other dialog types we have use CamelCase naming.
2022-02-01 22:01:22 -05:00
Narr the Reg
330b31ae2e yuzu: Disable auto repeat on hotkeys again 2022-02-01 21:00:37 -06:00
bunnei
50e9ba34b4 Merge pull request #7809 from Morph1984/clock-constants
common: wall_clock: Utilize constants for ms, us, and ns ratios
2022-02-01 18:34:31 -07:00
Morph
a28a10bc54 Merge pull request #7831 from lioncash/motion
configure_motion_touch: Use functor versions of invokeMethod
2022-02-01 14:48:52 -05:00
Morph
cd9345e10c Merge pull request #7830 from lioncash/player-copy
configure_input_player: Avoid unnecessary ParamPackage copies
2022-02-01 14:48:33 -05:00
Lioncash
5c4ed30c21 configure_motion_touch: Use functor versions of invokeMethod
Same behavior, but ensures that the functions we're calling exist, since
they can be checked at compile-time.
2022-02-01 13:22:42 -05:00
Lioncash
e2a86e2c8a configure_input_player: Eliminate variable shadowing 2022-02-01 12:54:00 -05:00
Lioncash
2dba59d1ff configure_input_player: std::move input setters in HandleClick 2022-02-01 12:51:03 -05:00
Lioncash
9ba6bab920 configure_input_player: Avoid unnecessary ParamPackage copies
Avoids churning allocations.
2022-02-01 12:20:23 -05:00
Fernando Sahmkow
35ed051742 Vulkan: Fix Scheduler Chunks when their FuncType is 0. 2022-01-31 17:08:08 +01:00
german77
5bd0c1517f input_common: Use attributes for analog range modifiers 2022-01-30 22:42:40 -06:00
Morph
6267110b69 common: wall_clock: Check precision against the emulated CPU and CNTFRQ
In addition to requiring nanosecond precision, using the native clock requires that the hardware TSC has a precision greater than the emulated CPU and its clock counter.
2022-01-30 12:57:23 -05:00
Morph
4e766280c4 common: wall_clock: Utilize constants for ms, us, and ns ratios 2022-01-30 12:36:56 -05:00
german77
3d2d77eb3f input_common: Add home and hard touch press buttons to UDP controllers 2022-01-30 09:17:22 -06:00
ameerj
e394e1ecc4 emit_glsl_atomic: Implement 32x2 fallback atomic ops 2022-01-29 19:56:03 -05:00
ameerj
90a0506d56 lower_int64_to_int32: Add 64-bit atomic fallbacks 2022-01-29 19:56:02 -05:00
ameerj
ad58d7eae7 shaders: Add U64->U32x2 Atomic fallback functions 2022-01-29 19:55:53 -05:00
german77
fea05a17ea hotkeys: Don't translate hotkey buttons 2022-01-27 17:32:48 -06:00
151 changed files with 4560 additions and 1615 deletions

View File

@@ -15,7 +15,9 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
} // namespace Audren
constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '9');
constexpr u8 BASE_REVISION = '0';
constexpr u32_le CURRENT_PROCESS_REVISION =
Common::MakeMagic('R', 'E', 'V', static_cast<u8>(BASE_REVISION + 0xA));
constexpr std::size_t MAX_MIX_BUFFERS = 24;
constexpr std::size_t MAX_BIQUAD_FILTERS = 2;
constexpr std::size_t MAX_CHANNEL_COUNT = 6;

View File

@@ -46,13 +46,3 @@ using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space
using u128 = std::array<std::uint64_t, 2>;
static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide");
// An inheritable class to disallow the copy constructor and operator= functions
class NonCopyable {
protected:
constexpr NonCopyable() = default;
~NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};

View File

@@ -16,6 +16,10 @@ std::u8string BufferToU8String(std::span<const u8> buffer) {
return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})};
}
std::u8string_view BufferToU8StringView(std::span<const u8> buffer) {
return std::u8string_view{reinterpret_cast<const char8_t*>(buffer.data())};
}
std::string ToUTF8String(std::u8string_view u8_string) {
return std::string{u8_string.begin(), u8_string.end()};
}
@@ -24,6 +28,10 @@ std::string BufferToUTF8String(std::span<const u8> buffer) {
return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
}
std::string_view BufferToUTF8StringView(std::span<const u8> buffer) {
return std::string_view{reinterpret_cast<const char*>(buffer.data())};
}
std::string PathToUTF8String(const std::filesystem::path& path) {
return ToUTF8String(path.u8string());
}

View File

@@ -37,6 +37,15 @@ concept IsChar = std::same_as<T, char>;
*/
[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer);
/**
* Same as BufferToU8String, but returns a string view of the buffer.
*
* @param buffer Buffer of bytes
*
* @returns UTF-8 encoded std::u8string_view.
*/
[[nodiscard]] std::u8string_view BufferToU8StringView(std::span<const u8> buffer);
/**
* Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string.
*
@@ -57,6 +66,15 @@ concept IsChar = std::same_as<T, char>;
*/
[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
/**
* Same as BufferToUTF8String, but returns a string view of the buffer.
*
* @param buffer Buffer of bytes
*
* @returns UTF-8 encoded std::string_view.
*/
[[nodiscard]] std::string_view BufferToUTF8StringView(std::span<const u8> buffer);
/**
* Converts a filesystem path to a UTF-8 encoded std::string.
*

View File

@@ -209,6 +209,8 @@ enum class ButtonNames {
Triangle,
Share,
Options,
Home,
Touch,
// Mouse buttons
ButtonMouseWheel,

View File

@@ -108,6 +108,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, Migration) \
SUB(Service, Mii) \
SUB(Service, MM) \
SUB(Service, MNPP) \
SUB(Service, NCM) \
SUB(Service, NFC) \
SUB(Service, NFP) \

View File

@@ -76,6 +76,7 @@ enum class Class : u8 {
Service_Migration, ///< The migration service
Service_Mii, ///< The Mii service
Service_MM, ///< The MM (Multimedia) service
Service_MNPP, ///< The MNPP service
Service_NCM, ///< The NCM service
Service_NFC, ///< The NFC (Near-field communication) service
Service_NFP, ///< The NFP service

View File

@@ -10,11 +10,65 @@ PageTable::PageTable() = default;
PageTable::~PageTable() noexcept = default;
void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) {
const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
u64 address) const {
// Setup invalid defaults.
out_entry.phys_addr = 0;
out_entry.block_size = page_size;
out_context.next_page = 0;
// Validate that we can read the actual entry.
const auto page = address / page_size;
if (page >= backing_addr.size()) {
return false;
}
// Validate that the entry is mapped.
const auto phys_addr = backing_addr[page];
if (phys_addr == 0) {
return false;
}
// Populate the results.
out_entry.phys_addr = phys_addr + address;
out_context.next_page = page + 1;
out_context.next_offset = address + page_size;
return true;
}
bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const {
// Setup invalid defaults.
out_entry.phys_addr = 0;
out_entry.block_size = page_size;
// Validate that we can read the actual entry.
const auto page = context.next_page;
if (page >= backing_addr.size()) {
return false;
}
// Validate that the entry is mapped.
const auto phys_addr = backing_addr[page];
if (phys_addr == 0) {
return false;
}
// Populate the results.
out_entry.phys_addr = phys_addr + context.next_offset;
context.next_page = page + 1;
context.next_offset += page_size;
return true;
}
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
const std::size_t num_page_table_entries{1ULL
<< (address_space_width_in_bits - page_size_in_bits)};
pointers.resize(num_page_table_entries);
backing_addr.resize(num_page_table_entries);
current_address_space_width_in_bits = address_space_width_in_bits;
page_size = 1ULL << page_size_in_bits;
}
} // namespace Common

View File

@@ -27,6 +27,16 @@ enum class PageType : u8 {
* mimics the way a real CPU page table works.
*/
struct PageTable {
struct TraversalEntry {
u64 phys_addr{};
std::size_t block_size{};
};
struct TraversalContext {
u64 next_page{};
u64 next_offset{};
};
/// Number of bits reserved for attribute tagging.
/// This can be at most the guaranteed alignment of the pointers in the page table.
static constexpr int ATTRIBUTE_BITS = 2;
@@ -89,6 +99,10 @@ struct PageTable {
PageTable(PageTable&&) noexcept = default;
PageTable& operator=(PageTable&&) noexcept = default;
bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
u64 address) const;
bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const;
/**
* Resizes the page table to be able to accommodate enough pages within
* a given address space.
@@ -96,9 +110,9 @@ struct PageTable {
* @param address_space_width_in_bits The address size width in bits.
* @param page_size_in_bits The page size in bits.
*/
void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits);
size_t GetAddressSpaceBits() const {
std::size_t GetAddressSpaceBits() const {
return current_address_space_width_in_bits;
}
@@ -110,9 +124,11 @@ struct PageTable {
VirtualBuffer<u64> backing_addr;
size_t current_address_space_width_in_bits;
std::size_t current_address_space_width_in_bits{};
u8* fastmem_arena;
u8* fastmem_arena{};
std::size_t page_size{};
};
} // namespace Common

View File

@@ -73,8 +73,8 @@ std::string ParamPackage::Serialize() const {
return result;
}
std::string ParamPackage::Get(std::string_view key, const std::string& default_value) const {
const auto pair = data.find(key);
std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const {
auto pair = data.find(key);
if (pair == data.end()) {
LOG_DEBUG(Common, "key '{}' not found", key);
return default_value;
@@ -83,8 +83,8 @@ std::string ParamPackage::Get(std::string_view key, const std::string& default_v
return pair->second;
}
int ParamPackage::Get(std::string_view key, int default_value) const {
const auto pair = data.find(key);
int ParamPackage::Get(const std::string& key, int default_value) const {
auto pair = data.find(key);
if (pair == data.end()) {
LOG_DEBUG(Common, "key '{}' not found", key);
return default_value;
@@ -98,8 +98,8 @@ int ParamPackage::Get(std::string_view key, int default_value) const {
}
}
float ParamPackage::Get(std::string_view key, float default_value) const {
const auto pair = data.find(key);
float ParamPackage::Get(const std::string& key, float default_value) const {
auto pair = data.find(key);
if (pair == data.end()) {
LOG_DEBUG(Common, "key {} not found", key);
return default_value;
@@ -125,8 +125,8 @@ void ParamPackage::Set(const std::string& key, float value) {
data.insert_or_assign(key, std::to_string(value));
}
bool ParamPackage::Has(std::string_view key) const {
return data.contains(key);
bool ParamPackage::Has(const std::string& key) const {
return data.find(key) != data.end();
}
void ParamPackage::Erase(const std::string& key) {

View File

@@ -4,7 +4,6 @@
#pragma once
#include <functional>
#include <initializer_list>
#include <string>
#include <unordered_map>
@@ -14,17 +13,7 @@ namespace Common {
/// A string-based key-value container supporting serializing to and deserializing from a string
class ParamPackage {
public:
struct DataHash final {
using is_transparent = void;
[[nodiscard]] size_t operator()(std::string_view view) const noexcept {
return std::hash<std::string_view>{}(view);
}
[[nodiscard]] size_t operator()(const std::string& str) const noexcept {
return std::hash<std::string>{}(str);
}
};
using DataType = std::unordered_map<std::string, std::string, DataHash, std::equal_to<>>;
using DataType = std::unordered_map<std::string, std::string>;
ParamPackage() = default;
explicit ParamPackage(const std::string& serialized);
@@ -33,16 +22,16 @@ public:
ParamPackage(ParamPackage&& other) noexcept = default;
ParamPackage& operator=(const ParamPackage& other) = default;
ParamPackage& operator=(ParamPackage&& other) noexcept = default;
ParamPackage& operator=(ParamPackage&& other) = default;
[[nodiscard]] std::string Serialize() const;
[[nodiscard]] std::string Get(std::string_view key, const std::string& default_value) const;
[[nodiscard]] int Get(std::string_view key, int default_value) const;
[[nodiscard]] float Get(std::string_view key, float default_value) const;
[[nodiscard]] std::string Get(const std::string& key, const std::string& default_value) const;
[[nodiscard]] int Get(const std::string& key, int default_value) const;
[[nodiscard]] float Get(const std::string& key, float default_value) const;
void Set(const std::string& key, std::string value);
void Set(const std::string& key, int value);
void Set(const std::string& key, float value);
[[nodiscard]] bool Has(std::string_view key) const;
[[nodiscard]] bool Has(const std::string& key) const;
void Erase(const std::string& key);
void Clear();

View File

@@ -167,6 +167,7 @@ void RestoreGlobalState(bool is_powered_on) {
// Core
values.use_multi_core.SetGlobal(true);
values.use_extended_memory_layout.SetGlobal(true);
// CPU
values.cpu_accuracy.SetGlobal(true);

View File

@@ -466,6 +466,7 @@ struct Values {
// Core
Setting<bool> use_multi_core{true, "use_multi_core"};
Setting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
// Cpu
RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,

View File

@@ -8,6 +8,7 @@
#include <map>
#include <memory>
#include <string>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Common::Telemetry {
@@ -28,7 +29,7 @@ struct VisitorInterface;
/**
* Interface class for telemetry data fields.
*/
class FieldInterface : NonCopyable {
class FieldInterface {
public:
virtual ~FieldInterface() = default;
@@ -52,14 +53,15 @@ public:
template <typename T>
class Field : public FieldInterface {
public:
YUZU_NON_COPYABLE(Field);
Field(FieldType type_, std::string name_, T value_)
: name(std::move(name_)), type(type_), value(std::move(value_)) {}
Field(const Field&) = default;
Field& operator=(const Field&) = default;
~Field() override = default;
Field(Field&&) = default;
Field& operator=(Field&& other) = default;
Field(Field&&) noexcept = default;
Field& operator=(Field&& other) noexcept = default;
void Accept(VisitorInterface& visitor) const override;
@@ -98,9 +100,15 @@ private:
/**
* Collection of data fields that have been logged.
*/
class FieldCollection final : NonCopyable {
class FieldCollection final {
public:
YUZU_NON_COPYABLE(FieldCollection);
FieldCollection() = default;
~FieldCollection() = default;
FieldCollection(FieldCollection&&) noexcept = default;
FieldCollection& operator=(FieldCollection&&) noexcept = default;
/**
* Accept method for the visitor pattern, visits each field in the collection.
@@ -133,7 +141,7 @@ private:
* Telemetry fields visitor interface class. A backend to log to a web service should implement
* this interface.
*/
struct VisitorInterface : NonCopyable {
struct VisitorInterface {
virtual ~VisitorInterface() = default;
virtual void Visit(const Field<bool>& field) = 0;
@@ -160,8 +168,11 @@ struct VisitorInterface : NonCopyable {
* Empty implementation of VisitorInterface that drops all fields. Used when a functional
* backend implementation is not available.
*/
struct NullVisitor : public VisitorInterface {
~NullVisitor() = default;
struct NullVisitor final : public VisitorInterface {
YUZU_NON_COPYABLE(NullVisitor);
NullVisitor() = default;
~NullVisitor() override = default;
void Visit(const Field<bool>& /*field*/) override {}
void Visit(const Field<double>& /*field*/) override {}

View File

@@ -1,23 +1,25 @@
// Copyright 2018 yuzu Emulator Project
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <bit>
#include <optional>
#include <random>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/tiny_mt.h"
#include "common/uuid.h"
namespace Common {
namespace {
bool IsHexDigit(char c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
constexpr size_t RawStringSize = sizeof(UUID) * 2;
constexpr size_t FormattedStringSize = RawStringSize + 4;
u8 HexCharToByte(char c) {
std::optional<u8> HexCharToByte(char c) {
if (c >= '0' && c <= '9') {
return static_cast<u8>(c - '0');
}
@@ -28,60 +30,184 @@ u8 HexCharToByte(char c) {
return static_cast<u8>(c - 'A' + 10);
}
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
return u8{0};
return std::nullopt;
}
std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) {
std::array<u8, 0x10> uuid;
for (size_t i = 0; i < RawStringSize; i += 2) {
const auto upper = HexCharToByte(raw_string[i]);
const auto lower = HexCharToByte(raw_string[i + 1]);
if (!upper || !lower) {
return {};
}
uuid[i / 2] = static_cast<u8>((*upper << 4) | *lower);
}
return uuid;
}
std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) {
std::array<u8, 0x10> uuid;
size_t i = 0;
// Process the first 8 characters.
const auto* str = formatted_string.data();
for (; i < 4; ++i) {
const auto upper = HexCharToByte(*(str++));
const auto lower = HexCharToByte(*(str++));
if (!upper || !lower) {
return {};
}
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
}
// Process the next 4 characters.
++str;
for (; i < 6; ++i) {
const auto upper = HexCharToByte(*(str++));
const auto lower = HexCharToByte(*(str++));
if (!upper || !lower) {
return {};
}
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
}
// Process the next 4 characters.
++str;
for (; i < 8; ++i) {
const auto upper = HexCharToByte(*(str++));
const auto lower = HexCharToByte(*(str++));
if (!upper || !lower) {
return {};
}
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
}
// Process the next 4 characters.
++str;
for (; i < 10; ++i) {
const auto upper = HexCharToByte(*(str++));
const auto lower = HexCharToByte(*(str++));
if (!upper || !lower) {
return {};
}
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
}
// Process the last 12 characters.
++str;
for (; i < 16; ++i) {
const auto upper = HexCharToByte(*(str++));
const auto lower = HexCharToByte(*(str++));
if (!upper || !lower) {
return {};
}
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
}
return uuid;
}
std::array<u8, 0x10> ConstructUUID(std::string_view uuid_string) {
const auto length = uuid_string.length();
if (length == 0) {
return {};
}
// Check if the input string contains 32 hexadecimal characters.
if (length == RawStringSize) {
return ConstructFromRawString(uuid_string);
}
// Check if the input string has the length of a RFC 4122 formatted UUID string.
if (length == FormattedStringSize) {
return ConstructFromFormattedString(uuid_string);
}
ASSERT_MSG(false, "UUID string has an invalid length of {} characters!", length);
return {};
}
} // Anonymous namespace
u128 HexStringToU128(std::string_view hex_string) {
const size_t length = hex_string.length();
UUID::UUID(std::string_view uuid_string) : uuid{ConstructUUID(uuid_string)} {}
// Detect "0x" prefix.
const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x';
const size_t offset = has_0x_prefix ? 2 : 0;
// Check length.
if (length > 32 + offset) {
ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!");
return INVALID_UUID;
}
u64 lo = 0;
u64 hi = 0;
for (size_t i = 0; i < length - offset; ++i) {
const char c = hex_string[length - 1 - i];
if (!IsHexDigit(c)) {
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
return INVALID_UUID;
}
if (i < 16) {
lo |= u64{HexCharToByte(c)} << (i * 4);
}
if (i >= 16) {
hi |= u64{HexCharToByte(c)} << ((i - 16) * 4);
}
}
return u128{lo, hi};
std::string UUID::RawString() const {
return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}"
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
uuid[15]);
}
UUID UUID::Generate() {
std::string UUID::FormattedString() const {
return fmt::format("{:02x}{:02x}{:02x}{:02x}"
"-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-"
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
uuid[15]);
}
size_t UUID::Hash() const noexcept {
u64 upper_hash;
u64 lower_hash;
std::memcpy(&upper_hash, uuid.data(), sizeof(u64));
std::memcpy(&lower_hash, uuid.data() + sizeof(u64), sizeof(u64));
return upper_hash ^ std::rotl(lower_hash, 1);
}
u128 UUID::AsU128() const {
u128 uuid_old;
std::memcpy(&uuid_old, uuid.data(), sizeof(UUID));
return uuid_old;
}
UUID UUID::MakeRandom() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return UUID{distribution(gen), distribution(gen)};
return MakeRandomWithSeed(device());
}
std::string UUID::Format() const {
return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]);
UUID UUID::MakeRandomWithSeed(u32 seed) {
// Create and initialize our RNG.
TinyMT rng;
rng.Initialize(seed);
UUID uuid;
// Populate the UUID with random bytes.
rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(UUID));
return uuid;
}
std::string UUID::FormatSwitch() const {
std::array<u8, 16> s{};
std::memcpy(s.data(), uuid.data(), sizeof(u128));
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
":02x}{:02x}{:02x}{:02x}{:02x}",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
s[12], s[13], s[14], s[15]);
UUID UUID::MakeRandomRFC4122V4() {
auto uuid = MakeRandom();
// According to Proposed Standard RFC 4122 Section 4.4, we must:
// 1. Set the two most significant bits (bits 6 and 7) of the
// clock_seq_hi_and_reserved to zero and one, respectively.
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
// 2. Set the four most significant bits (bits 12 through 15) of the
// time_hi_and_version field to the 4-bit version number from Section 4.1.3.
uuid.uuid[6] = 0x40 | (uuid.uuid[6] & 0xF);
return uuid;
}
} // namespace Common

View File

@@ -1,9 +1,11 @@
// Copyright 2018 yuzu Emulator Project
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <functional>
#include <string>
#include <string_view>
@@ -11,69 +13,119 @@
namespace Common {
constexpr u128 INVALID_UUID{{0, 0}};
/**
* Converts a hex string to a 128-bit unsigned integer.
*
* The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix.
*
* This function will assert and return INVALID_UUID under the following conditions:
* - If the hex string is more than 32 characters long
* - If the hex string contains non-hexadecimal characters
*
* @param hex_string Hexadecimal string
*
* @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise.
*/
[[nodiscard]] u128 HexStringToU128(std::string_view hex_string);
struct UUID {
// UUIDs which are 0 are considered invalid!
u128 uuid;
UUID() = default;
constexpr explicit UUID(const u128& id) : uuid{id} {}
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
explicit UUID(std::string_view hex_string) {
uuid = HexStringToU128(hex_string);
std::array<u8, 0x10> uuid{};
/// Constructs an invalid UUID.
constexpr UUID() = default;
/// Constructs a UUID from a reference to a 128 bit array.
constexpr explicit UUID(const std::array<u8, 16>& uuid_) : uuid{uuid_} {}
/**
* Constructs a UUID from either:
* 1. A 32 hexadecimal character string representing the bytes of the UUID
* 2. A RFC 4122 formatted UUID string, in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
*
* The input string may contain uppercase or lowercase characters, but they must:
* 1. Contain valid hexadecimal characters (0-9, a-f, A-F)
* 2. Not contain the "0x" hexadecimal prefix
*
* Should the input string not meet the above requirements,
* an assert will be triggered and an invalid UUID is set instead.
*/
explicit UUID(std::string_view uuid_string);
~UUID() = default;
constexpr UUID(const UUID&) noexcept = default;
constexpr UUID(UUID&&) noexcept = default;
constexpr UUID& operator=(const UUID&) noexcept = default;
constexpr UUID& operator=(UUID&&) noexcept = default;
/**
* Returns whether the stored UUID is valid or not.
*
* @returns True if the stored UUID is valid, false otherwise.
*/
constexpr bool IsValid() const {
return uuid != std::array<u8, 0x10>{};
}
[[nodiscard]] constexpr explicit operator bool() const {
return uuid != INVALID_UUID;
/**
* Returns whether the stored UUID is invalid or not.
*
* @returns True if the stored UUID is invalid, false otherwise.
*/
constexpr bool IsInvalid() const {
return !IsValid();
}
[[nodiscard]] constexpr bool operator==(const UUID& rhs) const {
return uuid == rhs.uuid;
/**
* Returns a 32 hexadecimal character string representing the bytes of the UUID.
*
* @returns A 32 hexadecimal character string of the UUID.
*/
std::string RawString() const;
/**
* Returns a RFC 4122 formatted UUID string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
*
* @returns A RFC 4122 formatted UUID string.
*/
std::string FormattedString() const;
/**
* Returns a 64-bit hash of the UUID for use in hash table data structures.
*
* @returns A 64-bit hash of the UUID.
*/
size_t Hash() const noexcept;
/// DO NOT USE. Copies the contents of the UUID into a u128.
u128 AsU128() const;
/**
* Creates a default UUID "yuzu Default UID".
*
* @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID".
*/
static constexpr UUID MakeDefault() {
return UUID{
{'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'},
};
}
[[nodiscard]] constexpr bool operator!=(const UUID& rhs) const {
return !operator==(rhs);
}
/**
* Creates a random UUID.
*
* @returns A random UUID.
*/
static UUID MakeRandom();
// TODO(ogniK): Properly generate uuids based on RFC-4122
[[nodiscard]] static UUID Generate();
/**
* Creates a random UUID with a seed.
*
* @param seed A seed to initialize the Mersenne-Twister RNG
*
* @returns A random UUID.
*/
static UUID MakeRandomWithSeed(u32 seed);
// Set the UUID to {0,0} to be considered an invalid user
constexpr void Invalidate() {
uuid = INVALID_UUID;
}
/**
* Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant.
*
* @returns A random UUID that is RFC 4122 Version 4 compliant.
*/
static UUID MakeRandomRFC4122V4();
[[nodiscard]] constexpr bool IsInvalid() const {
return uuid == INVALID_UUID;
}
[[nodiscard]] constexpr bool IsValid() const {
return !IsInvalid();
}
// TODO(ogniK): Properly generate a Nintendo ID
[[nodiscard]] constexpr u64 GetNintendoID() const {
return uuid[0];
}
[[nodiscard]] std::string Format() const;
[[nodiscard]] std::string FormatSwitch() const;
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size.");
/// An invalid UUID. This UUID has all its bytes set to 0.
constexpr UUID InvalidUUID = {};
} // namespace Common
@@ -82,7 +134,7 @@ namespace std {
template <>
struct hash<Common::UUID> {
size_t operator()(const Common::UUID& uuid) const noexcept {
return uuid.uuid[1] ^ uuid.uuid[0];
return uuid.Hash();
}
};

View File

@@ -65,16 +65,20 @@ private:
#ifdef ARCHITECTURE_x86_64
std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
u32 emulated_clock_frequency) {
std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
u64 emulated_clock_frequency) {
const auto& caps = GetCPUCaps();
u64 rtsc_frequency = 0;
if (caps.invariant_tsc) {
rtsc_frequency = EstimateRDTSCFrequency();
}
// Fallback to StandardWallClock if rtsc period is higher than a nano second
if (rtsc_frequency <= 1000000000) {
// Fallback to StandardWallClock if the hardware TSC does not have the precision greater than:
// - A nanosecond
// - The emulated CPU frequency
// - The emulated clock counter frequency (CNTFRQ)
if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency ||
rtsc_frequency <= emulated_clock_frequency) {
return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
emulated_clock_frequency);
} else {
@@ -85,8 +89,8 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
#else
std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
u32 emulated_clock_frequency) {
std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
u64 emulated_clock_frequency) {
return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
}

View File

@@ -13,6 +13,10 @@ namespace Common {
class WallClock {
public:
static constexpr u64 NS_RATIO = 1'000'000'000;
static constexpr u64 US_RATIO = 1'000'000;
static constexpr u64 MS_RATIO = 1'000;
virtual ~WallClock() = default;
/// Returns current wall time in nanoseconds
@@ -49,7 +53,7 @@ private:
bool is_native;
};
[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
u32 emulated_clock_frequency);
[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
u64 emulated_clock_frequency);
} // namespace Common

View File

@@ -47,9 +47,9 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
_mm_mfence();
time_point.inner.last_measure = __rdtsc();
time_point.inner.accumulated_ticks = 0U;
ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency);
us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency);
ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency);
ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency);
clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
}

View File

@@ -152,6 +152,7 @@ add_library(core STATIC
hle/api_version.h
hle/ipc.h
hle/ipc_helpers.h
hle/kernel/board/nintendo/nx/k_memory_layout.h
hle/kernel/board/nintendo/nx/k_system_control.cpp
hle/kernel/board/nintendo/nx/k_system_control.h
hle/kernel/board/nintendo/nx/secure_monitor.h
@@ -164,6 +165,7 @@ add_library(core STATIC
hle/kernel/hle_ipc.h
hle/kernel/init/init_slab_setup.cpp
hle/kernel/init/init_slab_setup.h
hle/kernel/initial_process.h
hle/kernel/k_address_arbiter.cpp
hle/kernel/k_address_arbiter.h
hle/kernel/k_address_space_info.cpp
@@ -467,6 +469,8 @@ add_library(core STATIC
hle/service/mii/types.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
hle/service/mnpp/mnpp_app.cpp
hle/service/mnpp/mnpp_app.h
hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h
hle/service/nfc/nfc.cpp

View File

@@ -6,6 +6,7 @@
#include <array>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hardware_properties.h"
@@ -24,8 +25,11 @@ class CPUInterruptHandler;
using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
/// Generic ARMv8 CPU interface
class ARM_Interface : NonCopyable {
class ARM_Interface {
public:
YUZU_NON_COPYABLE(ARM_Interface);
YUZU_NON_MOVEABLE(ARM_Interface);
explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
bool uses_wall_clock_)
: system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{

View File

@@ -28,7 +28,9 @@
#include "core/file_sys/vfs_real.h"
#include "core/hardware_interrupt_manager.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
@@ -252,9 +254,16 @@ struct System::Impl {
}
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
// Create a resource limit for the process.
const auto physical_memory_size =
kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size);
// Create the process.
auto main_process = Kernel::KProcess::Create(system.Kernel());
ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
Kernel::KProcess::ProcessType::Userland)
Kernel::KProcess::ProcessType::Userland, resource_limit)
.IsSuccess());
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
if (load_result != Loader::ResultStatus::Success) {

View File

@@ -3,10 +3,13 @@
// Refer to the license.txt file included.
#include "core/device_memory.h"
#include "hle/kernel/board/nintendo/nx/k_system_control.h"
namespace Core {
DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size, 1ULL << 39} {}
DeviceMemory::DeviceMemory()
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
1ULL << 39} {}
DeviceMemory::~DeviceMemory() = default;
} // namespace Core

View File

@@ -12,12 +12,8 @@ namespace Core {
namespace DramMemoryMap {
enum : u64 {
Base = 0x80000000ULL,
Size = 0x100000000ULL,
End = Base + Size,
KernelReserveBase = Base + 0x60000,
SlabHeapBase = KernelReserveBase + 0x85000,
SlapHeapSize = 0xa21000,
SlabHeapEnd = SlabHeapBase + SlapHeapSize,
};
}; // namespace DramMemoryMap

View File

@@ -128,15 +128,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
if (exefs == nullptr)
return exefs;
if (Settings::values.dump_exefs) {
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
VfsRawCopyD(exefs, exefs_dir);
}
}
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
@@ -179,6 +170,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
}
if (Settings::values.dump_exefs) {
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
VfsRawCopyD(exefs, exefs_dir);
}
}
return exefs;
}

View File

@@ -12,6 +12,7 @@
#include <type_traits>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
@@ -29,8 +30,11 @@ enum class VfsEntryType {
// A class representing an abstract filesystem. A default implementation given the root VirtualDir
// is provided for convenience, but if the Vfs implementation has any additional state or
// functionality, they will need to override.
class VfsFilesystem : NonCopyable {
class VfsFilesystem {
public:
YUZU_NON_COPYABLE(VfsFilesystem);
YUZU_NON_MOVEABLE(VfsFilesystem);
explicit VfsFilesystem(VirtualDir root);
virtual ~VfsFilesystem();
@@ -77,8 +81,12 @@ protected:
};
// A class representing a file in an abstract filesystem.
class VfsFile : NonCopyable {
class VfsFile {
public:
YUZU_NON_COPYABLE(VfsFile);
YUZU_NON_MOVEABLE(VfsFile);
VfsFile() = default;
virtual ~VfsFile();
// Retrieves the file name.
@@ -176,8 +184,12 @@ public:
};
// A class representing a directory in an abstract filesystem.
class VfsDirectory : NonCopyable {
class VfsDirectory {
public:
YUZU_NON_COPYABLE(VfsDirectory);
YUZU_NON_MOVEABLE(VfsDirectory);
VfsDirectory() = default;
virtual ~VfsDirectory();
// Retrives the file located at path as if the current directory was root. Returns nullptr if

View File

@@ -13,8 +13,7 @@ ProfileSelectApplet::~ProfileSelectApplet() = default;
void DefaultProfileSelectApplet::SelectProfile(
std::function<void(std::optional<Common::UUID>)> callback) const {
Service::Account::ProfileManager manager;
callback(manager.GetUser(Settings::values.current_user.GetValue())
.value_or(Common::UUID{Common::INVALID_UUID}));
callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
}

View File

@@ -10,6 +10,7 @@
#include <mutex>
#include <unordered_map>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"

View File

@@ -269,7 +269,8 @@ void EmulatedController::ReloadInput() {
}
// Use a common UUID for TAS
const auto tas_uuid = Common::UUID{0x0, 0x7A5};
static constexpr Common::UUID TAS_UUID = Common::UUID{
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
// Register TAS devices. No need to force update
for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
@@ -278,8 +279,8 @@ void EmulatedController::ReloadInput() {
}
tas_button_devices[index]->SetCallback({
.on_change =
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
SetButton(callback, index, tas_uuid);
[this, index](const Common::Input::CallbackStatus& callback) {
SetButton(callback, index, TAS_UUID);
},
});
}
@@ -290,8 +291,8 @@ void EmulatedController::ReloadInput() {
}
tas_stick_devices[index]->SetCallback({
.on_change =
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
SetStick(callback, index, tas_uuid);
[this, index](const Common::Input::CallbackStatus& callback) {
SetStick(callback, index, TAS_UUID);
},
});
}
@@ -884,6 +885,12 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
}
bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
}
void EmulatedController::SetLedPattern() {
for (auto& device : output_devices) {
if (!device) {

View File

@@ -13,8 +13,6 @@
#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"
#include "common/point.h"
#include "common/quaternion.h"
#include "common/settings.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
@@ -301,16 +299,23 @@ public:
/**
* Sends a specific vibration to the output device
* @return returns true if vibration had no errors
* @return true if vibration had no errors
*/
bool SetVibration(std::size_t device_index, VibrationValue vibration);
/**
* Sends a small vibration to the output device
* @return returns true if SetVibration was successfull
* @return true if SetVibration was successfull
*/
bool TestVibration(std::size_t device_index);
/**
* Sets the desired data to be polled from a controller
* @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
* @return true if SetPollingMode was successfull
*/
bool SetPollingMode(Common::Input::PollingMode polling_mode);
/// Returns the led pattern corresponding to this emulated controller
LedPattern GetLedPattern() const;

View File

@@ -6,6 +6,7 @@
#include <memory>
#include "common/common_funcs.h"
#include "core/hid/hid_types.h"
namespace Core::HID {

View File

@@ -28,7 +28,7 @@ Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackSta
if (value > 0.8f) {
battery = Common::Input::BatteryLevel::Full;
}
if (value >= 1.0f) {
if (value >= 0.95f) {
battery = Common::Input::BatteryLevel::Charging;
}
break;

View File

@@ -404,6 +404,11 @@ inline s32 RequestParser::Pop() {
return static_cast<s32>(Pop<u32>());
}
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
template <typename T>
void RequestParser::PopRaw(T& value) {
static_assert(std::is_trivially_copyable_v<T>,
@@ -411,6 +416,9 @@ void RequestParser::PopRaw(T& value) {
std::memcpy(&value, cmdbuf + index, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
template <typename T>
T RequestParser::PopRaw() {

View File

@@ -0,0 +1,13 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Kernel {
constexpr inline PAddr MainMemoryAddress = 0x80000000;
} // namespace Kernel

View File

@@ -5,6 +5,7 @@
#include <random>
#include "common/literals.h"
#include "common/settings.h"
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
@@ -28,33 +29,20 @@ namespace {
using namespace Common::Literals;
u32 GetMemoryModeForInit() {
return 0x01;
}
u32 GetMemorySizeForInit() {
return 0;
return Settings::values.use_extended_memory_layout ? Smc::MemorySize_6GB : Smc::MemorySize_4GB;
}
Smc::MemoryArrangement GetMemoryArrangeForInit() {
switch (GetMemoryModeForInit() & 0x3F) {
case 0x01:
default:
return Smc::MemoryArrangement_4GB;
case 0x02:
return Smc::MemoryArrangement_4GBForAppletDev;
case 0x03:
return Smc::MemoryArrangement_4GBForSystemDev;
case 0x11:
return Smc::MemoryArrangement_6GB;
case 0x12:
return Smc::MemoryArrangement_6GBForAppletDev;
case 0x21:
return Smc::MemoryArrangement_8GB;
}
return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_6GB
: Smc::MemoryArrangement_4GB;
}
} // namespace
size_t KSystemControl::Init::GetRealMemorySize() {
return GetIntendedMemorySize();
}
// Initialization.
size_t KSystemControl::Init::GetIntendedMemorySize() {
switch (GetMemorySizeForInit()) {
@@ -69,7 +57,13 @@ size_t KSystemControl::Init::GetIntendedMemorySize() {
}
PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) {
return base_address;
const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize();
const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
if (intended_dram_size * 2 < real_dram_size) {
return base_address;
} else {
return base_address + ((real_dram_size - intended_dram_size) / 2);
}
}
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {

View File

@@ -13,6 +13,7 @@ public:
class Init {
public:
// Initialization.
static std::size_t GetRealMemorySize();
static std::size_t GetIntendedMemorySize();
static PAddr GetKernelPhysicalBaseAddress(u64 base_address);
static bool ShouldIncreaseThreadResourceLimit();

View File

@@ -0,0 +1,23 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "common/literals.h"
#include "core/hle/kernel/board/nintendo/nx/k_memory_layout.h"
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
namespace Kernel {
using namespace Common::Literals;
constexpr std::size_t InitialProcessBinarySizeMax = 12_MiB;
static inline PAddr GetInitialProcessBinaryPhysicalAddress() {
return Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetKernelPhysicalBaseAddress(
MainMemoryAddress);
}
} // namespace Kernel

View File

@@ -20,8 +20,6 @@ class KernelCore;
class KProcess;
#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
YUZU_NON_COPYABLE(CLASS); \
YUZU_NON_MOVEABLE(CLASS); \
\
private: \
friend class ::Kernel::KClassTokenGenerator; \
@@ -32,6 +30,9 @@ private:
} \
\
public: \
YUZU_NON_COPYABLE(CLASS); \
YUZU_NON_MOVEABLE(CLASS); \
\
using BaseClass = BASE_CLASS; \
static constexpr TypeObj GetStaticTypeObj() { \
constexpr ClassTokenType Token = ClassToken(); \
@@ -224,9 +225,9 @@ private:
template <typename T>
class KScopedAutoObject {
public:
YUZU_NON_COPYABLE(KScopedAutoObject);
public:
constexpr KScopedAutoObject() = default;
constexpr KScopedAutoObject(T* o) : m_obj(o) {

View File

@@ -16,13 +16,12 @@ class KernelCore;
class KProcess;
class KAutoObjectWithListContainer {
public:
YUZU_NON_COPYABLE(KAutoObjectWithListContainer);
YUZU_NON_MOVEABLE(KAutoObjectWithListContainer);
public:
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
public:
class ListAccessor : public KScopedLightLock {
public:
explicit ListAccessor(KAutoObjectWithListContainer* container)
@@ -48,7 +47,6 @@ public:
friend class ListAccessor;
public:
KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
void Initialize() {}

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/alignment.h"
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
@@ -28,8 +29,7 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
auto& page_table = m_owner->PageTable();
// Construct the page group.
KMemoryInfo kBlockInfo = page_table.QueryInfo(addr);
m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize));
// Lock the memory.
R_TRY(page_table.LockForCodeMemory(addr, size))
@@ -143,4 +143,4 @@ ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
return ResultSuccess;
}
} // namespace Kernel
} // namespace Kernel

View File

@@ -14,7 +14,7 @@ KEvent::KEvent(KernelCore& kernel_)
KEvent::~KEvent() = default;
void KEvent::Initialize(std::string&& name_) {
void KEvent::Initialize(std::string&& name_, KProcess* owner_) {
// Increment reference count.
// Because reference count is one on creation, this will result
// in a reference count of two. Thus, when both readable and
@@ -30,10 +30,8 @@ void KEvent::Initialize(std::string&& name_) {
writable_event.Initialize(this, name_ + ":Writable");
// Set our owner process.
owner = kernel.CurrentProcess();
if (owner) {
owner->Open();
}
owner = owner_;
owner->Open();
// Mark initialized.
name = std::move(name_);
@@ -47,10 +45,8 @@ void KEvent::Finalize() {
void KEvent::PostDestroy(uintptr_t arg) {
// Release the event count resource the owner process holds.
KProcess* owner = reinterpret_cast<KProcess*>(arg);
if (owner) {
owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
owner->Close();
}
owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
owner->Close();
}
} // namespace Kernel

View File

@@ -22,7 +22,7 @@ public:
explicit KEvent(KernelCore& kernel_);
~KEvent() override;
void Initialize(std::string&& name);
void Initialize(std::string&& name, KProcess* owner_);
void Finalize() override;

View File

@@ -22,13 +22,12 @@ namespace Kernel {
class KernelCore;
class KHandleTable {
public:
YUZU_NON_COPYABLE(KHandleTable);
YUZU_NON_MOVEABLE(KHandleTable);
public:
static constexpr size_t MaxTableSize = 1024;
public:
explicit KHandleTable(KernelCore& kernel_);
~KHandleTable();

View File

@@ -173,6 +173,10 @@ public:
return Dereference(FindVirtualLinear(address));
}
const KMemoryRegion& GetPhysicalLinearRegion(PAddr address) const {
return Dereference(FindPhysicalLinear(address));
}
const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const {
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer);
}

View File

@@ -10,189 +10,412 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hle/kernel/initial_process.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/memory.h"
namespace Kernel {
KMemoryManager::KMemoryManager(Core::System& system_) : system{system_} {}
namespace {
std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
const auto size{end_address - start_address};
// Calculate metadata sizes
const auto ref_count_size{(size / PageSize) * sizeof(u16)};
const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)};
const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)};
const auto page_heap_size{KPageHeap::CalculateManagementOverheadSize(size)};
const auto total_metadata_size{manager_size + page_heap_size};
ASSERT(manager_size <= total_metadata_size);
ASSERT(Common::IsAligned(total_metadata_size, PageSize));
// Setup region
pool = new_pool;
// Initialize the manager's KPageHeap
heap.Initialize(start_address, size, page_heap_size);
// Free the memory to the heap
heap.Free(start_address, size / PageSize);
// Update the heap's used size
heap.UpdateUsedSize();
return total_metadata_size;
constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
if ((type | KMemoryRegionType_DramApplicationPool) == type) {
return KMemoryManager::Pool::Application;
} else if ((type | KMemoryRegionType_DramAppletPool) == type) {
return KMemoryManager::Pool::Applet;
} else if ((type | KMemoryRegionType_DramSystemPool) == type) {
return KMemoryManager::Pool::System;
} else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
return KMemoryManager::Pool::SystemNonSecure;
} else {
UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool");
return {};
}
}
void KMemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) {
ASSERT(pool < Pool::Count);
managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address);
} // namespace
KMemoryManager::KMemoryManager(Core::System& system_)
: system{system_}, pool_locks{
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
} {}
void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) {
// Clear the management region to zero.
const VAddr management_region_end = management_region + management_region_size;
// Reset our manager count.
num_managers = 0;
// Traverse the virtual memory layout tree, initializing each manager as appropriate.
while (num_managers != MaxManagerCount) {
// Locate the region that should initialize the current manager.
PAddr region_address = 0;
size_t region_size = 0;
Pool region_pool = Pool::Count;
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
// We only care about regions that we need to create managers for.
if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
continue;
}
// We want to initialize the managers in order.
if (it.GetAttributes() != num_managers) {
continue;
}
const PAddr cur_start = it.GetAddress();
const PAddr cur_end = it.GetEndAddress();
// Validate the region.
ASSERT(cur_end != 0);
ASSERT(cur_start != 0);
ASSERT(it.GetSize() > 0);
// Update the region's extents.
if (region_address == 0) {
region_address = cur_start;
region_size = it.GetSize();
region_pool = GetPoolFromMemoryRegionType(it.GetType());
} else {
ASSERT(cur_start == region_address + region_size);
// Update the size.
region_size = cur_end - region_address;
ASSERT(GetPoolFromMemoryRegionType(it.GetType()) == region_pool);
}
}
// If we didn't find a region, we're done.
if (region_size == 0) {
break;
}
// Initialize a new manager for the region.
Impl* manager = std::addressof(managers[num_managers++]);
ASSERT(num_managers <= managers.size());
const size_t cur_size = manager->Initialize(region_address, region_size, management_region,
management_region_end, region_pool);
management_region += cur_size;
ASSERT(management_region <= management_region_end);
// Insert the manager into the pool list.
const auto region_pool_index = static_cast<u32>(region_pool);
if (pool_managers_tail[region_pool_index] == nullptr) {
pool_managers_head[region_pool_index] = manager;
} else {
pool_managers_tail[region_pool_index]->SetNext(manager);
manager->SetPrev(pool_managers_tail[region_pool_index]);
}
pool_managers_tail[region_pool_index] = manager;
}
// Free each region to its corresponding heap.
size_t reserved_sizes[MaxManagerCount] = {};
const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress();
const PAddr ini_end = ini_start + InitialProcessBinarySizeMax;
const PAddr ini_last = ini_end - 1;
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
// Get the manager for the region.
auto index = it.GetAttributes();
auto& manager = managers[index];
const PAddr cur_start = it.GetAddress();
const PAddr cur_last = it.GetLastAddress();
const PAddr cur_end = it.GetEndAddress();
if (cur_start <= ini_start && ini_last <= cur_last) {
// Free memory before the ini to the heap.
if (cur_start != ini_start) {
manager.Free(cur_start, (ini_start - cur_start) / PageSize);
}
// Open/reserve the ini memory.
manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
// Free memory after the ini to the heap.
if (ini_last != cur_last) {
ASSERT(cur_end != 0);
manager.Free(ini_end, cur_end - ini_end);
}
} else {
// Ensure there's no partial overlap with the ini image.
if (cur_start <= ini_last) {
ASSERT(cur_last < ini_start);
} else {
// Otherwise, check the region for general validity.
ASSERT(cur_end != 0);
}
// Free the memory to the heap.
manager.Free(cur_start, it.GetSize() / PageSize);
}
}
}
// Update the used size for all managers.
for (size_t i = 0; i < num_managers; ++i) {
managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
}
}
VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size_t align_pages,
u32 option) {
// Early return if we're allocating no pages
PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) {
// Early return if we're allocating no pages.
if (num_pages == 0) {
return {};
return 0;
}
// Lock the pool that we're allocating from
// Lock the pool that we're allocating from.
const auto [pool, dir] = DecodeOption(option);
const auto pool_index{static_cast<std::size_t>(pool)};
std::lock_guard lock{pool_locks[pool_index]};
KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]);
// Choose a heap based on our page size request
const s32 heap_index{KPageHeap::GetAlignedBlockIndex(num_pages, align_pages)};
// Choose a heap based on our page size request.
const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages);
// Loop, trying to iterate from each block
// TODO (bunnei): Support multiple managers
Impl& chosen_manager{managers[pool_index]};
VAddr allocated_block{chosen_manager.AllocateBlock(heap_index, false)};
// If we failed to allocate, quit now
if (!allocated_block) {
return {};
// Loop, trying to iterate from each block.
Impl* chosen_manager = nullptr;
PAddr allocated_block = 0;
for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr;
chosen_manager = this->GetNextManager(chosen_manager, dir)) {
allocated_block = chosen_manager->AllocateBlock(heap_index, true);
if (allocated_block != 0) {
break;
}
}
// If we allocated more than we need, free some
const auto allocated_pages{KPageHeap::GetBlockNumPages(heap_index)};
// If we failed to allocate, quit now.
if (allocated_block == 0) {
return 0;
}
// If we allocated more than we need, free some.
const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index);
if (allocated_pages > num_pages) {
chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
}
// Open the first reference to the pages.
chosen_manager->OpenFirst(allocated_block, num_pages);
return allocated_block;
}
ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir, u32 heap_fill_value) {
ASSERT(page_list.GetNumPages() == 0);
ResultCode KMemoryManager::AllocatePageGroupImpl(KPageLinkedList* out, size_t num_pages, Pool pool,
Direction dir, bool random) {
// Choose a heap based on our page size request.
const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
R_UNLESS(0 <= heap_index, ResultOutOfMemory);
// Early return if we're allocating no pages
if (num_pages == 0) {
return ResultSuccess;
}
// Lock the pool that we're allocating from
const auto pool_index{static_cast<std::size_t>(pool)};
std::lock_guard lock{pool_locks[pool_index]};
// Choose a heap based on our page size request
const s32 heap_index{KPageHeap::GetBlockIndex(num_pages)};
if (heap_index < 0) {
return ResultOutOfMemory;
}
// TODO (bunnei): Support multiple managers
Impl& chosen_manager{managers[pool_index]};
// Ensure that we don't leave anything un-freed
auto group_guard = detail::ScopeExit([&] {
for (const auto& it : page_list.Nodes()) {
const auto min_num_pages{std::min<size_t>(
it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
chosen_manager.Free(it.GetAddress(), min_num_pages);
// Ensure that we don't leave anything un-freed.
auto group_guard = SCOPE_GUARD({
for (const auto& it : out->Nodes()) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress());
const size_t num_pages_to_free =
std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
manager.Free(it.GetAddress(), num_pages_to_free);
}
});
// Keep allocating until we've allocated all our pages
for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) {
const auto pages_per_alloc{KPageHeap::GetBlockNumPages(index)};
while (num_pages >= pages_per_alloc) {
// Allocate a block
VAddr allocated_block{chosen_manager.AllocateBlock(index, false)};
if (!allocated_block) {
break;
}
// Safely add it to our group
{
auto block_guard = detail::ScopeExit(
[&] { chosen_manager.Free(allocated_block, pages_per_alloc); });
if (const ResultCode result{page_list.AddBlock(allocated_block, pages_per_alloc)};
result.IsError()) {
return result;
// Keep allocating until we've allocated all our pages.
for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) {
const size_t pages_per_alloc = KPageHeap::GetBlockNumPages(index);
for (Impl* cur_manager = this->GetFirstManager(pool, dir); cur_manager != nullptr;
cur_manager = this->GetNextManager(cur_manager, dir)) {
while (num_pages >= pages_per_alloc) {
// Allocate a block.
PAddr allocated_block = cur_manager->AllocateBlock(index, random);
if (allocated_block == 0) {
break;
}
block_guard.Cancel();
}
// Safely add it to our group.
{
auto block_guard =
SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); });
R_TRY(out->AddBlock(allocated_block, pages_per_alloc));
block_guard.Cancel();
}
num_pages -= pages_per_alloc;
num_pages -= pages_per_alloc;
}
}
}
// Clear allocated memory.
for (const auto& it : page_list.Nodes()) {
std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value,
it.GetSize());
}
// Only succeed if we allocated as many pages as we wanted
if (num_pages) {
return ResultOutOfMemory;
}
// Only succeed if we allocated as many pages as we wanted.
R_UNLESS(num_pages == 0, ResultOutOfMemory);
// We succeeded!
group_guard.Cancel();
return ResultSuccess;
}
ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir, u32 heap_fill_value) {
// Early return if we're freeing no pages
if (!num_pages) {
return ResultSuccess;
}
ResultCode KMemoryManager::AllocateAndOpen(KPageLinkedList* out, size_t num_pages, u32 option) {
ASSERT(out != nullptr);
ASSERT(out->GetNumPages() == 0);
// Lock the pool that we're freeing from
const auto pool_index{static_cast<std::size_t>(pool)};
std::lock_guard lock{pool_locks[pool_index]};
// Early return if we're allocating no pages.
R_SUCCEED_IF(num_pages == 0);
// TODO (bunnei): Support multiple managers
Impl& chosen_manager{managers[pool_index]};
// Lock the pool that we're allocating from.
const auto [pool, dir] = DecodeOption(option);
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
// Free all of the pages
for (const auto& it : page_list.Nodes()) {
const auto min_num_pages{std::min<size_t>(
it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
chosen_manager.Free(it.GetAddress(), min_num_pages);
// Allocate the page group.
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
// Open the first reference to the pages.
for (const auto& block : out->Nodes()) {
PAddr cur_address = block.GetAddress();
size_t remaining_pages = block.GetNumPages();
while (remaining_pages > 0) {
// Get the manager for the current address.
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
// Process part or all of the block.
const size_t cur_pages =
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
manager.OpenFirst(cur_address, cur_pages);
// Advance.
cur_address += cur_pages * PageSize;
remaining_pages -= cur_pages;
}
}
return ResultSuccess;
}
std::size_t KMemoryManager::Impl::CalculateManagementOverheadSize(std::size_t region_size) {
const std::size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
const std::size_t optimize_map_size =
ResultCode KMemoryManager::AllocateAndOpenForProcess(KPageLinkedList* out, size_t num_pages,
u32 option, u64 process_id, u8 fill_pattern) {
ASSERT(out != nullptr);
ASSERT(out->GetNumPages() == 0);
// Decode the option.
const auto [pool, dir] = DecodeOption(option);
// Allocate the memory.
{
// Lock the pool that we're allocating from.
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
// Allocate the page group.
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
// Open the first reference to the pages.
for (const auto& block : out->Nodes()) {
PAddr cur_address = block.GetAddress();
size_t remaining_pages = block.GetNumPages();
while (remaining_pages > 0) {
// Get the manager for the current address.
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
// Process part or all of the block.
const size_t cur_pages =
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
manager.OpenFirst(cur_address, cur_pages);
// Advance.
cur_address += cur_pages * PageSize;
remaining_pages -= cur_pages;
}
}
}
// Set all the allocated memory.
for (const auto& block : out->Nodes()) {
std::memset(system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern,
block.GetSize());
}
return ResultSuccess;
}
void KMemoryManager::Open(PAddr address, size_t num_pages) {
// Repeatedly open references until we've done so for all pages.
while (num_pages) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
{
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
manager.Open(address, cur_pages);
}
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
void KMemoryManager::Close(PAddr address, size_t num_pages) {
// Repeatedly close references until we've done so for all pages.
while (num_pages) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
{
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
manager.Close(address, cur_pages);
}
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
void KMemoryManager::Close(const KPageLinkedList& pg) {
for (const auto& node : pg.Nodes()) {
Close(node.GetAddress(), node.GetNumPages());
}
}
void KMemoryManager::Open(const KPageLinkedList& pg) {
for (const auto& node : pg.Nodes()) {
Open(node.GetAddress(), node.GetNumPages());
}
}
size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management,
VAddr management_end, Pool p) {
// Calculate management sizes.
const size_t ref_count_size = (size / PageSize) * sizeof(u16);
const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(size);
const size_t manager_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(size);
const size_t total_management_size = manager_size + page_heap_size;
ASSERT(manager_size <= total_management_size);
ASSERT(management + total_management_size <= management_end);
ASSERT(Common::IsAligned(total_management_size, PageSize));
// Setup region.
pool = p;
management_region = management;
page_reference_counts.resize(
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize);
ASSERT(Common::IsAligned(management_region, PageSize));
// Initialize the manager's KPageHeap.
heap.Initialize(address, size, management + manager_size, page_heap_size);
return total_management_size;
}
size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {
const size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
const size_t optimize_map_size =
(Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
const std::size_t manager_meta_size =
Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
const std::size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size);
const size_t manager_meta_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size);
return manager_meta_size + page_heap_size;
}

View File

@@ -5,10 +5,12 @@
#pragma once
#include <array>
#include <mutex>
#include <tuple>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_page_heap.h"
#include "core/hle/result.h"
@@ -20,8 +22,11 @@ namespace Kernel {
class KPageLinkedList;
class KMemoryManager final : NonCopyable {
class KMemoryManager final {
public:
YUZU_NON_COPYABLE(KMemoryManager);
YUZU_NON_MOVEABLE(KMemoryManager);
enum class Pool : u32 {
Application = 0,
Applet = 1,
@@ -48,22 +53,33 @@ public:
explicit KMemoryManager(Core::System& system_);
constexpr std::size_t GetSize(Pool pool) const {
return managers[static_cast<std::size_t>(pool)].GetSize();
void Initialize(VAddr management_region, size_t management_region_size);
constexpr size_t GetSize(Pool pool) const {
constexpr Direction GetSizeDirection = Direction::FromFront;
size_t total = 0;
for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
manager = this->GetNextManager(manager, GetSizeDirection)) {
total += manager->GetSize();
}
return total;
}
void InitializeManager(Pool pool, u64 start_address, u64 end_address);
PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
ResultCode AllocateAndOpen(KPageLinkedList* out, size_t num_pages, u32 option);
ResultCode AllocateAndOpenForProcess(KPageLinkedList* out, size_t num_pages, u32 option,
u64 process_id, u8 fill_pattern);
VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
u32 heap_fill_value = 0);
ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
u32 heap_fill_value = 0);
static constexpr size_t MaxManagerCount = 10;
static constexpr std::size_t MaxManagerCount = 10;
void Close(PAddr address, size_t num_pages);
void Close(const KPageLinkedList& pg);
void Open(PAddr address, size_t num_pages);
void Open(const KPageLinkedList& pg);
public:
static std::size_t CalculateManagementOverheadSize(std::size_t region_size) {
static size_t CalculateManagementOverheadSize(size_t region_size) {
return Impl::CalculateManagementOverheadSize(region_size);
}
@@ -88,38 +104,34 @@ public:
}
private:
class Impl final : NonCopyable {
private:
using RefCount = u16;
private:
KPageHeap heap;
Pool pool{};
class Impl final {
public:
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
YUZU_NON_COPYABLE(Impl);
YUZU_NON_MOVEABLE(Impl);
static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
std::size_t region_size) {
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
}
public:
Impl() = default;
~Impl() = default;
std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address);
size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end,
Pool p);
VAddr AllocateBlock(s32 index, bool random) {
return heap.AllocateBlock(index, random);
}
void Free(VAddr addr, std::size_t num_pages) {
void Free(VAddr addr, size_t num_pages) {
heap.Free(addr, num_pages);
}
constexpr std::size_t GetSize() const {
void SetInitialUsedHeapSize(size_t reserved_size) {
heap.SetInitialUsedSize(reserved_size);
}
constexpr Pool GetPool() const {
return pool;
}
constexpr size_t GetSize() const {
return heap.GetSize();
}
@@ -130,12 +142,137 @@ private:
constexpr VAddr GetEndAddress() const {
return heap.GetEndAddress();
}
constexpr size_t GetPageOffset(PAddr address) const {
return heap.GetPageOffset(address);
}
constexpr size_t GetPageOffsetToEnd(PAddr address) const {
return heap.GetPageOffsetToEnd(address);
}
constexpr void SetNext(Impl* n) {
next = n;
}
constexpr void SetPrev(Impl* n) {
prev = n;
}
constexpr Impl* GetNext() const {
return next;
}
constexpr Impl* GetPrev() const {
return prev;
}
void OpenFirst(PAddr address, size_t num_pages) {
size_t index = this->GetPageOffset(address);
const size_t end = index + num_pages;
while (index < end) {
const RefCount ref_count = (++page_reference_counts[index]);
ASSERT(ref_count == 1);
index++;
}
}
void Open(PAddr address, size_t num_pages) {
size_t index = this->GetPageOffset(address);
const size_t end = index + num_pages;
while (index < end) {
const RefCount ref_count = (++page_reference_counts[index]);
ASSERT(ref_count > 1);
index++;
}
}
void Close(PAddr address, size_t num_pages) {
size_t index = this->GetPageOffset(address);
const size_t end = index + num_pages;
size_t free_start = 0;
size_t free_count = 0;
while (index < end) {
ASSERT(page_reference_counts[index] > 0);
const RefCount ref_count = (--page_reference_counts[index]);
// Keep track of how many zero refcounts we see in a row, to minimize calls to free.
if (ref_count == 0) {
if (free_count > 0) {
free_count++;
} else {
free_start = index;
free_count = 1;
}
} else {
if (free_count > 0) {
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
free_count = 0;
}
}
index++;
}
if (free_count > 0) {
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
}
}
static size_t CalculateManagementOverheadSize(size_t region_size);
static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
}
private:
using RefCount = u16;
KPageHeap heap;
std::vector<RefCount> page_reference_counts;
VAddr management_region{};
Pool pool{};
Impl* next{};
Impl* prev{};
};
private:
Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) {
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
}
const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const {
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
}
constexpr Impl* GetFirstManager(Pool pool, Direction dir) const {
return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)]
: pool_managers_head[static_cast<size_t>(pool)];
}
constexpr Impl* GetNextManager(Impl* cur, Direction dir) const {
if (dir == Direction::FromBack) {
return cur->GetPrev();
} else {
return cur->GetNext();
}
}
ResultCode AllocatePageGroupImpl(KPageLinkedList* out, size_t num_pages, Pool pool,
Direction dir, bool random);
private:
Core::System& system;
std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks;
std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks;
std::array<Impl*, MaxManagerCount> pool_managers_head{};
std::array<Impl*, MaxManagerCount> pool_managers_tail{};
std::array<Impl, MaxManagerCount> managers;
size_t num_managers{};
};
} // namespace Kernel

View File

@@ -5,6 +5,7 @@
#pragma once
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
#include "core/hle/kernel/k_memory_region_type.h"
@@ -13,11 +14,13 @@ namespace Kernel {
class KMemoryRegionAllocator;
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion>,
NonCopyable {
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion> {
friend class KMemoryRegionTree;
public:
YUZU_NON_COPYABLE(KMemoryRegion);
YUZU_NON_MOVEABLE(KMemoryRegion);
constexpr KMemoryRegion() = default;
constexpr KMemoryRegion(u64 address_, u64 last_address_)
: address{address_}, last_address{last_address_} {}
@@ -29,6 +32,8 @@ public:
: KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_,
type_id_) {}
~KMemoryRegion() = default;
static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) {
if (lhs.GetAddress() < rhs.GetAddress()) {
return -1;
@@ -39,16 +44,6 @@ public:
}
}
private:
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
address = a;
pair_address = p;
last_address = la;
attributes = r;
type_id = t;
}
public:
constexpr u64 GetAddress() const {
return address;
}
@@ -108,6 +103,14 @@ public:
}
private:
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
address = a;
pair_address = p;
last_address = la;
attributes = r;
type_id = t;
}
u64 address{};
u64 last_address{};
u64 pair_address{};
@@ -115,8 +118,25 @@ private:
u32 type_id{};
};
class KMemoryRegionTree final : NonCopyable {
class KMemoryRegionTree final {
private:
using TreeType =
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
public:
YUZU_NON_COPYABLE(KMemoryRegionTree);
YUZU_NON_MOVEABLE(KMemoryRegionTree);
using value_type = TreeType::value_type;
using size_type = TreeType::size_type;
using difference_type = TreeType::difference_type;
using pointer = TreeType::pointer;
using const_pointer = TreeType::const_pointer;
using reference = TreeType::reference;
using const_reference = TreeType::const_reference;
using iterator = TreeType::iterator;
using const_iterator = TreeType::const_iterator;
struct DerivedRegionExtents {
const KMemoryRegion* first_region{};
const KMemoryRegion* last_region{};
@@ -140,29 +160,9 @@ public:
}
};
private:
using TreeType =
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
public:
using value_type = TreeType::value_type;
using size_type = TreeType::size_type;
using difference_type = TreeType::difference_type;
using pointer = TreeType::pointer;
using const_pointer = TreeType::const_pointer;
using reference = TreeType::reference;
using const_reference = TreeType::const_reference;
using iterator = TreeType::iterator;
using const_iterator = TreeType::const_iterator;
private:
TreeType m_tree{};
KMemoryRegionAllocator& memory_region_allocator;
public:
explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_);
~KMemoryRegionTree() = default;
public:
KMemoryRegion* FindModifiable(u64 address) {
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) {
return std::addressof(*it);
@@ -241,7 +241,6 @@ public:
return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
}
public:
void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0);
bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0);
@@ -252,7 +251,6 @@ public:
return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size;
}
public:
// Iterator accessors.
iterator begin() {
return m_tree.begin();
@@ -322,13 +320,21 @@ public:
iterator nfind(const_reference ref) const {
return m_tree.nfind(ref);
}
private:
TreeType m_tree{};
KMemoryRegionAllocator& memory_region_allocator;
};
class KMemoryRegionAllocator final : NonCopyable {
class KMemoryRegionAllocator final {
public:
YUZU_NON_COPYABLE(KMemoryRegionAllocator);
YUZU_NON_MOVEABLE(KMemoryRegionAllocator);
static constexpr size_t MaxMemoryRegions = 200;
constexpr KMemoryRegionAllocator() = default;
constexpr ~KMemoryRegionAllocator() = default;
template <typename... Args>
KMemoryRegion* Allocate(Args&&... args) {

View File

@@ -14,7 +14,8 @@
namespace Kernel {
enum KMemoryRegionType : u32 {
KMemoryRegionAttr_CarveoutProtected = 0x04000000,
KMemoryRegionAttr_CarveoutProtected = 0x02000000,
KMemoryRegionAttr_Uncached = 0x04000000,
KMemoryRegionAttr_DidKernelMap = 0x08000000,
KMemoryRegionAttr_ShouldKernelMap = 0x10000000,
KMemoryRegionAttr_UserReadOnly = 0x20000000,
@@ -239,6 +240,11 @@ static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
// UNUSED: .DeriveSparse(2, 2, 0);
constexpr auto KMemoryRegionType_VirtualDramUnknownDebug =
KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
constexpr auto KMemoryRegionType_VirtualDramKernelInitPt =
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
constexpr auto KMemoryRegionType_VirtualDramPoolManagement =
@@ -330,6 +336,8 @@ constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelPtHeap;
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
return KMemoryRegionType_VirtualDramUnknownDebug;
} else {
return KMemoryRegionType_Dram;
}

View File

@@ -7,35 +7,51 @@
namespace Kernel {
void KPageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) {
// Check our assumptions
ASSERT(Common::IsAligned((address), PageSize));
void KPageHeap::Initialize(PAddr address, size_t size, VAddr management_address,
size_t management_size, const size_t* block_shifts,
size_t num_block_shifts) {
// Check our assumptions.
ASSERT(Common::IsAligned(address, PageSize));
ASSERT(Common::IsAligned(size, PageSize));
ASSERT(0 < num_block_shifts && num_block_shifts <= NumMemoryBlockPageShifts);
const VAddr management_end = management_address + management_size;
// Set our members
heap_address = address;
heap_size = size;
// Set our members.
m_heap_address = address;
m_heap_size = size;
m_num_blocks = num_block_shifts;
// Setup bitmaps
metadata.resize(metadata_size / sizeof(u64));
u64* cur_bitmap_storage{metadata.data()};
for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) {
const std::size_t cur_block_shift{MemoryBlockPageShifts[i]};
const std::size_t next_block_shift{
(i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0};
cur_bitmap_storage = blocks[i].Initialize(heap_address, heap_size, cur_block_shift,
next_block_shift, cur_bitmap_storage);
// Setup bitmaps.
m_management_data.resize(management_size / sizeof(u64));
u64* cur_bitmap_storage{m_management_data.data()};
for (size_t i = 0; i < num_block_shifts; i++) {
const size_t cur_block_shift = block_shifts[i];
const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
cur_bitmap_storage = m_blocks[i].Initialize(m_heap_address, m_heap_size, cur_block_shift,
next_block_shift, cur_bitmap_storage);
}
// Ensure we didn't overextend our bounds.
ASSERT(VAddr(cur_bitmap_storage) <= management_end);
}
VAddr KPageHeap::AllocateBlock(s32 index, bool random) {
const std::size_t needed_size{blocks[index].GetSize()};
size_t KPageHeap::GetNumFreePages() const {
size_t num_free = 0;
for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) {
if (const VAddr addr{blocks[i].PopBlock(random)}; addr) {
if (const std::size_t allocated_size{blocks[i].GetSize()};
allocated_size > needed_size) {
Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
for (size_t i = 0; i < m_num_blocks; i++) {
num_free += m_blocks[i].GetNumFreePages();
}
return num_free;
}
PAddr KPageHeap::AllocateBlock(s32 index, bool random) {
const size_t needed_size = m_blocks[index].GetSize();
for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) {
if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) {
if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) {
this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
}
return addr;
}
@@ -44,34 +60,34 @@ VAddr KPageHeap::AllocateBlock(s32 index, bool random) {
return 0;
}
void KPageHeap::FreeBlock(VAddr block, s32 index) {
void KPageHeap::FreeBlock(PAddr block, s32 index) {
do {
block = blocks[index++].PushBlock(block);
block = m_blocks[index++].PushBlock(block);
} while (block != 0);
}
void KPageHeap::Free(VAddr addr, std::size_t num_pages) {
// Freeing no pages is a no-op
void KPageHeap::Free(PAddr addr, size_t num_pages) {
// Freeing no pages is a no-op.
if (num_pages == 0) {
return;
}
// Find the largest block size that we can free, and free as many as possible
s32 big_index{static_cast<s32>(MemoryBlockPageShifts.size()) - 1};
const VAddr start{addr};
const VAddr end{(num_pages * PageSize) + addr};
VAddr before_start{start};
VAddr before_end{start};
VAddr after_start{end};
VAddr after_end{end};
// Find the largest block size that we can free, and free as many as possible.
s32 big_index = static_cast<s32>(m_num_blocks) - 1;
const PAddr start = addr;
const PAddr end = addr + num_pages * PageSize;
PAddr before_start = start;
PAddr before_end = start;
PAddr after_start = end;
PAddr after_end = end;
while (big_index >= 0) {
const std::size_t block_size{blocks[big_index].GetSize()};
const VAddr big_start{Common::AlignUp((start), block_size)};
const VAddr big_end{Common::AlignDown((end), block_size)};
const size_t block_size = m_blocks[big_index].GetSize();
const PAddr big_start = Common::AlignUp(start, block_size);
const PAddr big_end = Common::AlignDown(end, block_size);
if (big_start < big_end) {
// Free as many big blocks as we can
for (auto block{big_start}; block < big_end; block += block_size) {
FreeBlock(block, big_index);
// Free as many big blocks as we can.
for (auto block = big_start; block < big_end; block += block_size) {
this->FreeBlock(block, big_index);
}
before_end = big_start;
after_start = big_end;
@@ -81,31 +97,31 @@ void KPageHeap::Free(VAddr addr, std::size_t num_pages) {
}
ASSERT(big_index >= 0);
// Free space before the big blocks
for (s32 i{big_index - 1}; i >= 0; i--) {
const std::size_t block_size{blocks[i].GetSize()};
// Free space before the big blocks.
for (s32 i = big_index - 1; i >= 0; i--) {
const size_t block_size = m_blocks[i].GetSize();
while (before_start + block_size <= before_end) {
before_end -= block_size;
FreeBlock(before_end, i);
this->FreeBlock(before_end, i);
}
}
// Free space after the big blocks
for (s32 i{big_index - 1}; i >= 0; i--) {
const std::size_t block_size{blocks[i].GetSize()};
// Free space after the big blocks.
for (s32 i = big_index - 1; i >= 0; i--) {
const size_t block_size = m_blocks[i].GetSize();
while (after_start + block_size <= after_end) {
FreeBlock(after_start, i);
this->FreeBlock(after_start, i);
after_start += block_size;
}
}
}
std::size_t KPageHeap::CalculateManagementOverheadSize(std::size_t region_size) {
std::size_t overhead_size = 0;
for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) {
const std::size_t cur_block_shift{MemoryBlockPageShifts[i]};
const std::size_t next_block_shift{
(i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0};
size_t KPageHeap::CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
size_t num_block_shifts) {
size_t overhead_size = 0;
for (size_t i = 0; i < num_block_shifts; i++) {
const size_t cur_block_shift = block_shifts[i];
const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
overhead_size += KPageHeap::Block::CalculateManagementOverheadSize(
region_size, cur_block_shift, next_block_shift);
}

View File

@@ -8,183 +8,210 @@
#include <vector>
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_page_bitmap.h"
#include "core/hle/kernel/memory_types.h"
namespace Kernel {
class KPageHeap final : NonCopyable {
class KPageHeap final {
public:
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
const auto target_pages{std::max(num_pages, align_pages)};
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
if (target_pages <=
(static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
YUZU_NON_COPYABLE(KPageHeap);
YUZU_NON_MOVEABLE(KPageHeap);
KPageHeap() = default;
~KPageHeap() = default;
constexpr PAddr GetAddress() const {
return m_heap_address;
}
constexpr size_t GetSize() const {
return m_heap_size;
}
constexpr PAddr GetEndAddress() const {
return this->GetAddress() + this->GetSize();
}
constexpr size_t GetPageOffset(PAddr block) const {
return (block - this->GetAddress()) / PageSize;
}
constexpr size_t GetPageOffsetToEnd(PAddr block) const {
return (this->GetEndAddress() - block) / PageSize;
}
void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
size_t management_size) {
return this->Initialize(heap_address, heap_size, management_address, management_size,
MemoryBlockPageShifts.data(), NumMemoryBlockPageShifts);
}
size_t GetFreeSize() const {
return this->GetNumFreePages() * PageSize;
}
void SetInitialUsedSize(size_t reserved_size) {
// Check that the reserved size is valid.
const size_t free_size = this->GetNumFreePages() * PageSize;
ASSERT(m_heap_size >= free_size + reserved_size);
// Set the initial used size.
m_initial_used_size = m_heap_size - free_size - reserved_size;
}
PAddr AllocateBlock(s32 index, bool random);
void Free(PAddr addr, size_t num_pages);
static size_t CalculateManagementOverheadSize(size_t region_size) {
return CalculateManagementOverheadSize(region_size, MemoryBlockPageShifts.data(),
NumMemoryBlockPageShifts);
}
static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) {
const size_t target_pages = std::max(num_pages, align_pages);
for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
return static_cast<s32>(i);
}
}
return -1;
}
static constexpr s32 GetBlockIndex(std::size_t num_pages) {
for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) {
if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
static constexpr s32 GetBlockIndex(size_t num_pages) {
for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) {
if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
return i;
}
}
return -1;
}
static constexpr std::size_t GetBlockSize(std::size_t index) {
return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index];
static constexpr size_t GetBlockSize(size_t index) {
return size_t(1) << MemoryBlockPageShifts[index];
}
static constexpr std::size_t GetBlockNumPages(std::size_t index) {
static constexpr size_t GetBlockNumPages(size_t index) {
return GetBlockSize(index) / PageSize;
}
private:
static constexpr std::size_t NumMemoryBlockPageShifts{7};
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
};
class Block final : NonCopyable {
private:
KPageBitmap bitmap;
VAddr heap_address{};
uintptr_t end_offset{};
std::size_t block_shift{};
std::size_t next_block_shift{};
class Block final {
public:
YUZU_NON_COPYABLE(Block);
YUZU_NON_MOVEABLE(Block);
Block() = default;
~Block() = default;
constexpr std::size_t GetShift() const {
return block_shift;
constexpr size_t GetShift() const {
return m_block_shift;
}
constexpr std::size_t GetNextShift() const {
return next_block_shift;
constexpr size_t GetNextShift() const {
return m_next_block_shift;
}
constexpr std::size_t GetSize() const {
return static_cast<std::size_t>(1) << GetShift();
constexpr size_t GetSize() const {
return u64(1) << this->GetShift();
}
constexpr std::size_t GetNumPages() const {
return GetSize() / PageSize;
constexpr size_t GetNumPages() const {
return this->GetSize() / PageSize;
}
constexpr std::size_t GetNumFreeBlocks() const {
return bitmap.GetNumBits();
constexpr size_t GetNumFreeBlocks() const {
return m_bitmap.GetNumBits();
}
constexpr std::size_t GetNumFreePages() const {
return GetNumFreeBlocks() * GetNumPages();
constexpr size_t GetNumFreePages() const {
return this->GetNumFreeBlocks() * this->GetNumPages();
}
u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs,
u64* bit_storage) {
// Set shifts
block_shift = bs;
next_block_shift = nbs;
u64* Initialize(PAddr addr, size_t size, size_t bs, size_t nbs, u64* bit_storage) {
// Set shifts.
m_block_shift = bs;
m_next_block_shift = nbs;
// Align up the address
VAddr end{addr + size};
const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift)
: (1ULL << block_shift)};
addr = Common::AlignDown((addr), align);
end = Common::AlignUp((end), align);
// Align up the address.
PAddr end = addr + size;
const size_t align = (m_next_block_shift != 0) ? (u64(1) << m_next_block_shift)
: (u64(1) << m_block_shift);
addr = Common::AlignDown(addr, align);
end = Common::AlignUp(end, align);
heap_address = addr;
end_offset = (end - addr) / (1ULL << block_shift);
return bitmap.Initialize(bit_storage, end_offset);
m_heap_address = addr;
m_end_offset = (end - addr) / (u64(1) << m_block_shift);
return m_bitmap.Initialize(bit_storage, m_end_offset);
}
VAddr PushBlock(VAddr address) {
// Set the bit for the free block
std::size_t offset{(address - heap_address) >> GetShift()};
bitmap.SetBit(offset);
PAddr PushBlock(PAddr address) {
// Set the bit for the free block.
size_t offset = (address - m_heap_address) >> this->GetShift();
m_bitmap.SetBit(offset);
// If we have a next shift, try to clear the blocks below and return the address
if (GetNextShift()) {
const auto diff{1ULL << (GetNextShift() - GetShift())};
// If we have a next shift, try to clear the blocks below this one and return the new
// address.
if (this->GetNextShift()) {
const size_t diff = u64(1) << (this->GetNextShift() - this->GetShift());
offset = Common::AlignDown(offset, diff);
if (bitmap.ClearRange(offset, diff)) {
return heap_address + (offset << GetShift());
if (m_bitmap.ClearRange(offset, diff)) {
return m_heap_address + (offset << this->GetShift());
}
}
// We couldn't coalesce, or we're already as big as possible
return 0;
// We couldn't coalesce, or we're already as big as possible.
return {};
}
VAddr PopBlock(bool random) {
// Find a free block
const s64 soffset{bitmap.FindFreeBlock(random)};
PAddr PopBlock(bool random) {
// Find a free block.
s64 soffset = m_bitmap.FindFreeBlock(random);
if (soffset < 0) {
return 0;
return {};
}
const auto offset{static_cast<std::size_t>(soffset)};
const size_t offset = static_cast<size_t>(soffset);
// Update our tracking and return it
bitmap.ClearBit(offset);
return heap_address + (offset << GetShift());
// Update our tracking and return it.
m_bitmap.ClearBit(offset);
return m_heap_address + (offset << this->GetShift());
}
public:
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size,
std::size_t cur_block_shift,
std::size_t next_block_shift) {
const auto cur_block_size{(1ULL << cur_block_shift)};
const auto next_block_size{(1ULL << next_block_shift)};
const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size};
static constexpr size_t CalculateManagementOverheadSize(size_t region_size,
size_t cur_block_shift,
size_t next_block_shift) {
const size_t cur_block_size = (u64(1) << cur_block_shift);
const size_t next_block_size = (u64(1) << next_block_shift);
const size_t align = (next_block_shift != 0) ? next_block_size : cur_block_size;
return KPageBitmap::CalculateManagementOverheadSize(
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
}
private:
KPageBitmap m_bitmap;
PAddr m_heap_address{};
uintptr_t m_end_offset{};
size_t m_block_shift{};
size_t m_next_block_shift{};
};
public:
KPageHeap() = default;
private:
void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
size_t management_size, const size_t* block_shifts, size_t num_block_shifts);
size_t GetNumFreePages() const;
constexpr VAddr GetAddress() const {
return heap_address;
}
constexpr std::size_t GetSize() const {
return heap_size;
}
constexpr VAddr GetEndAddress() const {
return GetAddress() + GetSize();
}
constexpr std::size_t GetPageOffset(VAddr block) const {
return (block - GetAddress()) / PageSize;
}
void FreeBlock(PAddr block, s32 index);
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
VAddr AllocateBlock(s32 index, bool random);
void Free(VAddr addr, std::size_t num_pages);
void UpdateUsedSize() {
used_size = heap_size - (GetNumFreePages() * PageSize);
}
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
static constexpr size_t NumMemoryBlockPageShifts{7};
static constexpr std::array<size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
};
private:
constexpr std::size_t GetNumFreePages() const {
std::size_t num_free{};
static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
size_t num_block_shifts);
for (const auto& block : blocks) {
num_free += block.GetNumFreePages();
}
return num_free;
}
void FreeBlock(VAddr block, s32 index);
VAddr heap_address{};
std::size_t heap_size{};
std::size_t used_size{};
std::array<Block, NumMemoryBlockPageShifts> blocks{};
std::vector<u64> metadata;
private:
PAddr m_heap_address{};
size_t m_heap_size{};
size_t m_initial_used_size{};
size_t m_num_blocks{};
std::array<Block, NumMemoryBlockPageShifts> m_blocks{};
std::vector<u64> m_management_data;
};
} // namespace Kernel

View File

@@ -41,27 +41,12 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT
}
}
constexpr u64 GetAddressInRange(const KMemoryInfo& info, VAddr addr) {
if (info.GetAddress() < addr) {
return addr;
}
return info.GetAddress();
}
constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr end) {
std::size_t size{info.GetSize()};
if (info.GetAddress() < start) {
size -= start - info.GetAddress();
}
if (info.GetEndAddress() > end) {
size -= info.GetEndAddress() - end;
}
return size;
}
} // namespace
KPageTable::KPageTable(Core::System& system_) : system{system_} {}
KPageTable::KPageTable(Core::System& system_)
: general_lock{system_.Kernel()}, map_physical_memory_lock{system_.Kernel()}, system{system_} {}
KPageTable::~KPageTable() = default;
ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type,
bool enable_aslr, VAddr code_addr,
@@ -282,17 +267,18 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Verify that the destination memory is unmapped.
R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::None, KMemoryAttribute::None));
KPageLinkedList pg;
R_TRY(system.Kernel().MemoryManager().AllocateAndOpen(
&pg, num_pages,
KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, allocation_option)));
KPageLinkedList page_linked_list;
R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool,
allocation_option));
R_TRY(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup));
R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup));
block_manager->Update(addr, num_pages, state, perm);
@@ -300,7 +286,7 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
}
ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
const std::size_t num_pages{size / PageSize};
@@ -337,7 +323,7 @@ ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t
}
ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
if (!size) {
return ResultSuccess;
@@ -371,7 +357,7 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
KPageTable& src_page_table, VAddr src_addr) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
const std::size_t num_pages{size / PageSize};
@@ -397,150 +383,482 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
return ResultSuccess;
}
ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
ResultCode KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
// Lock the physical memory lock.
std::lock_guard phys_lk(map_physical_memory_lock);
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
// Lock the table.
std::lock_guard lock{page_table_lock};
// Calculate the last address for convenience.
const VAddr last_address = address + size - 1;
std::size_t mapped_size{};
const VAddr end_addr{addr + size};
// Define iteration variables.
VAddr cur_address;
std::size_t mapped_size;
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
if (info.state != KMemoryState::Free) {
mapped_size += GetSizeInRange(info, addr, end_addr);
}
});
// The entire mapping process can be retried.
while (true) {
// Check if the memory is already mapped.
{
// Lock the table.
KScopedLightLock lk(general_lock);
if (mapped_size == size) {
return ResultSuccess;
}
// Iterate over the memory.
cur_address = address;
mapped_size = 0;
const std::size_t remaining_size{size - mapped_size};
const std::size_t remaining_pages{remaining_size / PageSize};
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Reserve the memory from the process resource limit.
KScopedResourceReservation memory_reservation(
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
remaining_size);
if (!memory_reservation.Succeeded()) {
LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size);
return ResultLimitReached;
}
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
KPageLinkedList page_linked_list;
// Check if we're done.
if (last_address <= info.GetLastAddress()) {
if (info.GetState() != KMemoryState::Free) {
mapped_size += (last_address + 1 - cur_address);
}
break;
}
CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages,
memory_pool, allocation_option));
// Track the memory if it's mapped.
if (info.GetState() != KMemoryState::Free) {
mapped_size += VAddr(info.GetEndAddress()) - cur_address;
}
// We succeeded, so commit the memory reservation.
memory_reservation.Commit();
// Map the memory.
auto node{page_linked_list.Nodes().begin()};
PAddr map_addr{node->GetAddress()};
std::size_t src_num_pages{node->GetNumPages()};
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
if (info.state != KMemoryState::Free) {
return;
}
std::size_t dst_num_pages{GetSizeInRange(info, addr, end_addr) / PageSize};
VAddr dst_addr{GetAddressInRange(info, addr)};
while (dst_num_pages) {
if (!src_num_pages) {
node = std::next(node);
map_addr = node->GetAddress();
src_num_pages = node->GetNumPages();
// Advance.
cur_address = info.GetEndAddress();
++it;
}
const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)};
Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map,
map_addr);
dst_addr += num_pages * PageSize;
map_addr += num_pages * PageSize;
src_num_pages -= num_pages;
dst_num_pages -= num_pages;
// If the size mapped is the size requested, we've nothing to do.
R_SUCCEED_IF(size == mapped_size);
}
});
mapped_physical_memory_size += remaining_size;
// Allocate and map the memory.
{
// Reserve the memory from the process resource limit.
KScopedResourceReservation memory_reservation(
system.Kernel().CurrentProcess()->GetResourceLimit(),
LimitableResource::PhysicalMemory, size - mapped_size);
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
const std::size_t num_pages{size / PageSize};
block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
KMemoryAttribute::None, KMemoryState::Normal,
KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
// Allocate pages for the new memory.
KPageLinkedList pg;
R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess(
&pg, (size - mapped_size) / PageSize,
KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0));
return ResultSuccess;
// Map the memory.
{
// Lock the table.
KScopedLightLock lk(general_lock);
size_t num_allocator_blocks = 0;
// Verify that nobody has mapped memory since we first checked.
{
// Iterate over the memory.
size_t checked_mapped_size = 0;
cur_address = address;
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
const bool is_free = info.GetState() == KMemoryState::Free;
if (is_free) {
if (info.GetAddress() < address) {
++num_allocator_blocks;
}
if (last_address < info.GetLastAddress()) {
++num_allocator_blocks;
}
}
// Check if we're done.
if (last_address <= info.GetLastAddress()) {
if (!is_free) {
checked_mapped_size += (last_address + 1 - cur_address);
}
break;
}
// Track the memory if it's mapped.
if (!is_free) {
checked_mapped_size += VAddr(info.GetEndAddress()) - cur_address;
}
// Advance.
cur_address = info.GetEndAddress();
++it;
}
// If the size now isn't what it was before, somebody mapped or unmapped
// concurrently. If this happened, retry.
if (mapped_size != checked_mapped_size) {
continue;
}
}
// Reset the current tracking address, and make sure we clean up on failure.
cur_address = address;
auto unmap_guard = detail::ScopeExit([&] {
if (cur_address > address) {
const VAddr last_unmap_address = cur_address - 1;
// Iterate, unmapping the pages.
cur_address = address;
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
// If the memory state is free, we mapped it and need to unmap it.
if (info.GetState() == KMemoryState::Free) {
// Determine the range to unmap.
const size_t cur_pages =
std::min(VAddr(info.GetEndAddress()) - cur_address,
last_unmap_address + 1 - cur_address) /
PageSize;
// Unmap.
ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
OperationType::Unmap)
.IsSuccess());
}
// Check if we're done.
if (last_unmap_address <= info.GetLastAddress()) {
break;
}
// Advance.
cur_address = info.GetEndAddress();
++it;
}
}
});
// Iterate over the memory.
auto pg_it = pg.Nodes().begin();
PAddr pg_phys_addr = pg_it->GetAddress();
size_t pg_pages = pg_it->GetNumPages();
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
// If it's unmapped, we need to map it.
if (info.GetState() == KMemoryState::Free) {
// Determine the range to map.
size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
last_address + 1 - cur_address) /
PageSize;
// While we have pages to map, map them.
while (map_pages > 0) {
// Check if we're at the end of the physical block.
if (pg_pages == 0) {
// Ensure there are more pages to map.
ASSERT(pg_it != pg.Nodes().end());
// Advance our physical block.
++pg_it;
pg_phys_addr = pg_it->GetAddress();
pg_pages = pg_it->GetNumPages();
}
// Map whatever we can.
const size_t cur_pages = std::min(pg_pages, map_pages);
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
OperationType::Map, pg_phys_addr));
// Advance.
cur_address += cur_pages * PageSize;
map_pages -= cur_pages;
pg_phys_addr += cur_pages * PageSize;
pg_pages -= cur_pages;
}
}
// Check if we're done.
if (last_address <= info.GetLastAddress()) {
break;
}
// Advance.
cur_address = info.GetEndAddress();
++it;
}
// We succeeded, so commit the memory reservation.
memory_reservation.Commit();
// Increase our tracked mapped size.
mapped_physical_memory_size += (size - mapped_size);
// Update the relevant memory blocks.
block_manager->Update(address, size / PageSize, KMemoryState::Free,
KMemoryPermission::None, KMemoryAttribute::None,
KMemoryState::Normal, KMemoryPermission::UserReadWrite,
KMemoryAttribute::None);
// Cancel our guard.
unmap_guard.Cancel();
return ResultSuccess;
}
}
}
}
ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
ResultCode KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
// Lock the physical memory lock.
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
const VAddr end_addr{addr + size};
ResultCode result{ResultSuccess};
std::size_t mapped_size{};
// Lock the table.
KScopedLightLock lk(general_lock);
// Verify that the region can be unmapped
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
if (info.state == KMemoryState::Normal) {
if (info.attribute != KMemoryAttribute::None) {
result = ResultInvalidCurrentMemory;
return;
// Calculate the last address for convenience.
const VAddr last_address = address + size - 1;
// Define iteration variables.
VAddr cur_address = 0;
std::size_t mapped_size = 0;
std::size_t num_allocator_blocks = 0;
// Check if the memory is mapped.
{
// Iterate over the memory.
cur_address = address;
mapped_size = 0;
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
// Verify the memory's state.
const bool is_normal = info.GetState() == KMemoryState::Normal &&
info.GetAttribute() == KMemoryAttribute::None;
const bool is_free = info.GetState() == KMemoryState::Free;
R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory);
if (is_normal) {
R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory);
if (info.GetAddress() < address) {
++num_allocator_blocks;
}
if (last_address < info.GetLastAddress()) {
++num_allocator_blocks;
}
}
mapped_size += GetSizeInRange(info, addr, end_addr);
} else if (info.state != KMemoryState::Free) {
result = ResultInvalidCurrentMemory;
// Check if we're done.
if (last_address <= info.GetLastAddress()) {
if (is_normal) {
mapped_size += (last_address + 1 - cur_address);
}
break;
}
// Track the memory if it's mapped.
if (is_normal) {
mapped_size += VAddr(info.GetEndAddress()) - cur_address;
}
// Advance.
cur_address = info.GetEndAddress();
++it;
}
});
if (result.IsError()) {
return result;
// If there's nothing mapped, we've nothing to do.
R_SUCCEED_IF(mapped_size == 0);
}
if (!mapped_size) {
return ResultSuccess;
// Make a page group for the unmap region.
KPageLinkedList pg;
{
auto& impl = this->PageTableImpl();
// Begin traversal.
Common::PageTable::TraversalContext context;
Common::PageTable::TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
bool cur_valid = false;
Common::PageTable::TraversalEntry next_entry;
bool next_valid = false;
size_t tot_size = 0;
cur_address = address;
next_valid = impl.BeginTraversal(next_entry, context, cur_address);
next_entry.block_size =
(next_entry.block_size - (next_entry.phys_addr & (next_entry.block_size - 1)));
// Iterate, building the group.
while (true) {
if ((!next_valid && !cur_valid) ||
(next_valid && cur_valid &&
next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) {
cur_entry.block_size += next_entry.block_size;
} else {
if (cur_valid) {
// ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
R_TRY(pg.AddBlock(cur_entry.phys_addr, cur_entry.block_size / PageSize));
}
// Update tracking variables.
tot_size += cur_entry.block_size;
cur_entry = next_entry;
cur_valid = next_valid;
}
if (cur_entry.block_size + tot_size >= size) {
break;
}
next_valid = impl.ContinueTraversal(next_entry, context);
}
// Add the last block.
if (cur_valid) {
// ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
R_TRY(pg.AddBlock(cur_entry.phys_addr, (size - tot_size) / PageSize));
}
}
ASSERT(pg.GetNumPages() == mapped_size / PageSize);
// Unmap each region within the range
KPageLinkedList page_linked_list;
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
if (info.state == KMemoryState::Normal) {
const std::size_t block_size{GetSizeInRange(info, addr, end_addr)};
const std::size_t block_num_pages{block_size / PageSize};
const VAddr block_addr{GetAddressInRange(info, addr)};
// Reset the current tracking address, and make sure we clean up on failure.
cur_address = address;
auto remap_guard = detail::ScopeExit([&] {
if (cur_address > address) {
const VAddr last_map_address = cur_address - 1;
cur_address = address;
AddRegionToPages(block_addr, block_size / PageSize, page_linked_list);
// Iterate over the memory we unmapped.
auto it = block_manager->FindIterator(cur_address);
auto pg_it = pg.Nodes().begin();
PAddr pg_phys_addr = pg_it->GetAddress();
size_t pg_pages = pg_it->GetNumPages();
if (result = Operate(block_addr, block_num_pages, KMemoryPermission::None,
OperationType::Unmap);
result.IsError()) {
return;
while (true) {
// Get the memory info for the pages we unmapped, convert to property.
const KMemoryInfo info = it->GetMemoryInfo();
// If the memory is normal, we unmapped it and need to re-map it.
if (info.GetState() == KMemoryState::Normal) {
// Determine the range to map.
size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
last_map_address + 1 - cur_address) /
PageSize;
// While we have pages to map, map them.
while (map_pages > 0) {
// Check if we're at the end of the physical block.
if (pg_pages == 0) {
// Ensure there are more pages to map.
ASSERT(pg_it != pg.Nodes().end());
// Advance our physical block.
++pg_it;
pg_phys_addr = pg_it->GetAddress();
pg_pages = pg_it->GetNumPages();
}
// Map whatever we can.
const size_t cur_pages = std::min(pg_pages, map_pages);
ASSERT(this->Operate(cur_address, cur_pages, info.GetPermission(),
OperationType::Map, pg_phys_addr) == ResultSuccess);
// Advance.
cur_address += cur_pages * PageSize;
map_pages -= cur_pages;
pg_phys_addr += cur_pages * PageSize;
pg_pages -= cur_pages;
}
}
// Check if we're done.
if (last_map_address <= info.GetLastAddress()) {
break;
}
// Advance.
++it;
}
}
});
if (result.IsError()) {
return result;
// Iterate over the memory, unmapping as we go.
auto it = block_manager->FindIterator(cur_address);
while (true) {
// Check that the iterator is valid.
ASSERT(it != block_manager->end());
// Get the memory info.
const KMemoryInfo info = it->GetMemoryInfo();
// If the memory state is normal, we need to unmap it.
if (info.GetState() == KMemoryState::Normal) {
// Determine the range to unmap.
const size_t cur_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
last_address + 1 - cur_address) /
PageSize;
// Unmap.
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap));
}
// Check if we're done.
if (last_address <= info.GetLastAddress()) {
break;
}
// Advance.
cur_address = info.GetEndAddress();
++it;
}
const std::size_t num_pages{size / PageSize};
system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool,
allocation_option);
block_manager->Update(addr, num_pages, KMemoryState::Free);
// Release the memory resource.
mapped_physical_memory_size -= mapped_size;
auto process{system.Kernel().CurrentProcess()};
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
mapped_physical_memory_size -= mapped_size;
// Update memory blocks.
block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None,
KMemoryAttribute::None);
// TODO(bunnei): This is a workaround until the next set of changes, where we add reference
// counting for mapped pages. Until then, we must manually close the reference to the page
// group.
system.Kernel().MemoryManager().Close(pg);
// We succeeded.
remap_guard.Cancel();
return ResultSuccess;
}
ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryState src_state{};
CASCADE_CODE(CheckMemoryState(
@@ -579,7 +897,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz
}
ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryState src_state{};
CASCADE_CODE(CheckMemoryState(
@@ -622,6 +940,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s
ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
KMemoryPermission perm) {
ASSERT(this->IsLockedByCurrentThread());
VAddr cur_addr{addr};
for (const auto& node : page_linked_list.Nodes()) {
@@ -650,7 +970,7 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Check the memory state.
R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
@@ -667,12 +987,13 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
}
ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
ASSERT(this->IsLockedByCurrentThread());
VAddr cur_addr{addr};
for (const auto& node : page_linked_list.Nodes()) {
const std::size_t num_pages{(addr - cur_addr) / PageSize};
if (const auto result{
Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)};
if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
OperationType::Unmap)};
result.IsError()) {
return result;
}
@@ -691,7 +1012,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Check the memory state.
R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None,
@@ -712,7 +1033,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
const size_t num_pages = size / PageSize;
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Verify we can change the memory permission.
KMemoryState old_state;
@@ -766,7 +1087,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
}
KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
return block_manager->FindBlock(addr).GetMemoryInfo();
}
@@ -781,7 +1102,7 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) {
}
ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryState state{};
KMemoryAttribute attribute{};
@@ -799,7 +1120,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
}
ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryState state{};
@@ -818,7 +1139,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
const size_t num_pages = size / PageSize;
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Verify we can change the memory permission.
KMemoryState old_state;
@@ -847,7 +1168,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
KMemoryAttribute::SetMask);
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Verify we can change the memory attribute.
KMemoryState old_state;
@@ -878,7 +1199,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
// Lock the table.
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
// Only process page tables are allowed to set heap size.
ASSERT(!this->IsKernel());
@@ -889,15 +1210,15 @@ ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
}
ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
// Lock the physical memory lock.
std::lock_guard phys_lk(map_physical_memory_lock);
// Lock the physical memory mutex.
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
// Try to perform a reduction in heap, instead of an extension.
VAddr cur_address{};
std::size_t allocation_size{};
{
// Lock the table.
std::lock_guard lk(page_table_lock);
KScopedLightLock lk(general_lock);
// Validate that setting heap size is possible at all.
R_UNLESS(!is_kernel, ResultOutOfMemory);
@@ -955,14 +1276,21 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Allocate pages for the heap extension.
KPageLinkedList page_linked_list;
R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize,
memory_pool, allocation_option));
KPageLinkedList pg;
R_TRY(system.Kernel().MemoryManager().AllocateAndOpen(
&pg, allocation_size / PageSize,
KMemoryManager::EncodeOption(memory_pool, allocation_option)));
// Clear all the newly allocated pages.
for (const auto& it : pg.Nodes()) {
std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value,
it.GetSize());
}
// Map the pages.
{
// Lock the table.
std::lock_guard lk(page_table_lock);
KScopedLightLock lk(general_lock);
// Ensure that the heap hasn't changed since we began executing.
ASSERT(cur_address == current_heap_end);
@@ -976,7 +1304,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
// Map the pages.
const auto num_pages = allocation_size / PageSize;
R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup));
R_TRY(Operate(current_heap_end, num_pages, pg, OperationType::MapGroup));
// Clear all the newly allocated pages.
for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
@@ -1004,7 +1332,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
bool is_map_only, VAddr region_start,
std::size_t region_num_pages, KMemoryState state,
KMemoryPermission perm, PAddr map_addr) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
if (!CanContain(region_start, region_num_pages * PageSize, state)) {
return ResultInvalidCurrentMemory;
@@ -1024,8 +1352,9 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
} else {
KPageLinkedList page_group;
R_TRY(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool,
allocation_option));
R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess(
&page_group, needed_num_pages,
KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0));
R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
}
@@ -1035,7 +1364,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
}
ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryPermission perm{};
if (const ResultCode result{CheckMemoryState(
@@ -1058,7 +1387,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
}
ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryPermission perm{};
if (const ResultCode result{CheckMemoryState(
@@ -1081,7 +1410,7 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
}
ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
@@ -1108,7 +1437,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
}
ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KScopedLightLock lk(general_lock);
KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
@@ -1232,7 +1561,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss
return ResultSuccess;
}
constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
switch (state) {
case KMemoryState::Free:
case KMemoryState::Kernel:
@@ -1268,7 +1597,7 @@ constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
}
}
constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
switch (state) {
case KMemoryState::Free:
case KMemoryState::Kernel:

View File

@@ -5,11 +5,12 @@
#pragma once
#include <memory>
#include <mutex>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/page_table.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/result.h"
@@ -22,9 +23,13 @@ namespace Kernel {
class KMemoryBlockManager;
class KPageTable final : NonCopyable {
class KPageTable final {
public:
YUZU_NON_COPYABLE(KPageTable);
YUZU_NON_MOVEABLE(KPageTable);
explicit KPageTable(Core::System& system_);
~KPageTable();
ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
VAddr code_addr, std::size_t code_size,
@@ -97,8 +102,8 @@ private:
OperationType operation);
ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
OperationType operation, PAddr map_addr = 0);
constexpr VAddr GetRegionAddress(KMemoryState state) const;
constexpr std::size_t GetRegionSize(KMemoryState state) const;
VAddr GetRegionAddress(KMemoryState state) const;
std::size_t GetRegionSize(KMemoryState state) const;
ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
std::size_t size, KMemoryState state_mask,
@@ -142,11 +147,12 @@ private:
}
bool IsLockedByCurrentThread() const {
return true;
return general_lock.IsLockedByCurrentThread();
}
std::recursive_mutex page_table_lock;
std::mutex map_physical_memory_lock;
mutable KLightLock general_lock;
mutable KLightLock map_physical_memory_lock;
std::unique_ptr<KMemoryBlockManager> block_manager;
public:
@@ -205,7 +211,7 @@ public:
return alias_code_region_end - alias_code_region_start;
}
size_t GetNormalMemorySize() {
std::lock_guard lk(page_table_lock);
KScopedLightLock lk(general_lock);
return GetHeapSize() + mapped_physical_memory_size;
}
constexpr std::size_t GetAddressSpaceWidth() const {
@@ -247,7 +253,8 @@ public:
constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
return !IsOutsideASLRRegion(address, size);
}
constexpr PAddr GetPhysicalAddr(VAddr addr) {
PAddr GetPhysicalAddr(VAddr addr) const {
const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits];
ASSERT(backing_addr);
return backing_addr + addr;
@@ -303,6 +310,8 @@ private:
bool is_kernel{};
bool is_aslr_enabled{};
u32 heap_fill_value{};
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};

View File

@@ -123,12 +123,11 @@ private:
};
ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
ProcessType type) {
ProcessType type, KResourceLimit* res_limit) {
auto& kernel = system.Kernel();
process->name = std::move(process_name);
process->resource_limit = kernel.GetSystemResourceLimit();
process->resource_limit = res_limit;
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
@@ -143,9 +142,6 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
kernel.AppendNewProcess(process);
// Open a reference to the resource limit.
process->resource_limit->Open();
// Clear remaining fields.
process->num_running_threads = 0;
process->is_signaled = false;
@@ -153,6 +149,9 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
process->is_suspended = false;
process->schedule_count = 0;
// Open a reference to the resource limit.
process->resource_limit->Open();
return ResultSuccess;
}

View File

@@ -91,7 +91,7 @@ public:
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
static ResultCode Initialize(KProcess* process, Core::System& system, std::string process_name,
ProcessType type);
ProcessType type, KResourceLimit* res_limit);
/// Gets a reference to the process' page table.
KPageTable& PageTable() {

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/svc_results.h"
@@ -151,4 +152,22 @@ void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
}
}
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) {
auto* resource_limit = KResourceLimit::Create(system.Kernel());
resource_limit->Initialize(&system.CoreTiming());
// Initialize default resource limit values.
// TODO(bunnei): These values are the system defaults, the limits for service processes are
// lower. These should use the correct limit values.
ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size)
.IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
return resource_limit;
}
} // namespace Kernel

View File

@@ -67,4 +67,7 @@ private:
KLightConditionVariable cond_var;
const Core::Timing::CoreTiming* core_timing{};
};
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size);
} // namespace Kernel

View File

@@ -7,6 +7,7 @@
#include <atomic>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Kernel {
@@ -15,13 +16,17 @@ class KernelCore;
namespace impl {
class KSlabHeapImpl final : NonCopyable {
class KSlabHeapImpl final {
public:
YUZU_NON_COPYABLE(KSlabHeapImpl);
YUZU_NON_MOVEABLE(KSlabHeapImpl);
struct Node {
Node* next{};
};
constexpr KSlabHeapImpl() = default;
constexpr ~KSlabHeapImpl() = default;
void Initialize(std::size_t size) {
ASSERT(head == nullptr);
@@ -64,9 +69,13 @@ private:
} // namespace impl
class KSlabHeapBase : NonCopyable {
class KSlabHeapBase {
public:
YUZU_NON_COPYABLE(KSlabHeapBase);
YUZU_NON_MOVEABLE(KSlabHeapBase);
constexpr KSlabHeapBase() = default;
constexpr ~KSlabHeapBase() = default;
constexpr bool Contains(uintptr_t addr) const {
return start <= addr && addr < end;

View File

@@ -70,13 +70,12 @@ struct KernelCore::Impl {
// Derive the initial memory layout from the emulated board
Init::InitializeSlabResourceCounts(kernel);
KMemoryLayout memory_layout;
DeriveInitialMemoryLayout(memory_layout);
Init::InitializeSlabHeaps(system, memory_layout);
DeriveInitialMemoryLayout();
Init::InitializeSlabHeaps(system, *memory_layout);
// Initialize kernel memory and resources.
InitializeSystemResourceLimit(kernel, system.CoreTiming(), memory_layout);
InitializeMemoryLayout(memory_layout);
InitializeSystemResourceLimit(kernel, system.CoreTiming());
InitializeMemoryLayout();
InitializePageSlab();
InitializeSchedulers();
InitializeSuspendThreads();
@@ -219,12 +218,11 @@ struct KernelCore::Impl {
// Creates the default system resource limit
void InitializeSystemResourceLimit(KernelCore& kernel,
const Core::Timing::CoreTiming& core_timing,
const KMemoryLayout& memory_layout) {
const Core::Timing::CoreTiming& core_timing) {
system_resource_limit = KResourceLimit::Create(system.Kernel());
system_resource_limit->Initialize(&core_timing);
const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes();
const auto [total_size, kernel_size] = memory_layout->GetTotalAndKernelMemorySizes();
// If setting the default system values fails, then something seriously wrong has occurred.
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size)
@@ -240,13 +238,6 @@ struct KernelCore::Impl {
constexpr u64 secure_applet_memory_size{4_MiB};
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
secure_applet_memory_size));
// This memory seems to be reserved on hardware, but is not reserved/used by yuzu.
// Likely Horizon OS reserved memory
// TODO(ameerj): Derive the memory rather than hardcode it.
constexpr u64 unknown_reserved_memory{0x2f896000};
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
unknown_reserved_memory));
}
void InitializePreemption(KernelCore& kernel) {
@@ -360,16 +351,18 @@ struct KernelCore::Impl {
return schedulers[thread_id]->GetCurrentThread();
}
void DeriveInitialMemoryLayout(KMemoryLayout& memory_layout) {
void DeriveInitialMemoryLayout() {
memory_layout = std::make_unique<KMemoryLayout>();
// Insert the root region for the virtual memory tree, from which all other regions will
// derive.
memory_layout.GetVirtualMemoryRegionTree().InsertDirectly(
memory_layout->GetVirtualMemoryRegionTree().InsertDirectly(
KernelVirtualAddressSpaceBase,
KernelVirtualAddressSpaceBase + KernelVirtualAddressSpaceSize - 1);
// Insert the root region for the physical memory tree, from which all other regions will
// derive.
memory_layout.GetPhysicalMemoryRegionTree().InsertDirectly(
memory_layout->GetPhysicalMemoryRegionTree().InsertDirectly(
KernelPhysicalAddressSpaceBase,
KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceSize - 1);
@@ -386,7 +379,7 @@ struct KernelCore::Impl {
if (!(kernel_region_start + KernelRegionSize - 1 <= KernelVirtualAddressSpaceLast)) {
kernel_region_size = KernelVirtualAddressSpaceEnd - kernel_region_start;
}
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
kernel_region_start, kernel_region_size, KMemoryRegionType_Kernel));
// Setup the code region.
@@ -395,11 +388,11 @@ struct KernelCore::Impl {
Common::AlignDown(code_start_virt_addr, CodeRegionAlign);
constexpr VAddr code_region_end = Common::AlignUp(code_end_virt_addr, CodeRegionAlign);
constexpr size_t code_region_size = code_region_end - code_region_start;
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
code_region_start, code_region_size, KMemoryRegionType_KernelCode));
// Setup board-specific device physical regions.
Init::SetupDevicePhysicalMemoryRegions(memory_layout);
Init::SetupDevicePhysicalMemoryRegions(*memory_layout);
// Determine the amount of space needed for the misc region.
size_t misc_region_needed_size;
@@ -408,7 +401,7 @@ struct KernelCore::Impl {
misc_region_needed_size = Core::Hardware::NUM_CPU_CORES * (3 * (PageSize + PageSize));
// Account for each auto-map device.
for (const auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
for (const auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
if (region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) {
// Check that the region is valid.
ASSERT(region.GetEndAddress() != 0);
@@ -433,22 +426,22 @@ struct KernelCore::Impl {
// Setup the misc region.
const VAddr misc_region_start =
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
misc_region_size, MiscRegionAlign, KMemoryRegionType_Kernel);
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc));
// Setup the stack region.
constexpr size_t StackRegionSize = 14_MiB;
constexpr size_t StackRegionAlign = KernelAslrAlignment;
const VAddr stack_region_start =
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel);
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack));
// Determine the size of the resource region.
const size_t resource_region_size = memory_layout.GetResourceRegionSizeForInit();
const size_t resource_region_size = memory_layout->GetResourceRegionSizeForInit();
// Determine the size of the slab region.
const size_t slab_region_size =
@@ -465,23 +458,23 @@ struct KernelCore::Impl {
Common::AlignUp(code_end_phys_addr + slab_region_size, SlabRegionAlign) -
Common::AlignDown(code_end_phys_addr, SlabRegionAlign);
const VAddr slab_region_start =
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
slab_region_needed_size, SlabRegionAlign, KMemoryRegionType_Kernel) +
(code_end_phys_addr % SlabRegionAlign);
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab));
// Setup the temp region.
constexpr size_t TempRegionSize = 128_MiB;
constexpr size_t TempRegionAlign = KernelAslrAlignment;
const VAddr temp_region_start =
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel);
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize,
KMemoryRegionType_KernelTemp));
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize,
KMemoryRegionType_KernelTemp));
// Automatically map in devices that have auto-map attributes.
for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
// We only care about kernel regions.
if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) {
continue;
@@ -508,21 +501,21 @@ struct KernelCore::Impl {
const size_t map_size =
Common::AlignUp(region.GetEndAddress(), PageSize) - map_phys_addr;
const VAddr map_virt_addr =
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize);
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
map_virt_addr, map_size, KMemoryRegionType_KernelMiscMappedDevice));
region.SetPairAddress(map_virt_addr + region.GetAddress() - map_phys_addr);
}
Init::SetupDramPhysicalMemoryRegions(memory_layout);
Init::SetupDramPhysicalMemoryRegions(*memory_layout);
// Insert a physical region for the kernel code region.
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
code_start_phys_addr, code_region_size, KMemoryRegionType_DramKernelCode));
// Insert a physical region for the kernel slab region.
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
slab_start_phys_addr, slab_region_size, KMemoryRegionType_DramKernelSlab));
// Determine size available for kernel page table heaps, requiring > 8 MB.
@@ -531,12 +524,12 @@ struct KernelCore::Impl {
ASSERT(page_table_heap_size / 4_MiB > 2);
// Insert a physical region for the kernel page table heap region
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
slab_end_phys_addr, page_table_heap_size, KMemoryRegionType_DramKernelPtHeap));
// All DRAM regions that we haven't tagged by this point will be mapped under the linear
// mapping. Tag them.
for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
if (region.GetType() == KMemoryRegionType_Dram) {
// Check that the region is valid.
ASSERT(region.GetEndAddress() != 0);
@@ -548,7 +541,7 @@ struct KernelCore::Impl {
// Get the linear region extents.
const auto linear_extents =
memory_layout.GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
memory_layout->GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionAttr_LinearMapped);
ASSERT(linear_extents.GetEndAddress() != 0);
@@ -560,7 +553,7 @@ struct KernelCore::Impl {
Common::AlignUp(linear_extents.GetEndAddress(), LinearRegionAlign) -
aligned_linear_phys_start;
const VAddr linear_region_start =
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
linear_region_size, LinearRegionAlign, KMemoryRegionType_None, LinearRegionAlign);
const u64 linear_region_phys_to_virt_diff = linear_region_start - aligned_linear_phys_start;
@@ -569,7 +562,7 @@ struct KernelCore::Impl {
{
PAddr cur_phys_addr = 0;
u64 cur_size = 0;
for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) {
continue;
}
@@ -588,55 +581,49 @@ struct KernelCore::Impl {
const VAddr region_virt_addr =
region.GetAddress() + linear_region_phys_to_virt_diff;
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
region_virt_addr, region.GetSize(),
GetTypeForVirtualLinearMapping(region.GetType())));
region.SetPairAddress(region_virt_addr);
KMemoryRegion* virt_region =
memory_layout.GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr);
memory_layout->GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr);
ASSERT(virt_region != nullptr);
virt_region->SetPairAddress(region.GetAddress());
}
}
// Insert regions for the initial page table region.
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
resource_end_phys_addr, KernelPageTableHeapSize, KMemoryRegionType_DramKernelInitPt));
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
resource_end_phys_addr + linear_region_phys_to_virt_diff, KernelPageTableHeapSize,
KMemoryRegionType_VirtualDramKernelInitPt));
// All linear-mapped DRAM regions that we haven't tagged by this point will be allocated to
// some pool partition. Tag them.
for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
if (region.GetType() == (KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped)) {
region.SetType(KMemoryRegionType_DramPoolPartition);
}
}
// Setup all other memory regions needed to arrange the pool partitions.
Init::SetupPoolPartitionMemoryRegions(memory_layout);
Init::SetupPoolPartitionMemoryRegions(*memory_layout);
// Cache all linear regions in their own trees for faster access, later.
memory_layout.InitializeLinearMemoryRegionTrees(aligned_linear_phys_start,
linear_region_start);
memory_layout->InitializeLinearMemoryRegionTrees(aligned_linear_phys_start,
linear_region_start);
}
void InitializeMemoryLayout(const KMemoryLayout& memory_layout) {
const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents();
const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents();
const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents();
void InitializeMemoryLayout() {
const auto system_pool = memory_layout->GetKernelSystemPoolRegionPhysicalExtents();
// Initialize memory managers
// Initialize the memory manager.
memory_manager = std::make_unique<KMemoryManager>(system);
memory_manager->InitializeManager(KMemoryManager::Pool::Application,
application_pool.GetAddress(),
application_pool.GetEndAddress());
memory_manager->InitializeManager(KMemoryManager::Pool::Applet, applet_pool.GetAddress(),
applet_pool.GetEndAddress());
memory_manager->InitializeManager(KMemoryManager::Pool::System, system_pool.GetAddress(),
system_pool.GetEndAddress());
const auto& management_region = memory_layout->GetPoolManagementRegion();
ASSERT(management_region.GetEndAddress() != 0);
memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize());
// Setup memory regions for emulated processes
// TODO(bunnei): These should not be hardcoded regions initialized within the kernel
@@ -777,6 +764,9 @@ struct KernelCore::Impl {
Kernel::KSharedMemory* irs_shared_mem{};
Kernel::KSharedMemory* time_shared_mem{};
// Memory layout
std::unique_ptr<KMemoryLayout> memory_layout;
// Threads used for services
std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
Common::ThreadWorker service_threads_manager;
@@ -1142,6 +1132,10 @@ const KWorkerTaskManager& KernelCore::WorkerTaskManager() const {
return impl->worker_task_manager;
}
const KMemoryLayout& KernelCore::MemoryLayout() const {
return *impl->memory_layout;
}
bool KernelCore::IsPhantomModeForSingleCore() const {
return impl->IsPhantomModeForSingleCore();
}

View File

@@ -41,6 +41,7 @@ class KClientSession;
class KEvent;
class KHandleTable;
class KLinkedListNode;
class KMemoryLayout;
class KMemoryManager;
class KPort;
class KProcess;
@@ -350,6 +351,9 @@ public:
/// Gets the current worker task manager, used for dispatching KThread/KProcess tasks.
const KWorkerTaskManager& WorkerTaskManager() const;
/// Gets the memory layout.
const KMemoryLayout& MemoryLayout() const;
private:
friend class KProcess;
friend class KThread;

View File

@@ -396,7 +396,7 @@ static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle
// Get the process id.
*out_process_id = process->GetId();
return ResultInvalidHandle;
return ResultSuccess;
}
static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low,
@@ -645,6 +645,10 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
LOG_DEBUG(Debug_Emulated, "{}", str);
}
static void OutputDebugString32(Core::System& system, u32 address, u32 len) {
OutputDebugString(system, address, len);
}
/// Gets system/memory information for the current process
static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
u64 info_sub_id) {
@@ -1404,7 +1408,7 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
}
static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}",
LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}",
static_cast<void*>(out), address, size);
// Get kernel instance.
auto& kernel = system.Kernel();
@@ -1438,6 +1442,10 @@ static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr addr
return ResultSuccess;
}
static ResultCode CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
return CreateCodeMemory(system, out, address, size);
}
static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
VAddr address, size_t size, Svc::MemoryPermission perm) {
@@ -1517,6 +1525,12 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han
return ResultSuccess;
}
static ResultCode ControlCodeMemory32(Core::System& system, Handle code_memory_handle,
u32 operation, u64 address, u64 size,
Svc::MemoryPermission perm) {
return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm);
}
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
VAddr page_info_address, Handle process_handle,
VAddr address) {
@@ -2318,7 +2332,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o
R_UNLESS(event != nullptr, ResultOutOfResource);
// Initialize the event.
event->Initialize("CreateEvent");
event->Initialize("CreateEvent", kernel.CurrentProcess());
// Commit the thread reservation.
event_reservation.Commit();
@@ -2559,9 +2573,9 @@ struct FunctionDef {
} // namespace
static const FunctionDef SVC_Table_32[] = {
{0x00, nullptr, "Unknown"},
{0x00, nullptr, "Unknown0"},
{0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
{0x02, nullptr, "Unknown"},
{0x02, nullptr, "SetMemoryPermission32"},
{0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"},
{0x04, SvcWrap32<MapMemory32>, "MapMemory32"},
{0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"},
@@ -2591,97 +2605,97 @@ static const FunctionDef SVC_Table_32[] = {
{0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
{0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"},
{0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
{0x20, nullptr, "Unknown"},
{0x20, nullptr, "SendSyncRequestLight32"},
{0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
{0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
{0x23, nullptr, "Unknown"},
{0x23, nullptr, "SendAsyncRequestWithUserBuffer32"},
{0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"},
{0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
{0x26, SvcWrap32<Break32>, "Break32"},
{0x27, nullptr, "OutputDebugString32"},
{0x28, nullptr, "Unknown"},
{0x27, SvcWrap32<OutputDebugString32>, "OutputDebugString32"},
{0x28, nullptr, "ReturnFromException32"},
{0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
{0x2a, nullptr, "Unknown"},
{0x2b, nullptr, "Unknown"},
{0x2a, nullptr, "FlushEntireDataCache32"},
{0x2b, nullptr, "FlushDataCache32"},
{0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"},
{0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"},
{0x2e, nullptr, "Unknown"},
{0x2f, nullptr, "Unknown"},
{0x30, nullptr, "Unknown"},
{0x31, nullptr, "Unknown"},
{0x2e, nullptr, "GetDebugFutureThreadInfo32"},
{0x2f, nullptr, "GetLastThreadInfo32"},
{0x30, nullptr, "GetResourceLimitLimitValue32"},
{0x31, nullptr, "GetResourceLimitCurrentValue32"},
{0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"},
{0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"},
{0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"},
{0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"},
{0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"},
{0x37, nullptr, "Unknown"},
{0x38, nullptr, "Unknown"},
{0x39, nullptr, "Unknown"},
{0x3a, nullptr, "Unknown"},
{0x3b, nullptr, "Unknown"},
{0x3c, nullptr, "Unknown"},
{0x3d, nullptr, "Unknown"},
{0x3e, nullptr, "Unknown"},
{0x3f, nullptr, "Unknown"},
{0x37, nullptr, "GetResourceLimitPeakValue32"},
{0x38, nullptr, "Unknown38"},
{0x39, nullptr, "CreateIoPool32"},
{0x3a, nullptr, "CreateIoRegion32"},
{0x3b, nullptr, "Unknown3b"},
{0x3c, nullptr, "KernelDebug32"},
{0x3d, nullptr, "ChangeKernelTraceState32"},
{0x3e, nullptr, "Unknown3e"},
{0x3f, nullptr, "Unknown3f"},
{0x40, nullptr, "CreateSession32"},
{0x41, nullptr, "AcceptSession32"},
{0x42, nullptr, "Unknown"},
{0x42, nullptr, "ReplyAndReceiveLight32"},
{0x43, nullptr, "ReplyAndReceive32"},
{0x44, nullptr, "Unknown"},
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer32"},
{0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x48, nullptr, "Unknown"},
{0x49, nullptr, "Unknown"},
{0x4a, nullptr, "Unknown"},
{0x4b, nullptr, "Unknown"},
{0x4c, nullptr, "Unknown"},
{0x4d, nullptr, "Unknown"},
{0x4e, nullptr, "Unknown"},
{0x4f, nullptr, "Unknown"},
{0x50, nullptr, "Unknown"},
{0x51, nullptr, "Unknown"},
{0x52, nullptr, "Unknown"},
{0x53, nullptr, "Unknown"},
{0x54, nullptr, "Unknown"},
{0x55, nullptr, "Unknown"},
{0x56, nullptr, "Unknown"},
{0x57, nullptr, "Unknown"},
{0x58, nullptr, "Unknown"},
{0x59, nullptr, "Unknown"},
{0x5a, nullptr, "Unknown"},
{0x5b, nullptr, "Unknown"},
{0x5c, nullptr, "Unknown"},
{0x5d, nullptr, "Unknown"},
{0x5e, nullptr, "Unknown"},
{0x46, nullptr, "MapIoRegion32"},
{0x47, nullptr, "UnmapIoRegion32"},
{0x48, nullptr, "MapPhysicalMemoryUnsafe32"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe32"},
{0x4a, nullptr, "SetUnsafeLimit32"},
{0x4b, SvcWrap32<CreateCodeMemory32>, "CreateCodeMemory32"},
{0x4c, SvcWrap32<ControlCodeMemory32>, "ControlCodeMemory32"},
{0x4d, nullptr, "SleepSystem32"},
{0x4e, nullptr, "ReadWriteRegister32"},
{0x4f, nullptr, "SetProcessActivity32"},
{0x50, nullptr, "CreateSharedMemory32"},
{0x51, nullptr, "MapTransferMemory32"},
{0x52, nullptr, "UnmapTransferMemory32"},
{0x53, nullptr, "CreateInterruptEvent32"},
{0x54, nullptr, "QueryPhysicalAddress32"},
{0x55, nullptr, "QueryIoMapping32"},
{0x56, nullptr, "CreateDeviceAddressSpace32"},
{0x57, nullptr, "AttachDeviceAddressSpace32"},
{0x58, nullptr, "DetachDeviceAddressSpace32"},
{0x59, nullptr, "MapDeviceAddressSpaceByForce32"},
{0x5a, nullptr, "MapDeviceAddressSpaceAligned32"},
{0x5b, nullptr, "MapDeviceAddressSpace32"},
{0x5c, nullptr, "UnmapDeviceAddressSpace32"},
{0x5d, nullptr, "InvalidateProcessDataCache32"},
{0x5e, nullptr, "StoreProcessDataCache32"},
{0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"},
{0x60, nullptr, "Unknown"},
{0x61, nullptr, "Unknown"},
{0x62, nullptr, "Unknown"},
{0x63, nullptr, "Unknown"},
{0x64, nullptr, "Unknown"},
{0x60, nullptr, "StoreProcessDataCache32"},
{0x61, nullptr, "BreakDebugProcess32"},
{0x62, nullptr, "TerminateDebugProcess32"},
{0x63, nullptr, "GetDebugEvent32"},
{0x64, nullptr, "ContinueDebugEvent32"},
{0x65, nullptr, "GetProcessList32"},
{0x66, nullptr, "Unknown"},
{0x67, nullptr, "Unknown"},
{0x68, nullptr, "Unknown"},
{0x69, nullptr, "Unknown"},
{0x6A, nullptr, "Unknown"},
{0x6B, nullptr, "Unknown"},
{0x6C, nullptr, "Unknown"},
{0x6D, nullptr, "Unknown"},
{0x6E, nullptr, "Unknown"},
{0x66, nullptr, "GetThreadList"},
{0x67, nullptr, "GetDebugThreadContext32"},
{0x68, nullptr, "SetDebugThreadContext32"},
{0x69, nullptr, "QueryDebugProcessMemory32"},
{0x6A, nullptr, "ReadDebugProcessMemory32"},
{0x6B, nullptr, "WriteDebugProcessMemory32"},
{0x6C, nullptr, "SetHardwareBreakPoint32"},
{0x6D, nullptr, "GetDebugThreadParam32"},
{0x6E, nullptr, "Unknown6E"},
{0x6f, nullptr, "GetSystemInfo32"},
{0x70, nullptr, "CreatePort32"},
{0x71, nullptr, "ManageNamedPort32"},
{0x72, nullptr, "ConnectToPort32"},
{0x73, nullptr, "SetProcessMemoryPermission32"},
{0x74, nullptr, "Unknown"},
{0x75, nullptr, "Unknown"},
{0x76, nullptr, "Unknown"},
{0x74, nullptr, "MapProcessMemory32"},
{0x75, nullptr, "UnmapProcessMemory32"},
{0x76, nullptr, "QueryProcessMemory32"},
{0x77, nullptr, "MapProcessCodeMemory32"},
{0x78, nullptr, "UnmapProcessCodeMemory32"},
{0x79, nullptr, "Unknown"},
{0x7A, nullptr, "Unknown"},
{0x79, nullptr, "CreateProcess32"},
{0x7A, nullptr, "StartProcess32"},
{0x7B, nullptr, "TerminateProcess32"},
{0x7C, nullptr, "GetProcessInfo32"},
{0x7D, nullptr, "CreateResourceLimit32"},
@@ -2754,7 +2768,7 @@ static const FunctionDef SVC_Table_32[] = {
};
static const FunctionDef SVC_Table_64[] = {
{0x00, nullptr, "Unknown"},
{0x00, nullptr, "Unknown0"},
{0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
{0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
{0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
@@ -2809,23 +2823,23 @@ static const FunctionDef SVC_Table_64[] = {
{0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
{0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
{0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
{0x37, nullptr, "Unknown"},
{0x38, nullptr, "Unknown"},
{0x39, nullptr, "Unknown"},
{0x3A, nullptr, "Unknown"},
{0x3B, nullptr, "Unknown"},
{0x37, nullptr, "GetResourceLimitPeakValue"},
{0x38, nullptr, "Unknown38"},
{0x39, nullptr, "CreateIoPool"},
{0x3A, nullptr, "CreateIoRegion"},
{0x3B, nullptr, "Unknown3B"},
{0x3C, SvcWrap64<KernelDebug>, "KernelDebug"},
{0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
{0x3E, nullptr, "Unknown"},
{0x3F, nullptr, "Unknown"},
{0x3E, nullptr, "Unknown3e"},
{0x3F, nullptr, "Unknown3f"},
{0x40, nullptr, "CreateSession"},
{0x41, nullptr, "AcceptSession"},
{0x42, nullptr, "ReplyAndReceiveLight"},
{0x43, nullptr, "ReplyAndReceive"},
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
{0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x46, nullptr, "MapIoRegion"},
{0x47, nullptr, "UnmapIoRegion"},
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
{0x4A, nullptr, "SetUnsafeLimit"},
@@ -2864,7 +2878,7 @@ static const FunctionDef SVC_Table_64[] = {
{0x6B, nullptr, "WriteDebugProcessMemory"},
{0x6C, nullptr, "SetHardwareBreakPoint"},
{0x6D, nullptr, "GetDebugThreadParam"},
{0x6E, nullptr, "Unknown"},
{0x6E, nullptr, "Unknown6E"},
{0x6F, nullptr, "GetSystemInfo"},
{0x70, nullptr, "CreatePort"},
{0x71, nullptr, "ManageNamedPort"},

View File

@@ -669,4 +669,26 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval);
}
// Used by CreateCodeMemory32
template <ResultCode func(Core::System&, Handle*, u32, u32)>
void SvcWrap32(Core::System& system) {
Handle handle = 0;
const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2)).raw;
system.CurrentArmInterface().SetReg(1, handle);
FuncReturn(system, retval);
}
// Used by ControlCodeMemory32
template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, Param32(system, 0), Param32(system, 1), Param(system, 2), Param(system, 4),
static_cast<Svc::MemoryPermission>(Param32(system, 6)))
.raw;
FuncReturn(system, retval);
}
} // namespace Kernel

View File

@@ -39,9 +39,9 @@ constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
// Thumbnails are hard coded to be at least this size
constexpr std::size_t THUMBNAIL_SIZE = 0x24000;
static std::filesystem::path GetImagePath(Common::UUID uuid) {
static std::filesystem::path GetImagePath(const Common::UUID& uuid) {
return Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch());
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
}
static constexpr u32 SanitizeJPEGSize(std::size_t size) {
@@ -290,7 +290,7 @@ public:
protected:
void Get(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
ProfileBase profile_base{};
ProfileData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
@@ -300,21 +300,21 @@ protected:
rb.PushRaw(profile_base);
} else {
LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}",
user_id.Format());
user_id.RawString());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
}
}
void GetBase(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
ProfileBase profile_base{};
if (profile_manager.GetProfileBase(user_id, profile_base)) {
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(ResultSuccess);
rb.PushRaw(profile_base);
} else {
LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.Format());
LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.RawString());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
}
@@ -373,7 +373,7 @@ protected:
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.Format());
base.timestamp, base.user_uuid.RawString());
if (user_data.size() < sizeof(ProfileData)) {
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
@@ -406,7 +406,7 @@ protected:
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.Format());
base.timestamp, base.user_uuid.RawString());
if (user_data.size() < sizeof(ProfileData)) {
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
@@ -435,7 +435,7 @@ protected:
}
ProfileManager& profile_manager;
Common::UUID user_id{Common::INVALID_UUID}; ///< The user id this profile refers to.
Common::UUID user_id{}; ///< The user id this profile refers to.
};
class IProfile final : public IProfileCommon {
@@ -547,7 +547,7 @@ private:
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw<u64>(user_id.GetNintendoID());
rb.PushRaw<u64>(user_id.Hash());
}
void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) {
@@ -577,7 +577,7 @@ private:
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw<u64>(user_id.GetNintendoID());
rb.PushRaw<u64>(user_id.Hash());
}
void StoreOpenContext(Kernel::HLERequestContext& ctx) {
@@ -587,7 +587,7 @@ private:
}
std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{};
Common::UUID user_id{Common::INVALID_UUID};
Common::UUID user_id{};
};
// 6.0.0+
@@ -687,7 +687,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -718,7 +718,7 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -833,7 +833,7 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.Format());
LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.RawString());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -875,7 +875,7 @@ void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestCont
IPC::RequestParser rp{ctx};
const auto uuid = rp.PopRaw<Common::UUID>();
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.Format());
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.RawString());
// TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
// way of confirming things like the TID, we're going to assume a non zero value for the time
@@ -889,7 +889,7 @@ void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext&
const auto uuid = rp.PopRaw<Common::UUID>();
const auto tid = rp.Pop<u64_le>();
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.Format(), tid);
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.RawString(), tid);
StoreSaveDataThumbnail(ctx, uuid, tid);
}
@@ -903,7 +903,7 @@ void Module::Interface::StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx,
return;
}
if (!uuid) {
if (uuid.IsInvalid()) {
LOG_ERROR(Service_ACC, "User ID is not valid!");
rb.Push(ERR_INVALID_USER_ID);
return;
@@ -927,20 +927,20 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
IPC::ResponseBuilder rb{ctx, 6};
if (profile_manager->GetUserCount() != 1) {
rb.Push(ResultSuccess);
rb.PushRaw<u128>(Common::INVALID_UUID);
rb.PushRaw(Common::InvalidUUID);
return;
}
const auto user_list = profile_manager->GetAllUsers();
if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) {
rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code
rb.PushRaw<u128>(Common::INVALID_UUID);
rb.PushRaw(Common::InvalidUUID);
return;
}
// Select the first user we have
rb.Push(ResultSuccess);
rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid);
rb.PushRaw(profile_manager->GetUser(0)->uuid);
}
Module::Interface::Interface(std::shared_ptr<Module> module_,

View File

@@ -19,8 +19,8 @@ namespace FS = Common::FS;
using Common::UUID;
struct UserRaw {
UUID uuid{Common::INVALID_UUID};
UUID uuid2{Common::INVALID_UUID};
UUID uuid{};
UUID uuid2{};
u64 timestamp{};
ProfileUsername username{};
ProfileData extra_data{};
@@ -45,7 +45,7 @@ ProfileManager::ProfileManager() {
// Create an user if none are present
if (user_count == 0) {
CreateNewUser(UUID::Generate(), "yuzu");
CreateNewUser(UUID::MakeRandom(), "yuzu");
}
auto current =
@@ -101,7 +101,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
if (user_count == MAX_USERS) {
return ERROR_TOO_MANY_USERS;
}
if (!uuid) {
if (uuid.IsInvalid()) {
return ERROR_ARGUMENT_IS_NULL;
}
if (username[0] == 0x0) {
@@ -145,7 +145,7 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
/// Returns a users profile index based on their user id.
std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
if (!uuid) {
if (uuid.IsInvalid()) {
return std::nullopt;
}
@@ -250,9 +250,10 @@ UserIDArray ProfileManager::GetOpenUsers() const {
std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) {
if (p.is_open)
return p.user_uuid;
return UUID{Common::INVALID_UUID};
return Common::InvalidUUID;
});
std::stable_partition(output.begin(), output.end(), [](const UUID& uuid) { return uuid; });
std::stable_partition(output.begin(), output.end(),
[](const UUID& uuid) { return uuid.IsValid(); });
return output;
}
@@ -299,7 +300,7 @@ bool ProfileManager::RemoveUser(UUID uuid) {
profiles[*index] = ProfileInfo{};
std::stable_partition(profiles.begin(), profiles.end(),
[](const ProfileInfo& profile) { return profile.user_uuid; });
[](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
return true;
}
@@ -361,7 +362,7 @@ void ProfileManager::ParseUserSaveFile() {
}
std::stable_partition(profiles.begin(), profiles.end(),
[](const ProfileInfo& profile) { return profile.user_uuid; });
[](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
}
void ProfileManager::WriteUserSaveFile() {

View File

@@ -35,7 +35,7 @@ static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
struct ProfileInfo {
Common::UUID user_uuid{Common::INVALID_UUID};
Common::UUID user_uuid{};
ProfileUsername username{};
u64 creation_time{};
ProfileData data{}; // TODO(ognik): Work out what this is
@@ -49,7 +49,7 @@ struct ProfileBase {
// Zero out all the fields to make the profile slot considered "Empty"
void Invalidate() {
user_uuid.Invalidate();
user_uuid = {};
timestamp = 0;
username.fill(0);
}
@@ -103,7 +103,7 @@ private:
std::array<ProfileInfo, MAX_USERS> profiles{};
std::size_t user_count{};
Common::UUID last_opened_user{Common::INVALID_UUID};
Common::UUID last_opened_user{};
};
}; // namespace Service::Account

View File

@@ -55,7 +55,7 @@ constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
struct LaunchParameterAccountPreselectedUser {
u32_le magic;
u32_le is_account_selected;
u128 current_user;
Common::UUID current_user;
INSERT_PADDING_BYTES(0x70);
};
static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
@@ -618,7 +618,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) {
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
if (messages.empty()) {
on_new_message->GetWritableEvent().Clear();
return AppletMessage::NoMessage;
return AppletMessage::None;
}
auto msg = messages.front();
messages.pop();
@@ -633,7 +633,7 @@ std::size_t AppletMessageQueue::GetMessageCount() const {
}
void AppletMessageQueue::RequestExit() {
PushMessage(AppletMessage::ExitRequested);
PushMessage(AppletMessage::Exit);
}
void AppletMessageQueue::FocusStateChanged() {
@@ -732,7 +732,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
const auto message = msg_queue->PopMessage();
IPC::ResponseBuilder rb{ctx, 3};
if (message == AppletMessageQueue::AppletMessage::NoMessage) {
if (message == AppletMessageQueue::AppletMessage::None) {
LOG_ERROR(Service_AM, "Message queue is empty");
rb.Push(ERR_NO_MESSAGES);
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
@@ -1453,8 +1453,8 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
Account::ProfileManager profile_manager{};
const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user));
ASSERT(uuid);
params.current_user = uuid->uuid;
ASSERT(uuid.has_value() && uuid->IsValid());
params.current_user = *uuid;
IPC::ResponseBuilder rb{ctx, 2, 0, 1};

View File

@@ -22,6 +22,7 @@ class NVFlinger;
namespace Service::AM {
// This is nn::settings::Language
enum SystemLanguage {
Japanese = 0,
English = 1, // en-US
@@ -41,16 +42,44 @@ enum SystemLanguage {
// 4.0.0+
SimplifiedChinese = 15,
TraditionalChinese = 16,
// 10.1.0+
BrazilianPortuguese = 17,
};
class AppletMessageQueue {
public:
// This is nn::am::AppletMessage
enum class AppletMessage : u32 {
NoMessage = 0,
ExitRequested = 4,
None = 0,
ChangeIntoForeground = 1,
ChangeIntoBackground = 2,
Exit = 4,
ApplicationExited = 6,
FocusStateChanged = 15,
Resume = 16,
DetectShortPressingHomeButton = 20,
DetectLongPressingHomeButton = 21,
DetectShortPressingPowerButton = 22,
DetectMiddlePressingPowerButton = 23,
DetectLongPressingPowerButton = 24,
RequestToPrepareSleep = 25,
FinishedSleepSequence = 26,
SleepRequiredByHighTemperature = 27,
SleepRequiredByLowBattery = 28,
AutoPowerDown = 29,
OperationModeChanged = 30,
PerformanceModeChanged = 31,
DetectReceivingCecSystemStandby = 32,
SdCardRemoved = 33,
LaunchApplicationRequested = 50,
RequestToDisplay = 51,
ShowApplicationLogo = 55,
HideApplicationLogo = 56,
ForceHideApplicationLogo = 57,
FloatingApplicationDetected = 60,
DetectShortPressingCaptureButton = 90,
AlbumScreenShotTaken = 92,
AlbumRecordingSaved = 93,
};
explicit AppletMessageQueue(Core::System& system);
@@ -179,11 +208,14 @@ public:
~ICommonStateGetter() override;
private:
// This is nn::oe::FocusState
enum class FocusState : u8 {
InFocus = 1,
NotInFocus = 2,
Background = 3,
};
// This is nn::oe::OperationMode
enum class OperationMode : u8 {
Handheld = 0,
Docked = 1,

View File

@@ -62,11 +62,11 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
if (uuid.has_value() && uuid->IsValid()) {
output.result = 0;
output.uuid_selected = uuid->uuid;
output.uuid_selected = *uuid;
} else {
status = ERR_USER_CANCELLED_SELECTION;
output.result = ERR_USER_CANCELLED_SELECTION.raw;
output.uuid_selected = Common::INVALID_UUID;
output.uuid_selected = Common::InvalidUUID;
}
final_data = std::vector<u8>(sizeof(UserSelectionOutput));

View File

@@ -27,7 +27,7 @@ static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has inco
struct UserSelectionOutput {
u64 result;
u128 uuid_selected;
Common::UUID uuid_selected;
};
static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size.");

View File

@@ -17,8 +17,8 @@ constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Con
Controller::Controller(Core::Timing::CoreTiming& core_timing_)
: core_timing{core_timing_}, configs{
{PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION},
{PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION},
{PerformanceMode::Normal, DEFAULT_PERFORMANCE_CONFIGURATION},
{PerformanceMode::Boost, DEFAULT_PERFORMANCE_CONFIGURATION},
} {}
Controller::~Controller() = default;
@@ -63,13 +63,13 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
PerformanceConfiguration::Config15,
}};
SetPerformanceConfiguration(PerformanceMode::Docked,
SetPerformanceConfiguration(PerformanceMode::Boost,
BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode)));
}
PerformanceMode Controller::GetCurrentPerformanceMode() const {
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked
: PerformanceMode::Handheld;
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost
: PerformanceMode::Normal;
}
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {

View File

@@ -32,15 +32,18 @@ enum class PerformanceConfiguration : u32 {
Config16 = 0x9222000C,
};
// This is nn::oe::CpuBoostMode
enum class CpuBoostMode : u32 {
Disabled = 0,
Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16
Partial = 2, // GPU Only -> Config 15 or 16
Normal = 0, // Boost mode disabled
FastLoad = 1, // CPU + GPU -> Config 13, 14, 15, or 16
Partial = 2, // GPU Only -> Config 15 or 16
};
enum class PerformanceMode : u8 {
Handheld = 0,
Docked = 1,
// This is nn::oe::PerformanceMode
enum class PerformanceMode : s32 {
Invalid = -1,
Normal = 0,
Boost = 1,
};
// Class to manage the state and change of the emulated system performance.

View File

@@ -173,7 +173,7 @@ private:
const auto uuid = rp.PopRaw<Common::UUID>();
LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play,
uuid.Format());
uuid.RawString());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -186,7 +186,7 @@ private:
[[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
const auto pid = rp.Pop<u64>();
LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset,
uuid.Format(), pid);
uuid.RawString(), pid);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -312,7 +312,7 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
IPC::RequestParser rp{ctx};
auto uuid = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.Format());
LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.RawString());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);

View File

@@ -320,7 +320,7 @@ Hid::Hid(Core::System& system_)
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
{310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"},
{400, nullptr, "IsUsbFullKeyControllerEnabled"},
{400, &Hid::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
{401, nullptr, "EnableUsbFullKeyController"},
{402, nullptr, "IsUsbFullKeyControllerConnected"},
{403, nullptr, "HasBattery"},
@@ -1673,6 +1673,16 @@ void Hid::ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
void Hid::IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(false);
}
void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};

View File

@@ -159,6 +159,7 @@ private:
void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx);
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx);

View File

@@ -3,7 +3,9 @@
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_resource_limit.h"
@@ -15,10 +17,21 @@ namespace Service::KernelHelpers {
ServiceContext::ServiceContext(Core::System& system_, std::string name_)
: kernel(system_.Kernel()) {
// Create a resource limit for the process.
const auto physical_memory_size =
kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::System);
auto* resource_limit = Kernel::CreateResourceLimitForProcess(system_, physical_memory_size);
// Create the process.
process = Kernel::KProcess::Create(kernel);
ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_),
Kernel::KProcess::ProcessType::Userland)
Kernel::KProcess::ProcessType::KernelInternal,
resource_limit)
.IsSuccess());
// Close reference to our resource limit, as the process opens one.
resource_limit->Close();
}
ServiceContext::~ServiceContext() {
@@ -43,7 +56,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
}
// Initialize the event.
event->Initialize(std::move(name));
event->Initialize(std::move(name), process);
// Commit the thread reservation.
event_reservation.Commit();

View File

@@ -118,16 +118,6 @@ u16 GenerateCrc16(const void* data, std::size_t size) {
return Common::swap16(static_cast<u16>(crc));
}
Common::UUID GenerateValidUUID() {
auto uuid{Common::UUID::Generate()};
// Bit 7 must be set, and bit 6 unset for the UUID to be valid
uuid.uuid[1] &= 0xFFFFFFFFFFFFFF3FULL;
uuid.uuid[1] |= 0x0000000000000080ULL;
return uuid;
}
template <typename T>
T GetRandomValue(T min, T max) {
std::random_device device;
@@ -383,7 +373,7 @@ MiiStoreData::MiiStoreData() = default;
MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields,
const Common::UUID& user_id) {
data.name = name;
data.uuid = GenerateValidUUID();
data.uuid = Common::UUID::MakeRandomRFC4122V4();
std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields));
data_crc = GenerateCrc16(data.data.data(), sizeof(data));

View File

@@ -202,7 +202,7 @@ struct MiiStoreData {
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
Name name{};
Common::UUID uuid{Common::INVALID_UUID};
Common::UUID uuid{};
} data;
u16 data_crc{};
@@ -326,7 +326,7 @@ public:
ResultCode GetIndex(const MiiInfo& info, u32& index);
private:
const Common::UUID user_id{Common::INVALID_UUID};
const Common::UUID user_id{};
u64 update_counter{};
};

View File

@@ -0,0 +1,45 @@
// Copyright 2022 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/mnpp/mnpp_app.h"
#include "core/hle/service/sm/sm.h"
namespace Service::MNPP {
class MNPP_APP final : public ServiceFramework<MNPP_APP> {
public:
explicit MNPP_APP(Core::System& system_) : ServiceFramework{system_, "mnpp:app"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &MNPP_APP::Unknown0, "unknown0"},
{1, &MNPP_APP::Unknown1, "unknown1"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void Unknown0(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MNPP, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Unknown1(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MNPP, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
};
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
std::make_shared<MNPP_APP>(system)->InstallAsService(service_manager);
}
} // namespace Service::MNPP

View File

@@ -0,0 +1,20 @@
// Copyright 2022 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Core {
class System;
}
namespace Service::SM {
class ServiceManager;
}
namespace Service::MNPP {
/// Registers all MNPP services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // namespace Service::MNPP

File diff suppressed because it is too large Load Diff

View File

@@ -7,15 +7,132 @@
#include <array>
#include <vector>
#include "common/common_funcs.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/service.h"
namespace Kernel {
class KEvent;
}
class KReadableEvent;
} // namespace Kernel
namespace Core::HID {
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFP {
enum class ServiceType : u32 {
User,
Debug,
System,
};
enum class State : u32 {
NonInitialized,
Initialized,
};
enum class DeviceState : u32 {
Initialized,
SearchingForTag,
TagFound,
TagRemoved,
TagMounted,
Unaviable,
Finalized,
};
enum class ModelType : u32 {
Amiibo,
};
enum class MountTarget : u32 {
Rom,
Ram,
All,
};
enum class AmiiboType : u8 {
Figure,
Card,
Yarn,
};
enum class AmiiboSeries : u8 {
SuperSmashBros,
SuperMario,
ChibiRobo,
YoshiWoollyWorld,
Splatoon,
AnimalCrossing,
EightBitMario,
Skylanders,
Unknown8,
TheLegendOfZelda,
ShovelKnight,
Unknown11,
Kiby,
Pokemon,
MarioSportsSuperstars,
MonsterHunter,
BoxBoy,
Pikmin,
FireEmblem,
Metroid,
Others,
MegaMan,
Diablo
};
using TagUuid = std::array<u8, 10>;
struct TagInfo {
TagUuid uuid;
u8 uuid_length;
INSERT_PADDING_BYTES(0x15);
s32 protocol;
u32 tag_type;
INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
struct CommonInfo {
u16 last_write_year;
u8 last_write_month;
u8 last_write_day;
u16 write_counter;
u16 version;
u32 application_area_size;
INSERT_PADDING_BYTES(0x34);
};
static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
struct ModelInfo {
u16 character_id;
u8 character_variant;
AmiiboType amiibo_type;
u16 model_number;
AmiiboSeries series;
u8 fixed; // Must be 02
INSERT_PADDING_BYTES(0x4); // Unknown
INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash
INSERT_PADDING_BYTES(0x14); // SHA256-HMAC
};
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
struct RegisterInfo {
Service::Mii::MiiInfo mii_char_info;
u16 first_write_year;
u8 first_write_month;
u8 first_write_day;
std::array<u8, 11> amiibo_name;
u8 unknown;
INSERT_PADDING_BYTES(0x98);
};
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
@@ -24,34 +141,131 @@ public:
const char* name);
~Interface() override;
struct ModelInfo {
std::array<u8, 0x8> amiibo_identification_block;
INSERT_PADDING_BYTES(0x38);
struct EncryptedAmiiboFile {
u16 crypto_init; // Must be A5 XX
u16 write_count; // Number of times the amiibo has been written?
INSERT_PADDING_BYTES(0x20); // System crypts
INSERT_PADDING_BYTES(0x20); // SHA256-(HMAC?) hash
ModelInfo model_info; // This struct is bigger than documentation
INSERT_PADDING_BYTES(0xC); // SHA256-HMAC
INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer
INSERT_PADDING_BYTES(0x54); // section 2 encrypted buffer
};
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
struct AmiiboFile {
std::array<u8, 10> uuid;
INSERT_PADDING_BYTES(0x4a);
ModelInfo model_info;
struct NTAG215Password {
u32 PWD; // Password to allow write access
u16 PACK; // Password acknowledge reply
u16 RFUI; // Reserved for future use
};
static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size");
static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size");
struct NTAG215File {
TagUuid uuid; // Unique serial number
u16 lock_bytes; // Set defined pages as read only
u32 compability_container; // Defines available memory
EncryptedAmiiboFile user_memory; // Writable data
u32 dynamic_lock; // Dynamic lock
u32 CFG0; // Defines memory protected by password
u32 CFG1; // Defines number of verification attempts
NTAG215Password password; // Password data
};
static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size");
void CreateUserInterface(Kernel::HLERequestContext& ctx);
bool LoadAmiibo(const std::vector<u8>& buffer);
Kernel::KReadableEvent& GetNFCEvent();
const AmiiboFile& GetAmiiboBuffer() const;
void CloseAmiibo();
void Initialize();
void Finalize();
ResultCode StartDetection(s32 protocol_);
ResultCode StopDetection();
ResultCode Mount();
ResultCode Unmount();
ResultCode GetTagInfo(TagInfo& tag_info) const;
ResultCode GetCommonInfo(CommonInfo& common_info) const;
ResultCode GetModelInfo(ModelInfo& model_info) const;
ResultCode GetRegisterInfo(RegisterInfo& register_info) const;
ResultCode OpenApplicationArea(u32 access_id);
ResultCode GetApplicationArea(std::vector<u8>& data) const;
ResultCode SetApplicationArea(const std::vector<u8>& data);
ResultCode CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
u64 GetHandle() const;
DeviceState GetCurrentState() const;
Core::HID::NpadIdType GetNpadId() const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
protected:
std::shared_ptr<Module> module;
private:
/// Validates that the amiibo file is not corrupted
bool IsAmiiboValid() const;
bool AmiiboApplicationDataExist(u32 access_id) const;
std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const;
void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const;
/// return password needed to allow write access to protected memory
u32 GetTagPassword(const TagUuid& uuid) const;
const Core::HID::NpadIdType npad_id;
DeviceState device_state{DeviceState::Unaviable};
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* nfc_tag_load;
AmiiboFile amiibo{};
Kernel::KEvent* activate_event;
Kernel::KEvent* deactivate_event;
NTAG215File tag_data{};
s32 protocol;
bool is_application_area_initialized{};
u32 application_area_id;
std::vector<u8> application_area_data;
};
};
class IUser final : public ServiceFramework<IUser> {
public:
explicit IUser(Module::Interface& nfp_interface_, Core::System& system_);
private:
void Initialize(Kernel::HLERequestContext& ctx);
void Finalize(Kernel::HLERequestContext& ctx);
void ListDevices(Kernel::HLERequestContext& ctx);
void StartDetection(Kernel::HLERequestContext& ctx);
void StopDetection(Kernel::HLERequestContext& ctx);
void Mount(Kernel::HLERequestContext& ctx);
void Unmount(Kernel::HLERequestContext& ctx);
void OpenApplicationArea(Kernel::HLERequestContext& ctx);
void GetApplicationArea(Kernel::HLERequestContext& ctx);
void SetApplicationArea(Kernel::HLERequestContext& ctx);
void CreateApplicationArea(Kernel::HLERequestContext& ctx);
void GetTagInfo(Kernel::HLERequestContext& ctx);
void GetRegisterInfo(Kernel::HLERequestContext& ctx);
void GetCommonInfo(Kernel::HLERequestContext& ctx);
void GetModelInfo(Kernel::HLERequestContext& ctx);
void AttachActivateEvent(Kernel::HLERequestContext& ctx);
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
void GetState(Kernel::HLERequestContext& ctx);
void GetDeviceState(Kernel::HLERequestContext& ctx);
void GetNpadId(Kernel::HLERequestContext& ctx);
void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
KernelHelpers::ServiceContext service_context;
// TODO(german77): We should have a vector of interfaces
Module::Interface& nfp_interface;
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
};
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // namespace Service::NFP

View File

@@ -59,7 +59,7 @@ void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequ
LOG_WARNING(Service_NS,
"(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}",
unknown, application_id, user_account_uid.Format());
unknown, application_id, user_account_uid.RawString());
IPC::ResponseBuilder rb{ctx, 12};
rb.Push(ResultSuccess);

View File

@@ -91,6 +91,8 @@ public:
{4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"},
{5, nullptr, "HookToCreateApplicationProgress"},
{6, nullptr, "ClearHook"},
{65000, &DebugMonitor::AtmosphereGetProcessInfo, "AtmosphereGetProcessInfo"},
{65001, nullptr, "AtmosphereGetCurrentLimitInfo"},
};
// clang-format on
@@ -125,6 +127,49 @@ private:
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
}
void AtmosphereGetProcessInfo(Kernel::HLERequestContext& ctx) {
// https://github.com/Atmosphere-NX/Atmosphere/blob/master/stratosphere/pm/source/impl/pm_process_manager.cpp#L614
// This implementation is incomplete; only a handle to the process is returned.
IPC::RequestParser rp{ctx};
const auto pid = rp.PopRaw<u64>();
LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid);
const auto process = SearchProcessList(kernel.GetProcessList(), [pid](const auto& proc) {
return proc->GetProcessID() == pid;
});
if (!process.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultProcessNotFound);
return;
}
struct ProgramLocation {
u64 program_id;
u8 storage_id;
};
static_assert(sizeof(ProgramLocation) == 0x10, "ProgramLocation has an invalid size");
struct OverrideStatus {
u64 keys_held;
u64 flags;
};
static_assert(sizeof(OverrideStatus) == 0x10, "OverrideStatus has an invalid size");
OverrideStatus override_status{};
ProgramLocation program_location{
.program_id = (*process)->GetProgramID(),
.storage_id = 0,
};
IPC::ResponseBuilder rb{ctx, 10, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(*process);
rb.PushRaw(program_location);
rb.PushRaw(override_status);
}
const Kernel::KernelCore& kernel;
};

View File

@@ -39,6 +39,7 @@
#include "core/hle/service/mig/mig.h"
#include "core/hle/service/mii/mii.h"
#include "core/hle/service/mm/mm_u.h"
#include "core/hle/service/mnpp/mnpp_app.h"
#include "core/hle/service/ncm/ncm.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfp/nfp.h"
@@ -265,6 +266,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
Migration::InstallInterfaces(*sm, system);
Mii::InstallInterfaces(*sm, system);
MM::InstallInterfaces(*sm, system);
MNPP::InstallInterfaces(*sm, system);
NCM::InstallInterfaces(*sm, system);
NFC::InstallInterfaces(*sm, system);
NFP::InstallInterfaces(*sm, system);

View File

@@ -36,7 +36,7 @@ struct SteadyClockTimePoint {
}
static SteadyClockTimePoint GetRandom() {
return {0, Common::UUID::Generate()};
return {0, Common::UUID::MakeRandom()};
}
};
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");

View File

@@ -49,7 +49,7 @@ public:
}
private:
Common::UUID clock_source_id{Common::UUID::Generate()};
Common::UUID clock_source_id{Common::UUID::MakeRandom()};
bool is_initialized{};
};

View File

@@ -45,7 +45,7 @@ struct TimeManager::Impl final {
time_zone_content_manager{system} {
const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {});
SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
Clock::SystemClockContext clock_context{};

View File

@@ -28,10 +28,10 @@ class Layer;
/// Represents a single display type
class Display {
public:
YUZU_NON_COPYABLE(Display);
YUZU_NON_MOVEABLE(Display);
public:
/// Constructs a display with a given unique ID and name.
///
/// @param id The unique ID for this display.

View File

@@ -11,6 +11,7 @@
#include <utility>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/vfs.h"
@@ -139,8 +140,11 @@ std::string GetResultStatusString(ResultStatus status);
std::ostream& operator<<(std::ostream& os, ResultStatus status);
/// Interface for loading an application
class AppLoader : NonCopyable {
class AppLoader {
public:
YUZU_NON_COPYABLE(AppLoader);
YUZU_NON_MOVEABLE(AppLoader);
struct LoadParameters {
s32 main_thread_priority;
u64 main_thread_stack_size;

View File

@@ -39,8 +39,7 @@ struct Memory::Impl {
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
ASSERT_MSG(target >= DramMemoryMap::Base && target < DramMemoryMap::End,
"Out of bounds target: {:016X}", target);
ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
if (Settings::IsFastmemEnabled()) {

View File

@@ -248,7 +248,7 @@ bool GCAdapter::Setup() {
std::size_t port = 0;
for (GCController& pad : pads) {
pad.identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = port++,
.pad = 0,
};

View File

@@ -9,17 +9,17 @@
namespace InputCommon {
constexpr PadIdentifier key_identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = 0,
.pad = 0,
};
constexpr PadIdentifier keyboard_key_identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = 1,
.pad = 0,
};
constexpr PadIdentifier keyboard_modifier_identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = 1,
.pad = 1,
};

View File

@@ -20,7 +20,7 @@ constexpr int motion_wheel_y = 4;
constexpr int touch_axis_x = 10;
constexpr int touch_axis_y = 11;
constexpr PadIdentifier identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = 0,
.pad = 0,
};

View File

@@ -175,23 +175,23 @@ public:
return false;
}
BatteryLevel GetBatteryLevel() {
Common::Input::BatteryLevel GetBatteryLevel() {
const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get());
switch (level) {
case SDL_JOYSTICK_POWER_EMPTY:
return BatteryLevel::Empty;
return Common::Input::BatteryLevel::Empty;
case SDL_JOYSTICK_POWER_LOW:
return BatteryLevel::Critical;
return Common::Input::BatteryLevel::Low;
case SDL_JOYSTICK_POWER_MEDIUM:
return BatteryLevel::Low;
return Common::Input::BatteryLevel::Medium;
case SDL_JOYSTICK_POWER_FULL:
return BatteryLevel::Medium;
case SDL_JOYSTICK_POWER_MAX:
return BatteryLevel::Full;
case SDL_JOYSTICK_POWER_UNKNOWN:
return Common::Input::BatteryLevel::Full;
case SDL_JOYSTICK_POWER_WIRED:
return Common::Input::BatteryLevel::Charging;
case SDL_JOYSTICK_POWER_UNKNOWN:
default:
return BatteryLevel::Charging;
return Common::Input::BatteryLevel::None;
}
}
@@ -352,6 +352,8 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
const PadIdentifier identifier = joystick->GetPadIdentifier();
SetButton(identifier, event.jbutton.button, true);
// Battery doesn't trigger an event so just update every button press
SetBattery(identifier, joystick->GetBatteryLevel());
}
break;
}
@@ -503,7 +505,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
Common::Input::VibrationError SDLDriver::SetRumble(
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
const auto joystick =
GetSDLJoystickByGUID(identifier.guid.Format(), static_cast<int>(identifier.port));
GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
const auto process_amplitude_exp = [](f32 amplitude, f32 factor) {
return (amplitude + std::pow(amplitude, factor)) * 0.5f * 0xFFFF;
};
@@ -600,7 +602,7 @@ Common::ParamPackage SDLDriver::BuildParamPackageForAnalog(PadIdentifier identif
Common::ParamPackage params;
params.Set("engine", GetEngineName());
params.Set("port", static_cast<int>(identifier.port));
params.Set("guid", identifier.guid.Format());
params.Set("guid", identifier.guid.RawString());
params.Set("axis_x", axis_x);
params.Set("axis_y", axis_y);
params.Set("offset_x", offset_x);
@@ -812,7 +814,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p
PreSetAxis(identifier, binding_left_x.value.axis);
PreSetAxis(identifier, binding_left_y.value.axis);
const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis);
const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis);
mapping.insert_or_assign(Settings::NativeAnalog::LStick,
BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
binding_left_y.value.axis,
@@ -823,7 +825,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p
PreSetAxis(identifier, binding_left_x.value.axis);
PreSetAxis(identifier, binding_left_y.value.axis);
const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis);
const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis);
mapping.insert_or_assign(Settings::NativeAnalog::LStick,
BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
binding_left_y.value.axis,
@@ -838,7 +840,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p
PreSetAxis(identifier, binding_right_x.value.axis);
PreSetAxis(identifier, binding_right_y.value.axis);
const auto right_offset_x = -GetAxis(identifier, binding_right_x.value.axis);
const auto right_offset_y = -GetAxis(identifier, binding_right_y.value.axis);
const auto right_offset_y = GetAxis(identifier, binding_right_y.value.axis);
mapping.insert_or_assign(Settings::NativeAnalog::RStick,
BuildParamPackageForAnalog(identifier, binding_right_x.value.axis,
binding_right_y.value.axis, right_offset_x,

View File

@@ -8,7 +8,7 @@
namespace InputCommon {
constexpr PadIdentifier identifier = {
.guid = Common::UUID{Common::INVALID_UUID},
.guid = Common::UUID{},
.port = 0,
.pad = 0,
};

View File

@@ -192,22 +192,22 @@ std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const {
return MAX_UDP_CLIENTS;
}
BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const {
Common::Input::BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const {
switch (battery) {
case Response::Battery::Dying:
return BatteryLevel::Empty;
return Common::Input::BatteryLevel::Empty;
case Response::Battery::Low:
return BatteryLevel::Critical;
return Common::Input::BatteryLevel::Critical;
case Response::Battery::Medium:
return BatteryLevel::Low;
return Common::Input::BatteryLevel::Low;
case Response::Battery::High:
return BatteryLevel::Medium;
return Common::Input::BatteryLevel::Medium;
case Response::Battery::Full:
case Response::Battery::Charged:
return BatteryLevel::Full;
return Common::Input::BatteryLevel::Full;
case Response::Battery::Charging:
default:
return BatteryLevel::Charging;
return Common::Input::BatteryLevel::Charging;
}
}
@@ -271,7 +271,7 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
const auto touch_axis_y_id =
static_cast<int>(id == 0 ? PadAxes::Touch1Y : PadAxes::Touch2Y);
const auto touch_button_id =
static_cast<int>(id == 0 ? PadButton::Touch1 : PadButton::touch2);
static_cast<int>(id == 0 ? PadButton::Touch1 : PadButton::Touch2);
// TODO: Use custom calibration per device
const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
@@ -319,6 +319,9 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
SetButton(identifier, button, button_status);
}
SetButton(identifier, static_cast<int>(PadButton::Home), data.home != 0);
SetButton(identifier, static_cast<int>(PadButton::TouchHardPress), data.touch_hard_press != 0);
SetBattery(identifier, GetBatteryLevel(data.info.battery));
}
@@ -350,7 +353,7 @@ PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const {
Common::UUID UDPClient::GetHostUUID(const std::string& host) const {
const auto ip = boost::asio::ip::make_address_v4(host);
const auto hex_host = fmt::format("{:06x}", ip.to_uint());
const auto hex_host = fmt::format("00000000-0000-0000-0000-0000{:06x}", ip.to_uint());
return Common::UUID{hex_host};
}
@@ -382,7 +385,7 @@ std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const {
Common::ParamPackage identifier{};
identifier.Set("engine", GetEngineName());
identifier.Set("display", fmt::format("UDP Controller {}", pad_identifier.pad));
identifier.Set("guid", pad_identifier.guid.Format());
identifier.Set("guid", pad_identifier.guid.RawString());
identifier.Set("port", static_cast<int>(pad_identifier.port));
identifier.Set("pad", static_cast<int>(pad_identifier.pad));
devices.emplace_back(identifier);
@@ -393,7 +396,7 @@ std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const {
ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) {
// This list excludes any button that can't be really mapped
static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 18>
static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 20>
switch_to_dsu_button = {
std::pair{Settings::NativeButton::A, PadButton::Circle},
{Settings::NativeButton::B, PadButton::Cross},
@@ -413,6 +416,8 @@ ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& p
{Settings::NativeButton::SR, PadButton::R2},
{Settings::NativeButton::LStick, PadButton::L3},
{Settings::NativeButton::RStick, PadButton::R3},
{Settings::NativeButton::Home, PadButton::Home},
{Settings::NativeButton::Screenshot, PadButton::TouchHardPress},
};
if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
return {};
@@ -517,6 +522,12 @@ Common::Input::ButtonNames UDPClient::GetUIButtonName(const Common::ParamPackage
return Common::Input::ButtonNames::Share;
case PadButton::Options:
return Common::Input::ButtonNames::Options;
case PadButton::Home:
return Common::Input::ButtonNames::Home;
case PadButton::Touch1:
case PadButton::Touch2:
case PadButton::TouchHardPress:
return Common::Input::ButtonNames::Touch;
default:
return Common::Input::ButtonNames::Undefined;
}

View File

@@ -84,7 +84,9 @@ private:
Cross = 0x4000,
Square = 0x8000,
Touch1 = 0x10000,
touch2 = 0x20000,
Touch2 = 0x20000,
Home = 0x40000,
TouchHardPress = 0x80000,
};
enum class PadAxes : u8 {
@@ -124,7 +126,7 @@ private:
struct ClientConnection {
ClientConnection();
~ClientConnection();
Common::UUID uuid{"7F000001"};
Common::UUID uuid{"00000000-0000-0000-0000-00007F000001"};
std::string host{"127.0.0.1"};
u16 port{26760};
s8 active{-1};
@@ -139,7 +141,7 @@ private:
std::size_t GetClientNumber(std::string_view host, u16 port) const;
// Translates UDP battery level to input engine battery level
BatteryLevel GetBatteryLevel(Response::Battery battery) const;
Common::Input::BatteryLevel GetBatteryLevel(Response::Battery battery) const;
void OnVersion(Response::Version);
void OnPortInfo(Response::PortInfo);

View File

@@ -167,12 +167,34 @@ public:
}
void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) {
modifier_status = button_callback.button_status.value;
const auto& new_status = button_callback.button_status;
const bool new_button_value = new_status.inverted ? !new_status.value : new_status.value;
modifier_status.toggle = new_status.toggle;
// Update button status with current
if (!modifier_status.toggle) {
modifier_status.locked = false;
if (modifier_status.value != new_button_value) {
modifier_status.value = new_button_value;
}
} else {
// Toggle button and lock status
if (new_button_value && !modifier_status.locked) {
modifier_status.locked = true;
modifier_status.value = !modifier_status.value;
}
// Unlock button ready for next press
if (!new_button_value && modifier_status.locked) {
modifier_status.locked = false;
}
}
UpdateStatus();
}
void UpdateStatus() {
const float coef = modifier_status ? modifier_scale : 1.0f;
const float coef = modifier_status.value ? modifier_scale : 1.0f;
bool r = right_status;
bool l = left_status;
@@ -266,7 +288,7 @@ public:
if (down_status) {
--y;
}
const float coef = modifier_status ? modifier_scale : 1.0f;
const float coef = modifier_status.value ? modifier_scale : 1.0f;
status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
return status;
@@ -287,9 +309,9 @@ private:
bool down_status{};
bool left_status{};
bool right_status{};
bool modifier_status{};
float last_x_axis_value{};
float last_y_axis_value{};
Common::Input::ButtonStatus modifier_status{};
const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
std::chrono::time_point<std::chrono::steady_clock> last_update;
};

View File

@@ -4,7 +4,6 @@
#include <algorithm>
#include "common/settings.h"
#include "core/frontend/framebuffer_layout.h"
#include "input_common/helpers/touch_from_buttons.h"
namespace InputCommon {

View File

@@ -70,7 +70,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value)
TriggerOnAxisChange(identifier, axis, value);
}
void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) {
void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
{
std::lock_guard lock{mutex};
ControllerData& controller = controller_list.at(identifier);
@@ -96,7 +96,7 @@ bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
std::lock_guard lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
identifier.pad, identifier.port);
return false;
}
@@ -113,7 +113,7 @@ bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 d
std::lock_guard lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
identifier.pad, identifier.port);
return false;
}
@@ -130,7 +130,7 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
std::lock_guard lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
identifier.pad, identifier.port);
return 0.0f;
}
@@ -143,13 +143,13 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
return axis_iter->second;
}
BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
std::lock_guard lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
identifier.pad, identifier.port);
return BatteryLevel::Charging;
return Common::Input::BatteryLevel::Charging;
}
const ControllerData& controller = controller_iter->second;
return controller.battery;
@@ -159,7 +159,7 @@ BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion)
std::lock_guard lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
identifier.pad, identifier.port);
return {};
}
@@ -270,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis,
}
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
[[maybe_unused]] BatteryLevel value) {
[[maybe_unused]] Common::Input::BatteryLevel value) {
std::lock_guard lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;

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