Compare commits

..

150 Commits

Author SHA1 Message Date
Markus Wick
dce624e3f1 core: Use a raw pointer in GetGPUDebugContext.
This helper is called very often. The memory ownership shall not be transfered, so just return the raw pointer.
2018-09-04 14:10:05 +02:00
Markus Wick
2081ed7db2 command_processor: Use std::array for bound_engines.
subchannel is a 3 bit field. So there must not be more than 8 bound engines.
And using a hashmap for up to 8 values is a bit overpowered.
2018-09-04 14:10:05 +02:00
bunnei
1c5636e690 Merge pull request #1231 from lioncash/global
service: Migrate global named port map to the KernelCore class
2018-09-03 21:21:12 -04:00
bunnei
2afe8ac4a7 Merge pull request #1229 from lioncash/forward-decl
vfs_real: Forward declare IOFile
2018-09-03 21:20:34 -04:00
Mat M
9cfe2414cb Merge pull request #1233 from lioncash/dynarmic
externals: Update dynarmic to 0435ac2
2018-09-03 16:22:13 -04:00
Lioncash
c6fd56b00f externals: Update dynarmic to 0435ac2 2018-09-03 08:04:24 -04:00
Lioncash
a405373144 vfs_real: Forward declare IOFile
Eliminates the need to rebuild some source files if the file_util header
ever changes. This also uncovered some indirect inclusions, which have
also been fixed.
2018-09-02 12:38:14 -04:00
Lioncash
1242c1ec0a service: Migrate global named port map to the KernelCore class
Now that we have a class representing the kernel in some capacity, we
now have a place to put the named port map, so we move it over and get
rid of another piece of global state within the core.
2018-09-02 12:35:30 -04:00
bunnei
325f3e0693 Merge pull request #1213 from DarkLordZach/octopath-fs
filesystem/maxwell_3d: Various changes to boot Project Octopath Traveller
2018-09-02 10:49:18 -04:00
bunnei
89be49d2f3 Merge pull request #1215 from ogniK5377/texs-nodep-assert
Added assert for TEXS nodep
2018-09-02 10:48:27 -04:00
bunnei
2714d9e64c Merge pull request #1219 from jroweboy/less-artifacts
Build - Upload fewer artifacts
2018-09-02 10:48:03 -04:00
bunnei
d2ade27c3f Merge pull request #1220 from FearlessTobi/extensions-qol
yuzu: Display the unsupported GL extensions in the popup
2018-09-02 10:47:25 -04:00
bunnei
177c45e97d Merge pull request #1214 from ogniK5377/ipa-assert
Added better asserts to IPA, Renamed IPA modes to match mesa
2018-09-02 10:44:43 -04:00
bunnei
9c206fe94d Merge pull request #1216 from ogniK5377/ffma-assert
Added FFMA asserts and missing fields
2018-09-02 10:44:13 -04:00
bunnei
1ccc0457d5 Merge pull request #1218 from ogniK5377/fmul-assert
Added FMUL asserts
2018-09-02 10:43:48 -04:00
bunnei
7a439630bb Merge pull request #1228 from lioncash/construct
filesystem: Move dir retrieval after path checking in DeleteFile()
2018-09-02 10:43:09 -04:00
Lioncash
fda8f1da20 filesystem: Move dir retrieval after path checking in DeleteFile()
We don't need to do the lookup if the path is considered empty
currently.
2018-09-02 09:20:17 -04:00
fearlessTobi
0f453488e2 citra_qt: Display the unsupported GL extensions in the popup 2018-09-01 19:01:53 +02:00
James Rowe
a0e1fbfe14 Build - Upload fewer artifacts
Appveyor has a limit on artifact retention, and we hit the limit all the
time, so just lower the number of build artifacts to just the final zip
2018-09-01 10:42:16 -06:00
David Marcec
60754b4728 Removed saturate assert
Unneeded as we already implement it
2018-09-01 19:33:32 +10:00
David Marcec
2edab4e840 Removed saturate assert
Saturate already implemented
2018-09-01 19:29:20 +10:00
David Marcec
2bc6abb9a1 Changed tab5980_0 default from 0 -> 1 2018-09-01 19:15:03 +10:00
David Marcec
6f8ed9508d Added FMUL asserts 2018-09-01 19:05:10 +10:00
David Marcec
b89fc407d7 Added FFMA asserts 2018-09-01 18:45:14 +10:00
David Marcec
948bc87a59 Added assert for TEXS nodep 2018-09-01 17:00:01 +10:00
David Marcec
ad3dca7e62 Added better asserts to IPA, Renamed IPA modes to match mesa
IpaMode is changed to IpaInterpMode
IpaMode is suppose to be 2 bits not 3
Added IpaSampleMode
Added Saturate

Renamed modes based on
d27c791891/src/gallium/drivers/nouveau/codegen/nv50_ir_emit_gm107.cpp (L2530)
2018-09-01 16:34:27 +10:00
Zach Hilman
f32e28c7b8 maxwell_3d: Use CoreTiming for query timestamp 2018-08-31 23:25:18 -04:00
Zach Hilman
19d0951ae6 filesystem: Implement OpenReadOnlySaveDataFilesystem 2018-08-31 23:19:49 -04:00
Zach Hilman
7939ea18e8 filesystem: Add OpenFileSystemWithPatch 2018-08-31 23:19:23 -04:00
bunnei
c69dc5acf9 Merge pull request #1196 from FearlessTobi/ccache-consistency
.travis: Use Citras ccache for builds instead of yuzus
2018-08-31 21:50:44 -04:00
bunnei
1c05c06e04 Merge pull request #1212 from lioncash/forward-decl
core/core: Replace includes with forward declarations where applicable
2018-08-31 21:50:12 -04:00
Lioncash
4a587b81b2 core/core: Replace includes with forward declarations where applicable
The follow-up to e2457418da, which
replaces most of the includes in the core header with forward declarations.

This makes it so that if any of the headers the core header was
previously including change, then no one will need to rebuild the bulk
of the core, due to core.h being quite a prevalent inclusion.

This should make turnaround for changes much faster for developers.
2018-08-31 16:30:14 -04:00
fearlessTobi
dc3cc0002c travis: use Citras ccache 2018-08-31 20:13:26 +02:00
bunnei
42588493d5 Merge pull request #1205 from bunnei/improve-rasterizer-cache-2
Various fixes and improvements to rasterizer cache 2: Electric Boogaloo
2018-08-31 13:24:21 -04:00
bunnei
7f7eb29323 gl_rasterizer_cache: Use accurate framebuffer setting for accurate copies. 2018-08-31 13:07:28 -04:00
bunnei
123c065086 gl_rasterizer_cache: Also use reserve cache for RecreateSurface. 2018-08-31 13:07:28 -04:00
bunnei
9bc71fcc5f rasterizer_cache: Use boost::interval_map for a more accurate cache. 2018-08-31 13:07:28 -04:00
bunnei
d647d9550c gl_renderer: Cache textures, framebuffers, and shaders based on CPU address. 2018-08-31 13:07:27 -04:00
bunnei
16d65182f9 gl_rasterizer: Fix issues with the rasterizer cache.
- Use a single cached page map.
- Fix calculation of ending page.
2018-08-31 13:07:27 -04:00
greggameplayer
06578e89b2 Implement BC6H_UF16 & BC6H_SF16 (#1092)
* Implement BC6H_UF16 & BC6H_SF16
Require by ARMS

* correct coding style

* correct coding style part 2
2018-08-31 12:11:19 -04:00
bunnei
f08d24e9c0 Merge pull request #1204 from lioncash/pimpl
core: Make the main System class use the PImpl idiom
2018-08-31 11:31:20 -04:00
bunnei
6683bf50b5 Merge pull request #1207 from degasus/hotfix
Report correct shader size.
2018-08-31 11:21:15 -04:00
bunnei
e205e74e1f Merge pull request #1208 from Hexagon12/pred-comp-14
Add predicate comparison 14 (GreaterEqualWithNan)
2018-08-31 11:20:47 -04:00
Lioncash
e2457418da core: Make the main System class use the PImpl idiom
core.h is kind of a massive header in terms what it includes within
itself. It includes VFS utilities, kernel headers, file_sys header,
ARM-related headers, etc. This means that changing anything in the
headers included by core.h essentially requires you to rebuild almost
all of core.

Instead, we can modify the System class to use the PImpl idiom, which
allows us to move all of those headers to the cpp file and forward
declare the bulk of the types that would otherwise be included, reducing
compile times. This change specifically only performs the PImpl portion.
2018-08-31 07:16:57 -04:00
Markus Wick
5be8b7a362 Report correct shader size.
Seems like this was an oversee in regards to 1fd979f50a
It changed GLShader::ProgramCode to a std::vector, so sizeof is wrong.
2018-08-31 09:56:37 +02:00
Hexagon12
d626bc8c62 Added predicate comparison GreaterEqualWithNan 2018-08-31 10:40:18 +03:00
bunnei
26aaa86ece Merge pull request #1195 from FearlessTobi/port-gamelist-compat
yuzu: Show game compatibility in the game list (PR ported from Citra)
2018-08-30 21:34:43 -04:00
Laku
915ab81ec2 gl_shader_decompiler: Implement POPC (#1203)
* Implement POPC

* implement invert
2018-08-30 21:32:58 -04:00
bunnei
d6accf96ff Merge pull request #1200 from bunnei/improve-ipa
gl_shader_decompiler: Improve IPA for Pass mode with Position attribute.
2018-08-30 10:31:26 -04:00
bunnei
5094dfa081 Merge pull request #1198 from lioncash/kernel
kernel: Eliminate kernel global state
2018-08-30 10:02:50 -04:00
bunnei
42ef40884f Merge pull request #1202 from FearlessTobi/port-3825
Port #3825 from Citra: "travis: share environment variables with Docker"
2018-08-30 09:54:32 -04:00
bunnei
6e73039eb5 Merge pull request #1172 from tech4me/impl_iadd3
Shaders: Implemented IADD3
2018-08-30 09:52:27 -04:00
tech4me
a6dd577d02 Shaders: Implemented IADD3 2018-08-29 13:44:41 -04:00
fearlessTobi
78653f7339 Show game compatibility within yuzu 2018-08-29 15:42:53 +02:00
fearlessTobi
02dfbf961e Remove Citra specific variable 2018-08-29 15:29:37 +02:00
liushuyu
a2c97de929 travis: share env variables with Docker 2018-08-29 15:28:13 +02:00
bunnei
b1ccd88434 gl_shader_decompiler: Improve IPA for Pass mode with Position attribute. 2018-08-29 00:37:29 -04:00
Lioncash
0cbcd6ec9a kernel: Eliminate kernel global state
As means to pave the way for getting rid of global state within core,
This eliminates kernel global state by removing all globals. Instead
this introduces a KernelCore class which acts as a kernel instance. This
instance lives in the System class, which keeps its lifetime contained
to the lifetime of the System class.

This also forces the kernel types to actually interact with the main
kernel instance itself instead of having transient kernel state placed
all over several translation units, keeping everything together. It also
has a nice consequence of making dependencies much more explicit.

This also makes our initialization a tad bit more correct. Previously we
were creating a kernel process before the actual kernel was initialized,
which doesn't really make much sense.

The KernelCore class itself follows the PImpl idiom, which allows
keeping all the implementation details sealed away from everything else,
which forces the use of the exposed API and allows us to avoid any
unnecessary inclusions within the main kernel header.
2018-08-28 22:31:51 -04:00
bunnei
4d7e1662c8 Merge pull request #1193 from lioncash/priv
gpu: Make memory_manager private
2018-08-28 12:28:57 -04:00
bunnei
eb4f2d5596 Merge pull request #1192 from lioncash/unused
gl_rasterizer: Remove unused variables
2018-08-28 12:28:13 -04:00
bunnei
d8ba202070 Merge pull request #1191 from lioncash/noexcept
hle/result: Make ResultVal's move constructor as noexcept
2018-08-28 12:27:48 -04:00
bunnei
72e4499a9e Merge pull request #1194 from lioncash/alloc
gl_shader_cache: Remove unused program_code vector in GetShaderAddress()
2018-08-28 11:27:44 -04:00
Lioncash
2e7dc4cac9 gl_shader_cache: Remove unused program_code vector in GetShaderAddress()
Given std::vector is a type with a non-trivial destructor, this
variable cannot be optimized away by the compiler, even if unused.
Because of that, something that was intended to be fairly lightweight,
was actually allocating 32KB and deallocating it at the end of the
function.
2018-08-28 11:20:41 -04:00
Lioncash
45fb74d262 gpu: Make memory_manager private
Makes the class interface consistent and provides accessors for
obtaining a reference to the memory manager instance.

Given we also return references, this makes our more flimsy uses of
const apparent, given const doesn't propagate through pointers in the
way one would typically expect. This makes our mutable state more
apparent in some places.
2018-08-28 11:11:50 -04:00
Lioncash
6771a18c6c gl_rasterizer: Remove unused variables 2018-08-28 10:46:29 -04:00
bunnei
0d2435343a Merge pull request #1190 from FearlessTobi/im-so-retarded
yuzu: Fix two stupid errors made in #1141
2018-08-28 09:57:05 -04:00
Lioncash
f1bc62bb4c hle/result: Make ResultVal's move constructor as noexcept
Many containers within the standard library provide different behaviors
based on whether or not a move constructor/assignment operator can be
guaranteed not to throw or not.

Notably, implementations will generally use std::move_if_noexcept (or an
internal implementation of it) to provide strong exception guarantees.
If a move constructor potentially throws (in other words, is not
noexcept), then certain behaviors will create copies, rather than moving
the values.

For example, consider std::vector. When a std::vector calls resize(),
there are two ways the elements can be relocated to the new block of
memory (if a reallocation happens), by copy, or by moving the existing
elements into the new block of memory. If a type does not have a
guarantee that it will not throw in the move constructor, a copy will
happen. However, if it can be guaranteed that the move constructor won't
throw, then the elements will be moved.

This just allows ResultVal to be moved instead of copied all the time if
ever used in conjunction with containers for whatever reason.
2018-08-28 09:39:50 -04:00
fearlessTobi
4a56931703 Fix two stupid errors made in #1141 2018-08-28 15:16:03 +02:00
bunnei
ffe2336136 Merge pull request #1165 from bunnei/shader-cache
renderer_opengl: Implement a new shader cache.
2018-08-27 20:35:58 -04:00
bunnei
b4ac8218d0 Merge pull request #1189 from FearlessTobi/fix-stick-directions
yuzu: Fix input UI direction order for the right stick
2018-08-27 19:16:23 -04:00
fearlessTobi
9a6bfc55f3 yuzu: Fix stick UI direction order 2018-08-28 00:59:21 +02:00
bunnei
a409d49bbd Merge pull request #1177 from lioncash/err
kernel/error: Amend several error codes
2018-08-27 18:37:48 -04:00
bunnei
b55d8111e6 renderer_opengl: Implement a new shader cache. 2018-08-27 18:26:46 -04:00
bunnei
a0e1566dc5 gl_rasterizer_cache: Update to use RasterizerCache base class. 2018-08-27 18:26:46 -04:00
bunnei
382852418b video_core: Add RasterizerCache class for common cache management code. 2018-08-27 18:26:45 -04:00
bunnei
2f5ed3877c Merge pull request #1169 from Lakumakkara/sel
shader_bytecode: fix SEL_IMM bitstring
2018-08-27 18:24:57 -04:00
bunnei
90fd03015a Merge pull request #1188 from lioncash/unused
vfs_real: Remove unused variable in CreateDirectoryRelative()
2018-08-27 18:24:23 -04:00
bunnei
2562fe4a16 Merge pull request #1170 from lioncash/ret
file_util: Correct return value in early exit of ReadFileToString()
2018-08-27 18:18:31 -04:00
bunnei
62edc01525 Merge pull request #1175 from lioncash/ns
core: Namespace all code in the arm subdirectory under the Core namespace
2018-08-27 18:17:12 -04:00
bunnei
5d2043598e Merge pull request #1187 from lioncash/shadow
registered_cache: Get rid of variable shadowing in ProcessFiles()
2018-08-27 18:15:49 -04:00
Lioncash
c6024379a4 vfs_real: Remove unused variable in CreateDirectoryRelative() 2018-08-27 15:58:23 -04:00
Lioncash
d3934d7da7 registered_cache: Get rid of variable shadowing in ProcessFiles()
Prevents compiler warnings.
2018-08-27 15:55:56 -04:00
bunnei
887a9c5c29 Merge pull request #1128 from DarkLordZach/malformed-hex-crash
hex_util: Replace logic_errors with LOG_CRITICAL
2018-08-27 15:45:22 -04:00
bunnei
af59d4bff0 Merge pull request #1176 from lioncash/info
svc: Return process title ID if queried in GetInfo()
2018-08-27 15:44:52 -04:00
bunnei
f96ded9815 Merge pull request #1174 from lioncash/debug
debug_utils: Minor individual interface changes
2018-08-27 15:44:29 -04:00
bunnei
8c66a5a9a5 Merge pull request #1162 from ogniK5377/ttf-plu
PL:U Added SharedFonts loading via TTF
2018-08-27 15:43:10 -04:00
bunnei
34a447d24e Merge pull request #1168 from lioncash/header
hid: Move core include to cpp file
2018-08-27 15:42:52 -04:00
bunnei
8d86747514 Merge pull request #1171 from lioncash/true
core: Remove always true conditionals in Load()
2018-08-27 15:41:58 -04:00
bunnei
43a2598e26 Merge pull request #1180 from tech4me/languagecode_fix
set: Fixed GetAvailableLanguageCodes() to follow the max_entries
2018-08-27 15:41:38 -04:00
tech4me
d26a46feed set: Fixed GetAvailableLanguageCodes() to follow the max_entries
Rightnow, in games use GetAvailableLanguageCodes(), there is a WriteBuffer() with size larger than the buffer_size. (Core Critical core\hle\kernel\hle_ipc.cpp:WriteBuffer:296: size (0000000000000088) is greater than buffer_size (0000000000000078))

0x88 = 17(languages) * 8
0x78 = 15(languages) * 8

GetAvailableLanguageCodes() can only support 15 languages.
After firmware 4.0.0 there are 17 supported language instead of 15, to enable this GetAvailableLanguageCodes2() need to be used.
So GetAvailableLanguageCodes() will be caped at 15 languages.
Reference:
http://switchbrew.org/index.php/Settings_services
2018-08-26 00:11:13 -07:00
bunnei
be2f1eabd7 Merge pull request #1173 from lioncash/batch
maxwell3d: Move FinishedPrimitiveBatch event after AcceleratedDrawBatch()
2018-08-25 10:59:54 -04:00
bunnei
23b86fd3ea Merge pull request #1167 from lioncash/assert
gl_rasterizer: Correct assertion condition in SyncLogicOpState()
2018-08-25 10:50:59 -04:00
Lioncash
f708207ae6 kernel/error: Amend error code for ERR_MAX_CONNECTIONS_REACHED
We can make this error code an alias of the resource limit exceeded
error code, allowing us to get rid of the lingering 3DS error code of
the same type.
2018-08-25 09:40:42 -04:00
Lioncash
bfb0c87b7b kernel/error: Amend error code for ERR_PORT_NAME_TOO_LONG
We can treat this as an alias of TooLarge for documentation purposes.
This also lets us get rid of another lingering 3DS-related error code.
2018-08-25 09:40:29 -04:00
Lioncash
81ca46dd17 kernel/error: Add error code for the handle table being full
This replaces the lingering 3DS constant with the proper one, and
utilizes it within HandleTable's Create() member function.
2018-08-25 09:40:21 -04:00
Lioncash
b8be5524bc kernel/error: Add error code for invalid memory permissions 2018-08-25 09:40:12 -04:00
Lioncash
2fd45093f2 kernel/error: Correct kernel error code for invalid combination 2018-08-25 09:40:00 -04:00
Sebastian Valle
f170159fde Merge pull request #1166 from lioncash/typo
filesystem: Fix typo in log message
2018-08-25 07:19:46 -05:00
Lioncash
e81354ae38 svc: Return process title ID if queried in GetInfo()
We already have the variable itself set up to perform this task, so we
can just return its value from the currently executing process instead
of always stubbing it to zero.
2018-08-25 05:02:28 -04:00
Mat M
6426b0f551 Merge pull request #1094 from DarkLordZach/nax0
file_sys: Add support for NAX archives
2018-08-24 23:47:46 -04:00
Zach Hilman
6314a799aa file_sys/crypto: Fix missing/unnecessary includes 2018-08-24 22:15:32 -04:00
Lioncash
43e0d865fa core: Namespace all code in the arm subdirectory under the Core namespace
Gets all of these types and interfaces out of the global namespace.
2018-08-24 21:50:39 -04:00
Lioncash
c65713832c debug_utils: Remove unused includes
Quite a bit of these aren't necessary directly within the debug_utils
header and can be removed or included where actually necessary.
2018-08-24 20:49:14 -04:00
Lioncash
1e6a209649 debug_utils: Make BreakpointObserver class' constructor explicit
Avoids implicit conversions.
2018-08-24 20:49:14 -04:00
Lioncash
b6425c0511 debug_utils: Initialize active_breakpoint member of DebugContext
Ensures that all class members are initialized.
2018-08-24 20:15:50 -04:00
Lioncash
20800f2df7 maxwell3d: Move FinishedPrimitiveBatch event after AcceleratedDrawBatch()
The start and finish events should likely not be right after one another
like this, otherwise the batch will appear to complete immediately
2018-08-24 19:58:05 -04:00
Zach Hilman
f09da5d1c9 Merge pull request #1065 from DarkLordZach/window-title
qt: Add filename and title id to window title while running
2018-08-24 14:34:03 -04:00
Lioncash
8492ec1669 core: Remove always true conditionals in Load()
These conditions are always true, since the outer conditional already
checks for these conditions.
2018-08-24 02:48:30 -04:00
Lioncash
c74b7ee204 file_util: Correct return value in early exit of ReadFileToString()
While still essentially being zero, we should be returning a numeric
value here, not a boolean typed value.
2018-08-24 02:20:02 -04:00
Laku
36093a3e4d fix SEL_IMM bitstring 2018-08-24 07:18:12 +03:00
Lioncash
ec59e4a6c5 hid: Move core include to cpp file
This isn't required to be in the header. Instead, directly include what
this header needs and move it to the cpp file where it belongs.
2018-08-23 23:20:35 -04:00
Lioncash
8fd9eb71b4 gl_rasterizer: Correct assertion condition in SyncLogicOpState()
Previously the assert would always be hit, since it was the equivalent
of: array == nullptr, which is never true.
2018-08-23 23:00:54 -04:00
bunnei
018c25e123 Merge pull request #1164 from tech4me/decode_iadd3
Shaders: Added decodings for IADD3 instructions
2018-08-23 22:59:34 -04:00
Lioncash
f6f5c2e4d8 filesystem: Fix typo in log message 2018-08-23 22:12:31 -04:00
Tobias
165c23c848 Port #4013 from Citra: "Init logging sooner so we dont miss some logs on startup" (#1142)
* Port #4013 from Citra: "Init logging sooner so we dont miss some logs on startup"

* Fix compilation
2018-08-23 19:52:06 -04:00
Zach Hilman
d1a6dd61d1 xci: Ignore NCA files with updates in secure 2018-08-23 18:53:37 -04:00
Zach Hilman
4f18c17df7 content_archive: Add update title detection
This is needed because the title IDs of update NCAs will not use the update title ID. The only sure way to tell is to look for a partition with BKTR crypto.
2018-08-23 18:53:13 -04:00
David
5049ca5d8c Added GetBootMode (#1107)
* Added GetBootMode

Used by homebrew

* Added enum for GetBootMode
2018-08-23 18:31:45 -04:00
tech4me
ba2972bc64 Shaders: Added decodings for IADD3 instructions 2018-08-23 15:46:59 -04:00
Zach Hilman
06487c2c8d hex_util: Replace logic_errors with LOG_CRITICAL
Makes it so malformed hex strings do not crash the entire program.
2018-08-23 14:44:51 -04:00
Zach Hilman
67fa51ea2f qt: Add filename and title id to window title while running 2018-08-23 14:12:56 -04:00
David Marcec
78b109d195 Addressed plu TTF changes 2018-08-24 02:18:04 +10:00
bunnei
0dce6d7008 Merge pull request #1160 from bunnei/surface-reserve
gl_rasterizer_cache: Several improvements
2018-08-23 12:04:37 -04:00
bunnei
3ed0115e36 Merge pull request #1153 from bunnei/stencil-clear
gl_rasterizer: Implement partial color clear, stencil clear, and stencil test.
2018-08-23 12:04:08 -04:00
Zach Hilman
ccfd176382 key_manager: Eliminate indexed for loop 2018-08-23 11:53:30 -04:00
Zach Hilman
119ab308b5 key_manager: Create keys dir if it dosen't exist
On call to WriteKeyToFile, so that the autogenerated file can be written.
2018-08-23 11:53:30 -04:00
Zach Hilman
a7e8d10969 file_sys: Cut down on includes and copies 2018-08-23 11:53:30 -04:00
Zach Hilman
42dc856ce1 crypto: Eliminate magic constants 2018-08-23 11:53:30 -04:00
Zach Hilman
61a5b56abd key_manager: Add support for autogenerated keys
Stored in a separate file than manual keys.
2018-08-23 11:53:30 -04:00
Zach Hilman
f26fc64cb4 key_manager: Add support for KEK and SD seed derivation 2018-08-23 11:53:30 -04:00
Zach Hilman
cde665c565 key_manager: Switch to boost flat_map for keys
Should make key gets marginally faster.
2018-08-23 11:53:30 -04:00
Zach Hilman
60b7a3b904 game_list: Add SD registration loading to game list 2018-08-23 11:53:30 -04:00
Zach Hilman
ab44192ab0 file_sys: Implement NAX containers 2018-08-23 11:53:30 -04:00
Zach Hilman
8b52d6682a registration: Add GetEntryUnparsed methods
Returns the file before calling parser on it.
2018-08-23 11:53:30 -04:00
Zach Hilman
13524578b6 sdmc_factory: Add SDMC RegisteredCache getter 2018-08-23 11:53:30 -04:00
Zach Hilman
4112dd6b4e qt: Make default row data title name and title id
Helps with installed games by making the title not a hexadecimal id string, instead the name.
2018-08-23 11:53:30 -04:00
Zach Hilman
bf33f80fae vfs: Add GetOrCreateDirectoryRelative method 2018-08-23 11:52:44 -04:00
Zach Hilman
ef3768f323 filesystem: Add CreateFactories methods to fs
Allows frontend to create registration caches for use before a game has booted.
2018-08-23 11:52:44 -04:00
Zach Hilman
410062031b filesystem: Add logging to registration getters 2018-08-23 11:52:44 -04:00
Zach Hilman
b247e0cab0 loader: Add new NAX-specific errors and messages 2018-08-23 11:52:44 -04:00
Zach Hilman
2164702cf7 nax: Add AppLoader_NAX and update loader to support it 2018-08-23 11:52:44 -04:00
Zach Hilman
c4845df3d4 xts_encryption_layer: Implement XTSEncryptionLayer 2018-08-23 11:52:44 -04:00
Zach Hilman
10e5356e9a aes_util: Make XTSTranscode stricter about sizes
XTS with Nintendo Tweak will fail mysteriously if the sector size is not 0x4000. Upgrade the critical log to an assert to prevent undefined behavior.
2018-08-23 11:52:44 -04:00
Zach Hilman
6dd369ab88 ctr_encryption_layer: Fix bug when transcoding small data
Fixes a bug where data lengths of less than size 0x10 will fail or have misleading return values.
2018-08-23 11:52:44 -04:00
Zach Hilman
a9dc5a3c10 xci: Fix error masking issue
Prevents NCA-related errors from being masked into MissingProgramNCA or MissingKeyFile
2018-08-23 11:52:44 -04:00
bunnei
d65f079cc1 gl_rasterizer_cache: Blit when possible on RecreateSurface. 2018-08-23 11:27:01 -04:00
bunnei
fee8bdd90c gl_rasterizer_cache: Reserve surfaces that have already been created for later use. 2018-08-23 11:27:01 -04:00
bunnei
fde2017a3f gl_rasterizer_cache: Remove assert for RecreateSurface type. 2018-08-23 11:27:00 -04:00
bunnei
ebf5768340 gl_rasterizer_cache: Implement compressed texture copies. 2018-08-23 11:27:00 -04:00
David Marcec
eccc77a8c8 Added SharedFonts loading via TTF
By having the following TTF files in your yuzu sysdata directory. You can load sharedfonts via TTF files.
FontStandard.ttf
FontChineseSimplified.ttf
FontExtendedChineseSimplified.ttf
FontChineseTraditional.ttf
FontKorean.ttf
FontNintendoExtended.ttf
FontNintendoExtended2.ttf
2018-08-23 14:42:06 +10:00
164 changed files with 3503 additions and 1704 deletions

View File

@@ -20,6 +20,7 @@ matrix:
install: "./.travis/linux/deps.sh"
script: "./.travis/linux/build.sh"
after_success: "./.travis/linux/upload.sh"
cache: ccache
- os: osx
env: NAME="macos build"
sudo: false
@@ -27,6 +28,7 @@ matrix:
install: "./.travis/macos/deps.sh"
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"
cache: ccache
deploy:
provider: releases
@@ -42,7 +44,3 @@ notifications:
webhooks:
urls:
- https://api.yuzu-emu.org/code/travis/notify
cache:
directories:
- $HOME/.ccache

View File

@@ -0,0 +1,12 @@
# List of environment variables to be shared with Docker containers
CI
TRAVIS
CONTINUOUS_INTEGRATION
TRAVIS_BRANCH
TRAVIS_BUILD_ID
TRAVIS_BUILD_NUMBER
TRAVIS_COMMIT
TRAVIS_JOB_ID
TRAVIS_JOB_NUMBER
TRAVIS_REPO_SLUG
TRAVIS_TAG

View File

@@ -1,3 +1,4 @@
#!/bin/bash -ex
docker run -e CCACHE_DIR=/ccache -v $HOME/.ccache:/ccache -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
mkdir -p "$HOME/.ccache"
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh

View File

@@ -5,14 +5,8 @@ apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev
cd /yuzu
export PATH=/usr/lib/ccache:$PATH
ln -sf /usr/bin/ccache /usr/lib/ccache/cc
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
mkdir build && cd build
ccache --show-stats > ccache_before
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
ninja
ccache --show-stats > ccache_after
diff -U100 ccache_before ccache_after || true
ctest -VV -C Release

View File

@@ -5,14 +5,11 @@ set -o pipefail
export MACOSX_DEPLOYMENT_TARGET=10.12
export Qt5_DIR=$(brew --prefix)/opt/qt5
export UNICORNDIR=$(pwd)/externals/unicorn
export PATH="/usr/local/opt/ccache/libexec:$PATH"
mkdir build && cd build
export PATH=/usr/local/opt/ccache/libexec:$PATH
ccache --show-stats > ccache_before
cmake --version
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
make -j4
ccache --show-stats > ccache_after
diff -U100 ccache_before ccache_after || true
ctest -VV -C Release

View File

@@ -41,6 +41,19 @@ function(check_submodules_present)
endfunction()
check_submodules_present()
configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
COPYONLY)
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
message(STATUS "Downloading compatibility list for yuzu...")
file(DOWNLOAD
https://api.yuzu-emu.org/gamedb/
"${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
endif()
if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
endif()
# Detect current compilation architecture and create standard definitions
# =======================================================================

View File

@@ -41,9 +41,9 @@ before_build:
- ps: |
if ($env:BUILD_TYPE -eq 'msvc') {
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 .. 2>&1 && exit 0'
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1 && exit 0'
} else {
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release .. 2>&1"
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1"
}
- cd ..
@@ -162,10 +162,6 @@ artifacts:
- path: $(BUILD_ZIP)
name: build
type: zip
- path: $(BUILD_SYMBOLS)
name: debugsymbols
- path: $(BUILD_UPDATE)
name: update
deploy:
provider: GitHub

View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="compatibility_list">
<file>compatibility_list.json</file>
</qresource>
</RCC>

View File

@@ -764,7 +764,7 @@ size_t ReadFileToString(bool text_file, const char* filename, std::string& str)
IOFile file(filename, text_file ? "r" : "rb");
if (!file.IsOpen())
return false;
return 0;
str.resize(static_cast<u32>(file.GetSize()));
return file.ReadArray(&str[0], str.size());

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/hex_util.h"
#include "common/logging/log.h"
namespace Common {
@@ -13,18 +14,29 @@ u8 ToHexNibble(char c1) {
return c1 - 87;
if (c1 >= 48 && c1 <= 57)
return c1 - 48;
throw std::logic_error("Invalid hex digit");
LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
return 0;
}
std::array<u8, 16> operator""_array16(const char* str, size_t len) {
if (len != 32)
throw std::logic_error("Not of correct size.");
if (len != 32) {
LOG_ERROR(Common,
"Attempting to parse string to array that is not of correct size (expected=32, "
"actual={}).",
len);
return {};
}
return HexStringToArray<16>(str);
}
std::array<u8, 32> operator""_array32(const char* str, size_t len) {
if (len != 64)
throw std::logic_error("Not of correct size.");
if (len != 64) {
LOG_ERROR(Common,
"Attempting to parse string to array that is not of correct size (expected=64, "
"actual={}).",
len);
return {};
}
return HexStringToArray<32>(str);
}

View File

@@ -20,6 +20,8 @@ add_library(core STATIC
crypto/key_manager.h
crypto/ctr_encryption_layer.cpp
crypto/ctr_encryption_layer.h
crypto/xts_encryption_layer.cpp
crypto/xts_encryption_layer.h
file_sys/bis_factory.cpp
file_sys/bis_factory.h
file_sys/card_image.cpp
@@ -57,6 +59,8 @@ add_library(core STATIC
file_sys/vfs_real.h
file_sys/vfs_vector.cpp
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
frontend/emu_window.cpp
frontend/emu_window.h
frontend/framebuffer_layout.cpp
@@ -347,6 +351,8 @@ add_library(core STATIC
loader/linker.h
loader/loader.cpp
loader/loader.h
loader/nax.cpp
loader/nax.h
loader/nca.cpp
loader/nca.h
loader/nro.cpp

View File

@@ -8,6 +8,8 @@
#include "common/common_types.h"
#include "core/hle/kernel/vm_manager.h"
namespace Core {
/// Generic ARM11 CPU interface
class ARM_Interface : NonCopyable {
public:
@@ -122,3 +124,5 @@ public:
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
};
} // namespace Core

View File

@@ -9,11 +9,14 @@
#include "common/logging/log.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
namespace Core {
using Vector = Dynarmic::A64::Vector;
class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
@@ -300,3 +303,5 @@ bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, VAddr vaddr,
Memory::Write64(vaddr, value[1]);
});
}
} // namespace Core

View File

@@ -12,6 +12,8 @@
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
namespace Core {
class ARM_Dynarmic_Callbacks;
class DynarmicExclusiveMonitor;
@@ -81,3 +83,5 @@ private:
friend class ARM_Dynarmic;
Dynarmic::A64::ExclusiveMonitor monitor;
};
} // namespace Core

View File

@@ -4,4 +4,8 @@
#include "core/arm/exclusive_monitor.h"
namespace Core {
ExclusiveMonitor::~ExclusiveMonitor() = default;
} // namespace Core

View File

@@ -6,6 +6,8 @@
#include "common/common_types.h"
namespace Core {
class ExclusiveMonitor {
public:
virtual ~ExclusiveMonitor();
@@ -19,3 +21,5 @@ public:
virtual bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) = 0;
virtual bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) = 0;
};
} // namespace Core

View File

@@ -11,6 +11,8 @@
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
namespace Core {
// Load Unicorn DLL once on Windows using RAII
#ifdef _MSC_VER
#include <unicorn_dynload.h>
@@ -211,7 +213,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
}
}
void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {
void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
int uregs[32];
void* tregs[32];
@@ -238,7 +240,7 @@ void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
}
void ARM_Unicorn::LoadContext(const ARM_Interface::ThreadContext& ctx) {
void ARM_Unicorn::LoadContext(const ThreadContext& ctx) {
int uregs[32];
void* tregs[32];
@@ -277,3 +279,5 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
last_bkpt = bkpt;
last_bkpt_hit = true;
}
} // namespace Core

View File

@@ -9,6 +9,8 @@
#include "core/arm/arm_interface.h"
#include "core/gdbstub/gdbstub.h"
namespace Core {
class ARM_Unicorn final : public ARM_Interface {
public:
ARM_Unicorn();
@@ -46,3 +48,5 @@ private:
GDBStub::BreakpointAddress last_bkpt{};
bool last_bkpt_hit;
};
} // namespace Core

View File

@@ -2,24 +2,36 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <map>
#include <memory>
#include <thread>
#include <utility>
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/controller.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/perf_stats.h"
#include "core/settings.h"
#include "file_sys/vfs_concat.h"
#include "file_sys/vfs_real.h"
#include "core/telemetry_session.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -27,71 +39,9 @@ namespace Core {
/*static*/ System System::s_instance;
System::System() = default;
System::~System() = default;
/// Runs a CPU core while the system is powered on
static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
cpu_state->RunLoop(true);
}
}
Cpu& System::CurrentCpuCore() {
// If multicore is enabled, use host thread to figure out the current CPU core
if (Settings::values.use_multi_core) {
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
ASSERT(search != thread_to_cpu.end());
ASSERT(search->second);
return *search->second;
}
// Otherwise, use single-threaded mode active_core variable
return *cpu_cores[active_core];
}
System::ResultStatus System::RunLoop(bool tight_loop) {
status = ResultStatus::Success;
// Update thread_to_cpu in case Core 0 is run from a different host thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
if (GDBStub::IsServerEnabled()) {
GDBStub::HandlePacket();
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
tight_loop = false;
} else {
return ResultStatus::Success;
}
}
}
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
cpu_cores[active_core]->RunLoop(tight_loop);
if (Settings::values.use_multi_core) {
// Cores 1-3 are run on other threads in this mode
break;
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status;
}
System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
static FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) {
namespace {
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) {
// To account for split 00+01+etc files.
std::string dir_name;
std::string filename;
@@ -121,168 +71,402 @@ static FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem
return vfs->OpenFile(path, FileSys::Mode::Read);
}
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader;
/// Runs a CPU core while the system is powered on
void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
cpu_state->RunLoop(true);
}
std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
app_loader->LoadKernelSystemMode();
}
} // Anonymous namespace
if (system_mode.second != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
static_cast<int>(system_mode.second));
struct System::Impl {
Cpu& CurrentCpuCore() {
if (Settings::values.use_multi_core) {
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
ASSERT(search != thread_to_cpu.end());
ASSERT(search->second);
return *search->second;
}
// Otherwise, use single-threaded mode active_core variable
return *cpu_cores[active_core];
}
ResultStatus RunLoop(bool tight_loop) {
status = ResultStatus::Success;
// Update thread_to_cpu in case Core 0 is run from a different host thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
if (GDBStub::IsServerEnabled()) {
GDBStub::HandlePacket();
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
tight_loop = false;
} else {
return ResultStatus::Success;
}
}
}
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
cpu_cores[active_core]->RunLoop(tight_loop);
if (Settings::values.use_multi_core) {
// Cores 1-3 are run on other threads in this mode
break;
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status;
}
ResultStatus Init(Frontend::EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
kernel.Initialize();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
current_process = Kernel::Process::Create(kernel, "main");
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
Service::Init(service_manager, virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
if (Settings::values.use_multi_core) {
for (size_t index = 0; index < cpu_core_threads.size(); ++index) {
cpu_core_threads[index] =
std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
}
}
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats.BeginSystemFrame();
return ResultStatus::Success;
}
ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader;
}
std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
app_loader->LoadKernelSystemMode();
if (system_mode.second != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
static_cast<int>(system_mode.second));
if (system_mode.second != Loader::ResultStatus::Success)
return ResultStatus::ErrorSystemMode;
}
}
ResultStatus init_result{Init(emu_window)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
System::Shutdown();
return init_result;
}
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
if (Loader::ResultStatus::Success != load_result) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
System::Shutdown();
ResultStatus init_result{Init(emu_window)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
Shutdown();
return init_result;
}
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
if (load_result != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
Shutdown();
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
}
status = ResultStatus::Success;
return status;
}
status = ResultStatus::Success;
return status;
void Shutdown() {
// Log last frame performance stats
auto perf_results = GetAndResetPerfStats();
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
perf_results.emulation_speed * 100.0);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
perf_results.game_fps);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
// Shutdown emulation session
renderer.reset();
GDBStub::Shutdown();
Service::Shutdown();
service_manager.reset();
telemetry_session.reset();
gpu_core.reset();
// Close all CPU/threading state
cpu_barrier->NotifyEnd();
if (Settings::values.use_multi_core) {
for (auto& thread : cpu_core_threads) {
thread->join();
thread.reset();
}
}
thread_to_cpu.clear();
for (auto& cpu_core : cpu_cores) {
cpu_core.reset();
}
cpu_barrier.reset();
// Shutdown kernel and core timing
kernel.Shutdown();
CoreTiming::Shutdown();
// Close app loader
app_loader.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
Loader::ResultStatus GetGameName(std::string& out) const {
if (app_loader == nullptr)
return Loader::ResultStatus::ErrorNotInitialized;
return app_loader->ReadTitle(out);
}
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
status = new_status;
if (details) {
status_details = details;
}
}
PerfStatsResults GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
}
Kernel::KernelCore kernel;
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
size_t active_core{}; ///< Active core, only used in single thread mode
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
/// Map of guest threads to CPU cores
std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
Core::PerfStats perf_stats;
Core::FrameLimiter frame_limiter;
};
System::System() : impl{std::make_unique<Impl>()} {}
System::~System() = default;
Cpu& System::CurrentCpuCore() {
return impl->CurrentCpuCore();
}
System::ResultStatus System::RunLoop(bool tight_loop) {
return impl->RunLoop(tight_loop);
}
System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
void System::InvalidateCpuInstructionCaches() {
for (auto& cpu : impl->cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
return impl->Load(emu_window, filepath);
}
bool System::IsPoweredOn() const {
return impl->cpu_barrier && impl->cpu_barrier->IsAlive();
}
void System::PrepareReschedule() {
CurrentCpuCore().PrepareReschedule();
}
PerfStats::Results System::GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
PerfStatsResults System::GetAndResetPerfStats() {
return impl->GetAndResetPerfStats();
}
Core::TelemetrySession& System::TelemetrySession() const {
return *impl->telemetry_session;
}
ARM_Interface& System::CurrentArmInterface() {
return CurrentCpuCore().ArmInterface();
}
size_t System::CurrentCoreIndex() {
return CurrentCpuCore().CoreIndex();
}
Kernel::Scheduler& System::CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
}
const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES);
return cpu_cores[core_index]->Scheduler();
return impl->cpu_cores[core_index]->Scheduler();
}
Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() {
return impl->current_process;
}
ARM_Interface& System::ArmInterface(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES);
return cpu_cores[core_index]->ArmInterface();
return impl->cpu_cores[core_index]->ArmInterface();
}
Cpu& System::CpuCore(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES);
return *cpu_cores[core_index];
return *impl->cpu_cores[core_index];
}
ExclusiveMonitor& System::Monitor() {
return *impl->cpu_exclusive_monitor;
}
Tegra::GPU& System::GPU() {
return *impl->gpu_core;
}
const Tegra::GPU& System::GPU() const {
return *impl->gpu_core;
}
VideoCore::RendererBase& System::Renderer() {
return *impl->renderer;
}
const VideoCore::RendererBase& System::Renderer() const {
return *impl->renderer;
}
Kernel::KernelCore& System::Kernel() {
return impl->kernel;
}
const Kernel::KernelCore& System::Kernel() const {
return impl->kernel;
}
Core::PerfStats& System::GetPerfStats() {
return impl->perf_stats;
}
const Core::PerfStats& System::GetPerfStats() const {
return impl->perf_stats;
}
Core::FrameLimiter& System::FrameLimiter() {
return impl->frame_limiter;
}
const Core::FrameLimiter& System::FrameLimiter() const {
return impl->frame_limiter;
}
Loader::ResultStatus System::GetGameName(std::string& out) const {
return impl->GetGameName(out);
}
void System::SetStatus(ResultStatus new_status, const char* details) {
impl->SetStatus(new_status, details);
}
const std::string& System::GetStatusDetails() const {
return impl->status_details;
}
Loader::AppLoader& System::GetAppLoader() const {
return *impl->app_loader;
}
void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
impl->debug_context = std::move(context);
}
Tegra::DebugContext* System::GetGPUDebugContext() const {
return impl->debug_context.get();
}
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
impl->virtual_filesystem = std::move(vfs);
}
std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
current_process = Kernel::Process::Create("main");
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
Kernel::Init();
Service::Init(service_manager, virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
if (Settings::values.use_multi_core) {
for (size_t index = 0; index < cpu_core_threads.size(); ++index) {
cpu_core_threads[index] =
std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
}
}
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats.BeginSystemFrame();
return ResultStatus::Success;
return impl->Init(emu_window);
}
void System::Shutdown() {
// Log last frame performance stats
auto perf_results = GetAndResetPerfStats();
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
perf_results.emulation_speed * 100.0);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
perf_results.game_fps);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
// Shutdown emulation session
renderer.reset();
GDBStub::Shutdown();
Service::Shutdown();
Kernel::Shutdown();
service_manager.reset();
telemetry_session.reset();
gpu_core.reset();
// Close all CPU/threading state
cpu_barrier->NotifyEnd();
if (Settings::values.use_multi_core) {
for (auto& thread : cpu_core_threads) {
thread->join();
thread.reset();
}
}
thread_to_cpu.clear();
for (auto& cpu_core : cpu_cores) {
cpu_core.reset();
}
cpu_barrier.reset();
// Close core timing
CoreTiming::Shutdown();
// Close app loader
app_loader.reset();
LOG_DEBUG(Core, "Shutdown OK");
impl->Shutdown();
}
Service::SM::ServiceManager& System::ServiceManager() {
return *service_manager;
return *impl->service_manager;
}
const Service::SM::ServiceManager& System::ServiceManager() const {
return *service_manager;
return *impl->service_manager;
}
} // namespace Core

View File

@@ -4,40 +4,56 @@
#pragma once
#include <array>
#include <cstddef>
#include <memory>
#include <string>
#include <thread>
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/scheduler.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/telemetry_session.h"
#include "file_sys/vfs_real.h"
#include "hle/service/filesystem/filesystem.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
class ARM_Interface;
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
namespace Core::Frontend {
class EmuWindow;
}
} // namespace Core::Frontend
namespace FileSys {
class VfsFilesystem;
} // namespace FileSys
namespace Kernel {
class KernelCore;
class Process;
class Scheduler;
} // namespace Kernel
namespace Loader {
class AppLoader;
enum class ResultStatus : u16;
} // namespace Loader
namespace Service::SM {
class ServiceManager;
}
} // namespace Service::SM
namespace Tegra {
class DebugContext;
class GPU;
} // namespace Tegra
namespace VideoCore {
class RendererBase;
}
} // namespace VideoCore
namespace Core {
class ARM_Interface;
class Cpu;
class ExclusiveMonitor;
class FrameLimiter;
class PerfStats;
class TelemetrySession;
struct PerfStatsResults;
class System {
public:
System(const System&) = delete;
@@ -92,11 +108,7 @@ public:
* This function should only be used by GDB Stub to support breakpoints, memory updates and
* step/continue commands.
*/
void InvalidateCpuInstructionCaches() {
for (auto& cpu : cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
void InvalidateCpuInstructionCaches();
/// Shutdown the emulated system.
void Shutdown();
@@ -115,33 +127,28 @@ public:
* application).
* @returns True if the emulated system is powered on, otherwise false.
*/
bool IsPoweredOn() const {
return cpu_barrier && cpu_barrier->IsAlive();
}
bool IsPoweredOn() const;
/**
* Returns a reference to the telemetry session for this emulation session.
* @returns Reference to the telemetry session.
*/
Core::TelemetrySession& TelemetrySession() const {
return *telemetry_session;
}
Core::TelemetrySession& TelemetrySession() const;
/// Prepare the core emulation for a reschedule
void PrepareReschedule();
/// Gets and resets core performance statistics
PerfStats::Results GetAndResetPerfStats();
PerfStatsResults GetAndResetPerfStats();
/// Gets an ARM interface to the CPU core that is currently running
ARM_Interface& CurrentArmInterface() {
return CurrentCpuCore().ArmInterface();
}
ARM_Interface& CurrentArmInterface();
/// Gets the index of the currently running CPU core
size_t CurrentCoreIndex() {
return CurrentCpuCore().CoreIndex();
}
size_t CurrentCoreIndex();
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler();
/// Gets an ARM interface to the CPU core with the specified index
ARM_Interface& ArmInterface(size_t core_index);
@@ -149,80 +156,64 @@ public:
/// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(size_t core_index);
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor();
/// Gets a mutable reference to the GPU interface
Tegra::GPU& GPU() {
return *gpu_core;
}
Tegra::GPU& GPU();
/// Gets an immutable reference to the GPU interface.
const Tegra::GPU& GPU() const {
return *gpu_core;
}
const Tegra::GPU& GPU() const;
/// Gets a mutable reference to the renderer.
VideoCore::RendererBase& Renderer() {
return *renderer;
}
VideoCore::RendererBase& Renderer();
/// Gets an immutable reference to the renderer.
const VideoCore::RendererBase& Renderer() const {
return *renderer;
}
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
}
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor() {
return *cpu_exclusive_monitor;
}
const VideoCore::RendererBase& Renderer() const;
/// Gets the scheduler for the CPU core with the specified index
const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index);
/// Gets the current process
Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
return current_process;
}
Kernel::SharedPtr<Kernel::Process>& CurrentProcess();
PerfStats perf_stats;
FrameLimiter frame_limiter;
/// Provides a reference to the kernel instance.
Kernel::KernelCore& Kernel();
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
status = new_status;
if (details) {
status_details = details;
}
}
/// Provides a constant reference to the kernel instance.
const Kernel::KernelCore& Kernel() const;
const std::string& GetStatusDetails() const {
return status_details;
}
/// Provides a reference to the internal PerfStats instance.
Core::PerfStats& GetPerfStats();
Loader::AppLoader& GetAppLoader() const {
return *app_loader;
}
/// Provides a constant reference to the internal PerfStats instance.
const Core::PerfStats& GetPerfStats() const;
/// Provides a reference to the frame limiter;
Core::FrameLimiter& FrameLimiter();
/// Provides a constant referent to the frame limiter
const Core::FrameLimiter& FrameLimiter() const;
/// Gets the name of the current game
Loader::ResultStatus GetGameName(std::string& out) const;
void SetStatus(ResultStatus new_status, const char* details);
const std::string& GetStatusDetails() const;
Loader::AppLoader& GetAppLoader() const;
Service::SM::ServiceManager& ServiceManager();
const Service::SM::ServiceManager& ServiceManager() const;
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
debug_context = std::move(context);
}
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context);
std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const {
return debug_context;
}
Tegra::DebugContext* GetGPUDebugContext() const;
void SetFilesystem(FileSys::VirtualFilesystem vfs) {
virtual_filesystem = std::move(vfs);
}
void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs);
FileSys::VirtualFilesystem GetFilesystem() const {
return virtual_filesystem;
}
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
private:
System();
@@ -238,33 +229,10 @@ private:
*/
ResultStatus Init(Frontend::EmuWindow& emu_window);
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
size_t active_core{}; ///< Active core, only used in single thread mode
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
struct Impl;
std::unique_ptr<Impl> impl;
static System s_instance;
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
/// Map of guest threads to CPU cores
std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
};
inline ARM_Interface& CurrentArmInterface() {

View File

@@ -12,14 +12,14 @@
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
class ARM_Interface;
namespace Kernel {
class Scheduler;
}
namespace Core {
class ARM_Interface;
constexpr unsigned NUM_CPU_CORES{4};
class CpuBarrier {

View File

@@ -99,10 +99,7 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id,
size_t sector_size, Op op) {
if (size % sector_size > 0) {
LOG_CRITICAL(Crypto, "Data size must be a multiple of sector size.");
return;
}
ASSERT_MSG(size % sector_size == 0, "XTS decryption size must be a multiple of sector size.");
for (size_t i = 0; i < size; i += sector_size) {
SetIV(CalculateNintendoTweak(sector_id++));
@@ -112,4 +109,4 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest,
template class AESCipher<Key128>;
template class AESCipher<Key256>;
} // namespace Core::Crypto
} // namespace Core::Crypto

View File

@@ -20,10 +20,8 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
if (sector_offset == 0) {
UpdateIV(base_offset + offset);
std::vector<u8> raw = base->ReadBytes(length, offset);
if (raw.size() != length)
return Read(data, raw.size(), offset);
cipher.Transcode(raw.data(), length, data, Op::Decrypt);
return length;
cipher.Transcode(raw.data(), raw.size(), data, Op::Decrypt);
return raw.size();
}
// offset does not fall on block boundary (0x10)
@@ -34,7 +32,7 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
if (length + sector_offset < 0x10) {
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
return read;
return std::min<u64>(length, read);
}
std::memcpy(data, block.data() + sector_offset, read);
return read + Read(data + read, length - read, offset + read);

View File

@@ -12,11 +12,112 @@
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
#include "core/settings.h"
namespace Core::Crypto {
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
Key128 out{};
AESCipher<Key128> cipher1(master, Mode::ECB);
cipher1.Transcode(kek_seed.data(), kek_seed.size(), out.data(), Op::Decrypt);
AESCipher<Key128> cipher2(out, Mode::ECB);
cipher2.Transcode(source.data(), source.size(), out.data(), Op::Decrypt);
if (key_seed != Key128{}) {
AESCipher<Key128> cipher3(out, Mode::ECB);
cipher3.Transcode(key_seed.data(), key_seed.size(), out.data(), Op::Decrypt);
}
return out;
}
boost::optional<Key128> DeriveSDSeed() {
const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000043",
"rb+");
if (!save_43.IsOpen())
return boost::none;
const FileUtil::IOFile sd_private(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
if (!sd_private.IsOpen())
return boost::none;
sd_private.Seek(0, SEEK_SET);
std::array<u8, 0x10> private_seed{};
if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != 0x10)
return boost::none;
std::array<u8, 0x10> buffer{};
size_t offset = 0;
for (; offset + 0x10 < save_43.GetSize(); ++offset) {
save_43.Seek(offset, SEEK_SET);
save_43.ReadBytes(buffer.data(), buffer.size());
if (buffer == private_seed)
break;
}
if (offset + 0x10 >= save_43.GetSize())
return boost::none;
Key128 seed{};
save_43.Seek(offset + 0x10, SEEK_SET);
save_43.ReadBytes(seed.data(), seed.size());
return seed;
}
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys) {
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK)))
return Loader::ResultStatus::ErrorMissingSDKEKSource;
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration)))
return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)))
return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
const auto sd_kek_source =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK));
const auto aes_kek_gen =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration));
const auto aes_key_gen =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
const auto master_00 = keys.GetKey(S128KeyType::Master);
const auto sd_kek =
GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
if (!keys.HasKey(S128KeyType::SDSeed))
return Loader::ResultStatus::ErrorMissingSDSeed;
const auto sd_seed = keys.GetKey(S128KeyType::SDSeed);
if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)))
return Loader::ResultStatus::ErrorMissingSDSaveKeySource;
if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA)))
return Loader::ResultStatus::ErrorMissingSDNCAKeySource;
std::array<Key256, 2> sd_key_sources{
keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)),
keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA)),
};
// Combine sources and seed
for (auto& source : sd_key_sources) {
for (size_t i = 0; i < source.size(); ++i)
source[i] ^= sd_seed[i & 0xF];
}
AESCipher<Key128> cipher(sd_kek, Mode::ECB);
// The transform manipulates sd_keys as part of the Transcode, so the return/output is
// unnecessary. This does not alter sd_keys_sources.
std::transform(sd_key_sources.begin(), sd_key_sources.end(), sd_keys.begin(),
sd_key_sources.begin(), [&cipher](const Key256& source, Key256& out) {
cipher.Transcode(source.data(), source.size(), out.data(), Op::Decrypt);
return source; ///< Return unaltered source to satisfy output requirement.
});
return Loader::ResultStatus::Success;
}
KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
@@ -24,12 +125,15 @@ KeyManager::KeyManager() {
if (Settings::values.use_dev_keys) {
dev_mode = true;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "dev.keys_autogenerated", false);
} else {
dev_mode = false;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "prod.keys", false);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "prod.keys_autogenerated", false);
}
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true);
}
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
@@ -56,17 +160,17 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
u128 rights_id{};
std::memcpy(rights_id.data(), rights_id_raw.data(), rights_id_raw.size());
Key128 key = Common::HexStringToArray<16>(out[1]);
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
} else {
std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower);
if (s128_file_id.find(out[0]) != s128_file_id.end()) {
const auto index = s128_file_id.at(out[0]);
Key128 key = Common::HexStringToArray<16>(out[1]);
SetKey(index.type, key, index.field1, index.field2);
s128_keys[{index.type, index.field1, index.field2}] = key;
} else if (s256_file_id.find(out[0]) != s256_file_id.end()) {
const auto index = s256_file_id.at(out[0]);
Key256 key = Common::HexStringToArray<32>(out[1]);
SetKey(index.type, key, index.field1, index.field2);
s256_keys[{index.type, index.field1, index.field2}] = key;
}
}
}
@@ -100,11 +204,50 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
return s256_keys.at({id, field1, field2});
}
template <size_t Size>
void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
const std::array<u8, Size>& key) {
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
std::string filename = "title.keys_autogenerated";
if (!title_key)
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename);
FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename);
std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app);
if (!file.is_open())
return;
if (add_info_text) {
file
<< "# This file is autogenerated by Yuzu\n"
<< "# It serves to store keys that were automatically generated from the normal keys\n"
<< "# If you are experiencing issues involving keys, it may help to delete this file\n";
}
file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, title_key);
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
const auto iter = std::find_if(
s128_file_id.begin(), s128_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
if (iter != s128_file_id.end())
WriteKeyToFile(id == S128KeyType::Titlekey, iter->first, key);
s128_keys[{id, field1, field2}] = key;
}
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
const auto iter = std::find_if(
s256_file_id.begin(), s256_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
if (iter != s256_file_id.end())
WriteKeyToFile(false, iter->first, key);
s256_keys[{id, field1, field2}] = key;
}
@@ -125,7 +268,16 @@ bool KeyManager::KeyFileExists(bool title) {
FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
}
const std::unordered_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
void KeyManager::DeriveSDSeedLazy() {
if (HasKey(S128KeyType::SDSeed))
return;
const auto res = DeriveSDSeed();
if (res != boost::none)
SetKey(S128KeyType::SDSeed, res.get());
}
const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
{"master_key_00", {S128KeyType::Master, 0, 0}},
{"master_key_01", {S128KeyType::Master, 1, 0}},
{"master_key_02", {S128KeyType::Master, 2, 0}},
@@ -167,11 +319,17 @@ const std::unordered_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_fi
{"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
{"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK), 0}},
{"aes_kek_generation_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration), 0}},
{"aes_key_generation_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
{"sd_seed", {S128KeyType::SDSeed, 0, 0}},
};
const std::unordered_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
{"header_key", {S256KeyType::Header, 0, 0}},
{"sd_card_save_key", {S256KeyType::SDSave, 0, 0}},
{"sd_card_nca_key", {S256KeyType::SDNCA, 0, 0}},
{"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
{"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
};
} // namespace Core::Crypto

View File

@@ -6,11 +6,13 @@
#include <array>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include <boost/container/flat_map.hpp>
#include <fmt/format.h>
#include "common/common_types.h"
#include "core/loader/loader.h"
namespace Core::Crypto {
@@ -22,9 +24,8 @@ static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big.");
enum class S256KeyType : u64 {
Header, //
SDSave, //
SDNCA, //
Header, //
SDKeySource, // f1=SDKeyType
};
enum class S128KeyType : u64 {
@@ -36,6 +37,7 @@ enum class S128KeyType : u64 {
KeyArea, // f1=crypto revision f2=type {app, ocean, system}
SDSeed, //
Titlekey, // f1=rights id LSB f2=rights id MSB
Source, // f1=source type, f2= sub id
};
enum class KeyAreaKeyType : u8 {
@@ -44,6 +46,17 @@ enum class KeyAreaKeyType : u8 {
System,
};
enum class SourceKeyType : u8 {
SDKEK,
AESKEKGeneration,
AESKeyGeneration,
};
enum class SDKeyType : u8 {
Save,
NCA,
};
template <typename KeyType>
struct KeyIndex {
KeyType type;
@@ -59,34 +72,12 @@ struct KeyIndex {
}
};
// The following two (== and hash) are so KeyIndex can be a key in unordered_map
// boost flat_map requires operator< for O(log(n)) lookups.
template <typename KeyType>
bool operator==(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return std::tie(lhs.type, lhs.field1, lhs.field2) == std::tie(rhs.type, rhs.field1, rhs.field2);
bool operator<(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return std::tie(lhs.type, lhs.field1, lhs.field2) < std::tie(rhs.type, rhs.field1, rhs.field2);
}
template <typename KeyType>
bool operator!=(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return !operator==(lhs, rhs);
}
} // namespace Core::Crypto
namespace std {
template <typename KeyType>
struct hash<Core::Crypto::KeyIndex<KeyType>> {
size_t operator()(const Core::Crypto::KeyIndex<KeyType>& k) const {
using std::hash;
return ((hash<u64>()(static_cast<u64>(k.type)) ^ (hash<u64>()(k.field1) << 1)) >> 1) ^
(hash<u64>()(k.field2) << 1);
}
};
} // namespace std
namespace Core::Crypto {
class KeyManager {
public:
KeyManager();
@@ -102,16 +93,27 @@ public:
static bool KeyFileExists(bool title);
// Call before using the sd seed to attempt to derive it if it dosen't exist. Needs system save
// 8*43 and the private file to exist.
void DeriveSDSeedLazy();
private:
std::unordered_map<KeyIndex<S128KeyType>, Key128> s128_keys;
std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
boost::container::flat_map<KeyIndex<S128KeyType>, Key128> s128_keys;
boost::container::flat_map<KeyIndex<S256KeyType>, Key256> s256_keys;
bool dev_mode;
void LoadFromFile(const std::string& filename, bool is_title_keys);
void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title);
template <size_t Size>
void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key);
static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
};
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
boost::optional<Key128> DeriveSDSeed();
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys);
} // namespace Core::Crypto

View File

@@ -0,0 +1,58 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "common/assert.h"
#include "core/crypto/xts_encryption_layer.h"
namespace Core::Crypto {
constexpr u64 XTS_SECTOR_SIZE = 0x4000;
XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_)
: EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {}
size_t XTSEncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
if (length == 0)
return 0;
const auto sector_offset = offset & 0x3FFF;
if (sector_offset == 0) {
if (length % XTS_SECTOR_SIZE == 0) {
std::vector<u8> raw = base->ReadBytes(length, offset);
cipher.XTSTranscode(raw.data(), raw.size(), data, offset / XTS_SECTOR_SIZE,
XTS_SECTOR_SIZE, Op::Decrypt);
return raw.size();
}
if (length > XTS_SECTOR_SIZE) {
const auto rem = length % XTS_SECTOR_SIZE;
const auto read = length - rem;
return Read(data, read, offset) + Read(data + read, rem, offset + read);
}
std::vector<u8> buffer = base->ReadBytes(XTS_SECTOR_SIZE, offset);
if (buffer.size() < XTS_SECTOR_SIZE)
buffer.resize(XTS_SECTOR_SIZE);
cipher.XTSTranscode(buffer.data(), buffer.size(), buffer.data(), offset / XTS_SECTOR_SIZE,
XTS_SECTOR_SIZE, Op::Decrypt);
std::memcpy(data, buffer.data(), std::min(buffer.size(), length));
return std::min(buffer.size(), length);
}
// offset does not fall on block boundary (0x4000)
std::vector<u8> block = base->ReadBytes(0x4000, offset - sector_offset);
if (block.size() < XTS_SECTOR_SIZE)
block.resize(XTS_SECTOR_SIZE);
cipher.XTSTranscode(block.data(), block.size(), block.data(),
(offset - sector_offset) / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt);
const size_t read = XTS_SECTOR_SIZE - sector_offset;
if (length + sector_offset < XTS_SECTOR_SIZE) {
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
return std::min<u64>(length, read);
}
std::memcpy(data, block.data() + sector_offset, read);
return read + Read(data + read, length - read, offset + read);
}
} // namespace Core::Crypto

View File

@@ -0,0 +1,25 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/crypto/aes_util.h"
#include "core/crypto/encryption_layer.h"
#include "core/crypto/key_manager.h"
namespace Core::Crypto {
// Sits on top of a VirtualFile and provides XTS-mode AES decription.
class XTSEncryptionLayer : public EncryptionLayer {
public:
XTSEncryptionLayer(FileSys::VirtualFile base, Key256 key);
size_t Read(u8* data, size_t length, size_t offset) const override;
private:
// Must be mutable as operations modify cipher contexts.
mutable AESCipher<Key256> cipher;
};
} // namespace Core::Crypto

View File

@@ -6,19 +6,12 @@
namespace FileSys {
static VirtualDir GetOrCreateDirectory(const VirtualDir& dir, std::string_view path) {
const auto res = dir->GetDirectoryRelative(path);
if (res == nullptr)
return dir->CreateDirectoryRelative(path);
return res;
}
BISFactory::BISFactory(VirtualDir nand_root_)
: nand_root(std::move(nand_root_)),
sysnand_cache(std::make_shared<RegisteredCache>(
GetOrCreateDirectory(nand_root, "/system/Contents/registered"))),
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_shared<RegisteredCache>(
GetOrCreateDirectory(nand_root, "/user/Contents/registered"))) {}
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const {
return sysnand_cache;

View File

@@ -43,6 +43,8 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
}
program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
auto result = AddNCAFromPartition(XCIPartition::Secure);
if (result != Loader::ResultStatus::Success) {
status = result;
@@ -76,6 +78,10 @@ Loader::ResultStatus XCI::GetStatus() const {
return status;
}
Loader::ResultStatus XCI::GetProgramNCAStatus() const {
return program_nca_status;
}
VirtualDir XCI::GetPartition(XCIPartition partition) const {
return partitions[static_cast<size_t>(partition)];
}
@@ -143,6 +149,12 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
if (file->GetExtension() != "nca")
continue;
auto nca = std::make_shared<NCA>(file);
// TODO(DarkLordZach): Add proper Rev1+ Support
if (nca->IsUpdate())
continue;
if (nca->GetType() == NCAContentType::Program) {
program_nca_status = nca->GetStatus();
}
if (nca->GetStatus() == Loader::ResultStatus::Success) {
ncas.push_back(std::move(nca));
} else {

View File

@@ -59,6 +59,7 @@ public:
explicit XCI(VirtualFile file);
Loader::ResultStatus GetStatus() const;
Loader::ResultStatus GetProgramNCAStatus() const;
u8 GetFormatVersion() const;
@@ -90,6 +91,7 @@ private:
GamecardHeader header{};
Loader::ResultStatus status;
Loader::ResultStatus program_nca_status;
std::vector<VirtualDir> partitions;
std::vector<std::shared_ptr<NCA>> ncas;

View File

@@ -178,7 +178,7 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
return std::static_pointer_cast<VfsFile>(out);
}
case NCASectionCryptoType::XTS:
// TODO(DarkLordZach): Implement XTSEncryptionLayer.
// TODO(DarkLordZach): Find a test case for XTS-encrypted NCAs
default:
LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
static_cast<u8>(s_header.raw.header.crypto_type));
@@ -258,6 +258,10 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
}
is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
}) != sections.end();
for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
auto section = sections[i];
@@ -358,6 +362,10 @@ VirtualFile NCA::GetBaseFile() const {
return file;
}
bool NCA::IsUpdate() const {
return is_update;
}
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}

View File

@@ -93,6 +93,8 @@ public:
VirtualFile GetBaseFile() const;
bool IsUpdate() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
@@ -111,6 +113,7 @@ private:
NCAHeader header{};
bool has_rights_id{};
bool is_update{};
Loader::ResultStatus status{};

View File

@@ -5,9 +5,9 @@
#include <regex>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/encryption_layer.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
@@ -216,11 +216,11 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
const auto section0 = nca->GetSubdirectories()[0];
for (const auto& file : section0->GetFiles()) {
if (file->GetExtension() != "cnmt")
for (const auto& section0_file : section0->GetFiles()) {
if (section0_file->GetExtension() != "cnmt")
continue;
meta.insert_or_assign(nca->GetTitleId(), CNMT(file));
meta.insert_or_assign(nca->GetTitleId(), CNMT(section0_file));
meta_id.insert_or_assign(nca->GetTitleId(), id);
break;
}
@@ -254,6 +254,8 @@ RegisteredCache::RegisteredCache(VirtualDir dir_, RegisteredCacheParsingFunction
Refresh();
}
RegisteredCache::~RegisteredCache() = default;
bool RegisteredCache::HasEntry(u64 title_id, ContentRecordType type) const {
return GetEntryRaw(title_id, type) != nullptr;
}
@@ -262,6 +264,18 @@ bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry) != nullptr;
}
VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
if (id == boost::none)
return nullptr;
return GetFileAtID(id.get());
}
VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
if (id == boost::none)

View File

@@ -63,12 +63,16 @@ public:
explicit RegisteredCache(VirtualDir dir,
RegisteredCacheParsingFunction parsing_function =
[](const VirtualFile& file, const NcaID& id) { return file; });
~RegisteredCache();
void Refresh();
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;

View File

@@ -7,6 +7,7 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs.h"
#include "core/hle/kernel/process.h"
namespace FileSys {

View File

@@ -8,6 +8,7 @@
#include <string>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace FileSys {

View File

@@ -3,14 +3,27 @@
// Refer to the license.txt file included.
#include <memory>
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/xts_archive.h"
namespace FileSys {
SDMCFactory::SDMCFactory(VirtualDir dir) : dir(std::move(dir)) {}
SDMCFactory::SDMCFactory(VirtualDir dir_)
: dir(std::move(dir_)), contents(std::make_shared<RegisteredCache>(
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
[](const VirtualFile& file, const NcaID& id) {
return std::make_shared<NAX>(file, id)->GetDecrypted();
})) {}
SDMCFactory::~SDMCFactory() = default;
ResultVal<VirtualDir> SDMCFactory::Open() {
return MakeResult<VirtualDir>(dir);
}
std::shared_ptr<RegisteredCache> SDMCFactory::GetSDMCContents() const {
return contents;
}
} // namespace FileSys

View File

@@ -4,20 +4,27 @@
#pragma once
#include <memory>
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace FileSys {
class RegisteredCache;
/// File system interface to the SDCard archive
class SDMCFactory {
public:
explicit SDMCFactory(VirtualDir dir);
~SDMCFactory();
ResultVal<VirtualDir> Open();
std::shared_ptr<RegisteredCache> GetSDMCContents() const;
private:
VirtualDir dir;
std::shared_ptr<RegisteredCache> contents;
};
} // namespace FileSys

View File

@@ -462,4 +462,11 @@ bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
std::vector<u8> data = src->ReadAllBytes();
return dest->WriteBytes(data, 0) == data.size();
}
VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) {
const auto res = rel->GetDirectoryRelative(path);
if (res == nullptr)
return rel->CreateDirectoryRelative(path);
return res;
}
} // namespace FileSys

View File

@@ -318,4 +318,8 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block
// directory of src/dest.
bool VfsRawCopy(VirtualFile src, VirtualFile dest);
// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
// it attempts to create it and returns the new dir or nullptr on failure.
VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path);
} // namespace FileSys

View File

@@ -8,6 +8,7 @@
#include <utility>
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
@@ -39,6 +40,7 @@ static std::string ModeFlagsToString(Mode mode) {
}
RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
RealVfsFilesystem::~RealVfsFilesystem() = default;
std::string RealVfsFilesystem::GetName() const {
return "Real";
@@ -219,6 +221,8 @@ RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOF
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {}
RealVfsFile::~RealVfsFile() = default;
std::string RealVfsFile::GetName() const {
return path_components.back();
}
@@ -312,6 +316,8 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string&
FileUtil::CreateDir(path);
}
RealVfsDirectory::~RealVfsDirectory() = default;
std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path))
@@ -341,7 +347,6 @@ std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view p
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
auto parent = std::string(FileUtil::GetParentPath(full_path));
return base.CreateDirectory(full_path, perms);
}

View File

@@ -6,15 +6,19 @@
#include <string_view>
#include <boost/container/flat_map.hpp>
#include "common/file_util.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
namespace FileUtil {
class IOFile;
}
namespace FileSys {
class RealVfsFilesystem : public VfsFilesystem {
public:
RealVfsFilesystem();
~RealVfsFilesystem() override;
std::string GetName() const override;
bool IsReadable() const override;
@@ -40,10 +44,9 @@ class RealVfsFile : public VfsFile {
friend class RealVfsDirectory;
friend class RealVfsFilesystem;
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
const std::string& path, Mode perms = Mode::Read);
public:
~RealVfsFile() override;
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
@@ -55,6 +58,9 @@ public:
bool Rename(std::string_view name) override;
private:
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
const std::string& path, Mode perms = Mode::Read);
bool Close();
RealVfsFilesystem& base;
@@ -70,9 +76,9 @@ private:
class RealVfsDirectory : public VfsDirectory {
friend class RealVfsFilesystem;
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
public:
~RealVfsDirectory() override;
std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
@@ -97,6 +103,8 @@ protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
template <typename T, typename R>
std::vector<std::shared_ptr<R>> IterateEntries() const;

View File

@@ -0,0 +1,170 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <cstring>
#include <regex>
#include <string>
#include <mbedtls/md.h>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/xts_encryption_layer.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/xts_archive.h"
#include "core/loader/loader.h"
namespace FileSys {
constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000;
template <typename SourceData, typename SourceKey, typename Destination>
static bool CalculateHMAC256(Destination* out, const SourceKey* key, size_t key_length,
const SourceData* data, size_t data_length) {
mbedtls_md_context_t context;
mbedtls_md_init(&context);
const auto key_f = reinterpret_cast<const u8*>(key);
const std::vector<u8> key_v(key_f, key_f + key_length);
if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) ||
mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) ||
mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) ||
mbedtls_md_hmac_finish(&context, reinterpret_cast<u8*>(out))) {
mbedtls_md_free(&context);
return false;
}
mbedtls_md_free(&context);
return true;
}
NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NAXHeader>()) {
std::string path = FileUtil::SanitizePath(file->GetFullPath());
static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
std::regex_constants::ECMAScript |
std::regex_constants::icase);
std::smatch match;
if (!std::regex_search(path, match, nax_path_regex)) {
status = Loader::ResultStatus::ErrorBadNAXFilePath;
return;
}
std::string two_dir = match[1];
std::string nca_id = match[2];
std::transform(two_dir.begin(), two_dir.end(), two_dir.begin(), ::toupper);
std::transform(nca_id.begin(), nca_id.end(), nca_id.begin(), ::tolower);
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
}
NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
: file(std::move(file_)), header(std::make_unique<NAXHeader>()) {
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
Common::HexArrayToString(nca_id, false)));
}
Loader::ResultStatus NAX::Parse(std::string_view path) {
if (file->ReadObject(header.get()) != sizeof(NAXHeader))
return Loader::ResultStatus::ErrorBadNAXHeader;
if (header->magic != Common::MakeMagic('N', 'A', 'X', '0'))
return Loader::ResultStatus::ErrorBadNAXHeader;
if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size)
return Loader::ResultStatus::ErrorIncorrectNAXFileSize;
keys.DeriveSDSeedLazy();
std::array<Core::Crypto::Key256, 2> sd_keys{};
const auto sd_keys_res = Core::Crypto::DeriveSDKeys(sd_keys, keys);
if (sd_keys_res != Loader::ResultStatus::Success) {
return sd_keys_res;
}
const auto enc_keys = header->key_area;
size_t i = 0;
for (; i < sd_keys.size(); ++i) {
std::array<Core::Crypto::Key128, 2> nax_keys{};
if (!CalculateHMAC256(nax_keys.data(), sd_keys[i].data(), 0x10, std::string(path).c_str(),
path.size())) {
return Loader::ResultStatus::ErrorNAXKeyHMACFailed;
}
for (size_t j = 0; j < nax_keys.size(); ++j) {
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(nax_keys[j],
Core::Crypto::Mode::ECB);
cipher.Transcode(enc_keys[j].data(), 0x10, header->key_area[j].data(),
Core::Crypto::Op::Decrypt);
}
Core::Crypto::SHA256Hash validation{};
if (!CalculateHMAC256(validation.data(), &header->magic, 0x60, sd_keys[i].data() + 0x10,
0x10)) {
return Loader::ResultStatus::ErrorNAXValidationHMACFailed;
}
if (header->hmac == validation)
break;
}
if (i == 2) {
return Loader::ResultStatus::ErrorNAXKeyDerivationFailed;
}
type = static_cast<NAXContentType>(i);
Core::Crypto::Key256 final_key{};
std::memcpy(final_key.data(), &header->key_area, final_key.size());
const auto enc_file =
std::make_shared<OffsetVfsFile>(file, header->file_size, NAX_HEADER_PADDING_SIZE);
dec_file = std::make_shared<Core::Crypto::XTSEncryptionLayer>(enc_file, final_key);
return Loader::ResultStatus::Success;
}
Loader::ResultStatus NAX::GetStatus() const {
return status;
}
VirtualFile NAX::GetDecrypted() const {
return dec_file;
}
std::shared_ptr<NCA> NAX::AsNCA() const {
if (type == NAXContentType::NCA)
return std::make_shared<NCA>(GetDecrypted());
return nullptr;
}
NAXContentType NAX::GetContentType() const {
return type;
}
std::vector<std::shared_ptr<VfsFile>> NAX::GetFiles() const {
return {dec_file};
}
std::vector<std::shared_ptr<VfsDirectory>> NAX::GetSubdirectories() const {
return {};
}
std::string NAX::GetName() const {
return file->GetName();
}
std::shared_ptr<VfsDirectory> NAX::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool NAX::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -0,0 +1,69 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
namespace FileSys {
struct NAXHeader {
std::array<u8, 0x20> hmac;
u64_le magic;
std::array<Core::Crypto::Key128, 2> key_area;
u64_le file_size;
INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(NAXHeader) == 0x80, "NAXHeader has incorrect size.");
enum class NAXContentType : u8 {
Save = 0,
NCA = 1,
};
class NAX : public ReadOnlyVfsDirectory {
public:
explicit NAX(VirtualFile file);
explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id);
Loader::ResultStatus GetStatus() const;
VirtualFile GetDecrypted() const;
std::shared_ptr<NCA> AsNCA() const;
NAXContentType GetContentType() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
Loader::ResultStatus Parse(std::string_view path);
std::unique_ptr<NAXHeader> header;
VirtualFile file;
Loader::ResultStatus status;
NAXContentType type;
VirtualFile dec_file;
Core::Crypto::KeyManager keys;
};
} // namespace FileSys

View File

@@ -12,6 +12,7 @@
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
@@ -135,7 +136,9 @@ public:
if (context->Session()->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
auto sessions = Kernel::ServerSession::CreateSessionPair(iface->GetServiceName());
auto& kernel = Core::System::GetInstance().Kernel();
auto sessions =
Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName());
auto server = std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions);
auto client = std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions);
iface->ClientConnected(server);

View File

@@ -8,9 +8,11 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
#include "core/memory.h"

View File

@@ -14,7 +14,7 @@
namespace Kernel {
ClientPort::ClientPort() = default;
ClientPort::ClientPort(KernelCore& kernel) : Object{kernel} {}
ClientPort::~ClientPort() = default;
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
@@ -27,7 +27,7 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
active_sessions++;
// Create a new session pair, let the created sessions inherit the parent port's HLE handler.
auto sessions = ServerSession::CreateSessionPair(server_port->GetName(), this);
auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
if (server_port->hle_handler)
server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));

View File

@@ -11,8 +11,9 @@
namespace Kernel {
class ServerPort;
class ClientSession;
class KernelCore;
class ServerPort;
class ClientPort final : public Object {
public:
@@ -44,7 +45,7 @@ public:
void ConnectionClosed();
private:
ClientPort();
explicit ClientPort(KernelCore& kernel);
~ClientPort() override;
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.

View File

@@ -11,7 +11,7 @@
namespace Kernel {
ClientSession::ClientSession() = default;
ClientSession::ClientSession(KernelCore& kernel) : Object{kernel} {}
ClientSession::~ClientSession() {
// This destructor will be called automatically when the last ClientSession handle is closed by
// the emulated application.

View File

@@ -12,8 +12,9 @@
namespace Kernel {
class ServerSession;
class KernelCore;
class Session;
class ServerSession;
class Thread;
class ClientSession final : public Object {
@@ -41,7 +42,7 @@ public:
std::shared_ptr<Session> parent;
private:
ClientSession();
explicit ClientSession(KernelCore& kernel);
~ClientSession() override;
};

View File

@@ -11,17 +11,16 @@ namespace Kernel {
namespace ErrCodes {
enum {
// TODO(Subv): Remove these 3DS OS error codes.
OutOfHandles = 19,
SessionClosedByRemote = 26,
PortNameTooLong = 30,
NoPendingSessions = 35,
WrongPermission = 46,
InvalidBufferDescriptor = 48,
MaxConnectionsReached = 52,
// Confirmed Switch OS error codes
MaxConnectionsReached = 7,
InvalidAddress = 102,
HandleTableFull = 105,
InvalidMemoryState = 106,
InvalidMemoryPermissions = 108,
InvalidProcessorId = 113,
InvalidHandle = 114,
InvalidCombination = 116,
@@ -30,6 +29,7 @@ enum {
TooLarge = 119,
InvalidEnumValue = 120,
InvalidState = 125,
ResourceLimitExceeded = 132,
};
}
@@ -37,18 +37,21 @@ enum {
// double check that the code matches before re-using the constant.
// TODO(bunnei): Replace these with correct errors for Switch OS
constexpr ResultCode ERR_OUT_OF_HANDLES(-1);
constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(-1);
constexpr ResultCode ERR_WRONG_PERMISSION(-1);
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1);
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
ErrCodes::MaxConnectionsReached);
constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
constexpr ResultCode ERR_INVALID_COMBINATION(-1);
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(-1);
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
ErrCodes::InvalidCombination);
constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
ErrCodes::InvalidMemoryPermissions);
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
constexpr ResultCode ERR_INVALID_POINTER(-1);

View File

@@ -10,11 +10,11 @@
namespace Kernel {
Event::Event() {}
Event::~Event() {}
Event::Event(KernelCore& kernel) : WaitObject{kernel} {}
Event::~Event() = default;
SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) {
SharedPtr<Event> evt(new Event);
SharedPtr<Event> Event::Create(KernelCore& kernel, ResetType reset_type, std::string name) {
SharedPtr<Event> evt(new Event(kernel));
evt->signaled = false;
evt->reset_type = reset_type;

View File

@@ -10,14 +10,18 @@
namespace Kernel {
class KernelCore;
class Event final : public WaitObject {
public:
/**
* Creates an event
* @param kernel The kernel instance to create this event under.
* @param reset_type ResetType describing how to create event
* @param name Optional name of event
*/
static SharedPtr<Event> Create(ResetType reset_type, std::string name = "Unknown");
static SharedPtr<Event> Create(KernelCore& kernel, ResetType reset_type,
std::string name = "Unknown");
std::string GetTypeName() const override {
return "Event";
@@ -44,7 +48,7 @@ public:
void Clear();
private:
Event();
explicit Event(KernelCore& kernel);
~Event() override;
ResetType reset_type; ///< Current ResetType

View File

@@ -13,8 +13,6 @@
namespace Kernel {
HandleTable g_handle_table;
HandleTable::HandleTable() {
next_generation = 1;
Clear();
@@ -26,7 +24,7 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
u16 slot = next_free_slot;
if (slot >= generations.size()) {
LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
return ERR_OUT_OF_HANDLES;
return ERR_HANDLE_TABLE_FULL;
}
next_free_slot = generations[slot];

View File

@@ -47,7 +47,7 @@ public:
/**
* Allocates a handle for the given object.
* @return The created Handle or one of the following errors:
* - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
* - `ERR_HANDLE_TABLE_FULL`: the maximum number of handles has been exceeded.
*/
ResultVal<Handle> Create(SharedPtr<Object> obj);
@@ -121,6 +121,4 @@ private:
u16 next_free_slot;
};
extern HandleTable g_handle_table;
} // namespace Kernel

View File

@@ -13,10 +13,12 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
@@ -51,7 +53,9 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
if (!event) {
// Create event if not provided
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
auto& kernel = Core::System::GetInstance().Kernel();
event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
}
event->Clear();
@@ -90,12 +94,14 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
rp.Skip(2, false);
}
if (incoming) {
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
// Populate the object lists with the data in the IPC request.
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
copy_objects.push_back(Kernel::g_handle_table.GetGeneric(rp.Pop<Handle>()));
copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>()));
}
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_move; ++handle) {
move_objects.push_back(Kernel::g_handle_table.GetGeneric(rp.Pop<Handle>()));
move_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>()));
}
} else {
// For responses we just ignore the handles, they're empty and will be populated when
@@ -230,17 +236,19 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
// We don't make a distinction between copy and move handles when translating since HLE
// services don't deal with handles directly. However, the guest applications might check
// for specific values in each of these descriptors.
for (auto& object : copy_objects) {
ASSERT(object != nullptr);
dst_cmdbuf[current_offset++] = Kernel::g_handle_table.Create(object).Unwrap();
dst_cmdbuf[current_offset++] = handle_table.Create(object).Unwrap();
}
for (auto& object : move_objects) {
ASSERT(object != nullptr);
dst_cmdbuf[current_offset++] = Kernel::g_handle_table.Create(object).Unwrap();
dst_cmdbuf[current_offset++] = handle_table.Create(object).Unwrap();
}
}

View File

@@ -2,38 +2,315 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <atomic>
#include <memory>
#include <mutex>
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
namespace Kernel {
std::atomic<u32> Object::next_object_id{0};
/**
* Callback that will wake up the thread it was scheduled for
* @param thread_handle The handle of the thread that's been awoken
* @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
*/
static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
const auto proper_handle = static_cast<Handle>(thread_handle);
auto& system = Core::System::GetInstance();
/// Initialize the kernel
void Init() {
Kernel::ResourceLimitsInit();
Kernel::ThreadingInit();
Kernel::TimersInit();
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
Object::next_object_id = 0;
// TODO(Subv): Start the process ids from 10 for now, as lower PIDs are
// reserved for low-level services
Process::next_process_id = 10;
SharedPtr<Thread> thread =
system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle);
if (thread == nullptr) {
LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
return;
}
bool resume = true;
if (thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll ||
thread->status == ThreadStatus::WaitHLEEvent) {
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects) {
object->RemoveWaitingThread(thread.get());
}
thread->wait_objects.clear();
// Invoke the wakeup callback before clearing the wait objects
if (thread->wakeup_callback) {
resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
}
}
if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
thread->wait_handle) {
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
auto lock_owner = thread->lock_owner;
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
// and don't have a lock owner unless SignalProcessWideKey was called first and the thread
// wasn't awakened due to the mutex already being acquired.
if (lock_owner) {
lock_owner->RemoveMutexWaiter(thread);
}
}
if (thread->arb_wait_address != 0) {
ASSERT(thread->status == ThreadStatus::WaitArb);
thread->arb_wait_address = 0;
}
if (resume) {
thread->ResumeFromWait();
}
}
/// Shutdown the kernel
void Shutdown() {
// Free all kernel objects
g_handle_table.Clear();
/// The timer callback event, called when a timer is fired
static void TimerCallback(u64 timer_handle, int cycles_late) {
const auto proper_handle = static_cast<Handle>(timer_handle);
auto& system = Core::System::GetInstance();
SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
Kernel::ThreadingShutdown();
if (timer == nullptr) {
LOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle);
return;
}
Kernel::TimersShutdown();
Kernel::ResourceLimitsShutdown();
timer->Signal(cycles_late);
}
struct KernelCore::Impl {
void Initialize(KernelCore& kernel) {
Shutdown();
InitializeResourceLimits(kernel);
InitializeThreads();
InitializeTimers();
}
void Shutdown() {
next_object_id = 0;
next_process_id = 10;
next_thread_id = 1;
process_list.clear();
handle_table.Clear();
resource_limits.fill(nullptr);
thread_wakeup_callback_handle_table.Clear();
thread_wakeup_event_type = nullptr;
timer_callback_handle_table.Clear();
timer_callback_event_type = nullptr;
named_ports.clear();
}
void InitializeResourceLimits(KernelCore& kernel) {
// Create the four resource limits that the system uses
// Create the APPLICATION resource limit
SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications");
resource_limit->max_priority = 0x18;
resource_limit->max_commit = 0x4000000;
resource_limit->max_threads = 0x20;
resource_limit->max_events = 0x20;
resource_limit->max_mutexes = 0x20;
resource_limit->max_semaphores = 0x8;
resource_limit->max_timers = 0x8;
resource_limit->max_shared_mems = 0x10;
resource_limit->max_address_arbiters = 0x2;
resource_limit->max_cpu_time = 0x1E;
resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
// Create the SYS_APPLET resource limit
resource_limit = ResourceLimit::Create(kernel, "System Applets");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x5E00000;
resource_limit->max_threads = 0x1D;
resource_limit->max_events = 0xB;
resource_limit->max_mutexes = 0x8;
resource_limit->max_semaphores = 0x4;
resource_limit->max_timers = 0x4;
resource_limit->max_shared_mems = 0x8;
resource_limit->max_address_arbiters = 0x3;
resource_limit->max_cpu_time = 0x2710;
resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
// Create the LIB_APPLET resource limit
resource_limit = ResourceLimit::Create(kernel, "Library Applets");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x600000;
resource_limit->max_threads = 0xE;
resource_limit->max_events = 0x8;
resource_limit->max_mutexes = 0x8;
resource_limit->max_semaphores = 0x4;
resource_limit->max_timers = 0x4;
resource_limit->max_shared_mems = 0x8;
resource_limit->max_address_arbiters = 0x1;
resource_limit->max_cpu_time = 0x2710;
resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
// Create the OTHER resource limit
resource_limit = ResourceLimit::Create(kernel, "Others");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x2180000;
resource_limit->max_threads = 0xE1;
resource_limit->max_events = 0x108;
resource_limit->max_mutexes = 0x25;
resource_limit->max_semaphores = 0x43;
resource_limit->max_timers = 0x2C;
resource_limit->max_shared_mems = 0x1F;
resource_limit->max_address_arbiters = 0x2D;
resource_limit->max_cpu_time = 0x3E8;
resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
}
void InitializeThreads() {
thread_wakeup_event_type =
CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
}
void InitializeTimers() {
timer_callback_handle_table.Clear();
timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
}
std::atomic<u32> next_object_id{0};
// TODO(Subv): Start the process ids from 10 for now, as lower PIDs are
// reserved for low-level services
std::atomic<u32> next_process_id{10};
std::atomic<u32> next_thread_id{1};
// Lists all processes that exist in the current session.
std::vector<SharedPtr<Process>> process_list;
Kernel::HandleTable handle_table;
std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
/// The event type of the generic timer callback event
CoreTiming::EventType* timer_callback_event_type = nullptr;
// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future,
// allowing us to simply use a pool index or similar.
Kernel::HandleTable timer_callback_handle_table;
CoreTiming::EventType* thread_wakeup_event_type = nullptr;
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
// allowing us to simply use a pool index or similar.
Kernel::HandleTable thread_wakeup_callback_handle_table;
/// Map of named ports managed by the kernel, which can be retrieved using
/// the ConnectToPort SVC.
NamedPortTable named_ports;
};
KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {}
KernelCore::~KernelCore() {
Shutdown();
}
void KernelCore::Initialize() {
impl->Initialize(*this);
}
void KernelCore::Shutdown() {
impl->Shutdown();
}
Kernel::HandleTable& KernelCore::HandleTable() {
return impl->handle_table;
}
const Kernel::HandleTable& KernelCore::HandleTable() const {
return impl->handle_table;
}
SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
ResourceLimitCategory category) const {
return impl->resource_limits.at(static_cast<std::size_t>(category));
}
SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
return impl->thread_wakeup_callback_handle_table.Get<Thread>(handle);
}
SharedPtr<Timer> KernelCore::RetrieveTimerFromCallbackHandleTable(Handle handle) const {
return impl->timer_callback_handle_table.Get<Timer>(handle);
}
void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
impl->process_list.push_back(std::move(process));
}
void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
impl->named_ports.emplace(std::move(name), std::move(port));
}
KernelCore::NamedPortTable::iterator KernelCore::FindNamedPort(const std::string& name) {
return impl->named_ports.find(name);
}
KernelCore::NamedPortTable::const_iterator KernelCore::FindNamedPort(
const std::string& name) const {
return impl->named_ports.find(name);
}
bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const {
return port != impl->named_ports.cend();
}
u32 KernelCore::CreateNewObjectID() {
return impl->next_object_id++;
}
u32 KernelCore::CreateNewThreadID() {
return impl->next_thread_id++;
}
u32 KernelCore::CreateNewProcessID() {
return impl->next_process_id++;
}
ResultVal<Handle> KernelCore::CreateTimerCallbackHandle(const SharedPtr<Timer>& timer) {
return impl->timer_callback_handle_table.Create(timer);
}
CoreTiming::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
return impl->thread_wakeup_event_type;
}
CoreTiming::EventType* KernelCore::TimerCallbackEventType() const {
return impl->timer_callback_event_type;
}
Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() {
return impl->thread_wakeup_callback_handle_table;
}
const Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() const {
return impl->thread_wakeup_callback_handle_table;
}
} // namespace Kernel

View File

@@ -4,14 +4,111 @@
#pragma once
#include "common/common_types.h"
#include <string>
#include <unordered_map>
#include "core/hle/kernel/object.h"
template <typename T>
class ResultVal;
namespace CoreTiming {
struct EventType;
}
namespace Kernel {
/// Initialize the kernel with the specified system mode.
void Init();
class ClientPort;
class HandleTable;
class Process;
class ResourceLimit;
class Thread;
class Timer;
/// Shutdown the kernel
void Shutdown();
enum class ResourceLimitCategory : u8;
/// Represents a single instance of the kernel.
class KernelCore {
private:
using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>;
public:
KernelCore();
~KernelCore();
KernelCore(const KernelCore&) = delete;
KernelCore& operator=(const KernelCore&) = delete;
KernelCore(KernelCore&&) = delete;
KernelCore& operator=(KernelCore&&) = delete;
/// Resets the kernel to a clean slate for use.
void Initialize();
/// Clears all resources in use by the kernel instance.
void Shutdown();
/// Provides a reference to the handle table.
Kernel::HandleTable& HandleTable();
/// Provides a const reference to the handle table.
const Kernel::HandleTable& HandleTable() const;
/// Retrieves a shared pointer to a ResourceLimit identified by the given category.
SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
/// Retrieves a shared pointer to a Timer instance within the timer callback handle table.
SharedPtr<Timer> RetrieveTimerFromCallbackHandleTable(Handle handle) const;
/// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(SharedPtr<Process> process);
/// Adds a port to the named port table
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
/// Finds a port within the named port table with the given name.
NamedPortTable::iterator FindNamedPort(const std::string& name);
/// Finds a port within the named port table with the given name.
NamedPortTable::const_iterator FindNamedPort(const std::string& name) const;
/// Determines whether or not the given port is a valid named port.
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
private:
friend class Object;
friend class Process;
friend class Thread;
friend class Timer;
/// Creates a new object ID, incrementing the internal object ID counter.
u32 CreateNewObjectID();
/// Creates a new process ID, incrementing the internal process ID counter;
u32 CreateNewProcessID();
/// Creates a new thread ID, incrementing the internal thread ID counter.
u32 CreateNewThreadID();
/// Creates a timer callback handle for the given timer.
ResultVal<Handle> CreateTimerCallbackHandle(const SharedPtr<Timer>& timer);
/// Retrieves the event type used for thread wakeup callbacks.
CoreTiming::EventType* ThreadWakeupCallbackEventType() const;
/// Retrieves the event type used for timer callbacks.
CoreTiming::EventType* TimerCallbackEventType() const;
/// Provides a reference to the thread wakeup callback handle table.
Kernel::HandleTable& ThreadWakeupCallbackHandleTable();
/// Provides a const reference to the thread wakeup callback handle table.
const Kernel::HandleTable& ThreadWakeupCallbackHandleTable() const;
struct Impl;
std::unique_ptr<Impl> impl;
};
} // namespace Kernel

View File

@@ -58,15 +58,15 @@ static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_t
}
}
ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress);
}
SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle);
SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle);
SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
// TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
// thread.

View File

@@ -11,6 +11,7 @@ union ResultCode;
namespace Kernel {
class HandleTable;
class Thread;
class Mutex final {
@@ -21,8 +22,8 @@ public:
static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
/// Attempts to acquire a mutex at the specified address.
static ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle);
static ResultCode TryAcquire(HandleTable& handle_table, VAddr address,
Handle holding_thread_handle, Handle requesting_thread_handle);
/// Releases the mutex at the specified address.
static ResultCode Release(VAddr address);

View File

@@ -3,10 +3,12 @@
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
namespace Kernel {
Object::Object(KernelCore& kernel) : kernel{kernel}, object_id{kernel.CreateNewObjectID()} {}
Object::~Object() = default;
bool Object::IsWaitable() const {

View File

@@ -14,6 +14,8 @@
namespace Kernel {
class KernelCore;
using Handle = u32;
enum class HandleType : u32 {
@@ -40,6 +42,7 @@ enum class ResetType {
class Object : NonCopyable {
public:
explicit Object(KernelCore& kernel);
virtual ~Object();
/// Returns a unique identifier for the object. For debugging purposes only.
@@ -61,15 +64,16 @@ public:
*/
bool IsWaitable() const;
public:
static std::atomic<u32> next_object_id;
protected:
/// The kernel instance this object was created under.
KernelCore& kernel;
private:
friend void intrusive_ptr_add_ref(Object*);
friend void intrusive_ptr_release(Object*);
std::atomic<u32> ref_count{0};
std::atomic<u32> object_id{next_object_id++};
std::atomic<u32> object_id{0};
};
// Special functions used by boost::instrusive_ptr to do automatic ref-counting

View File

@@ -8,6 +8,7 @@
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
@@ -16,30 +17,26 @@
namespace Kernel {
// Lists all processes that exist in the current session.
static std::vector<SharedPtr<Process>> process_list;
SharedPtr<CodeSet> CodeSet::Create(std::string name) {
SharedPtr<CodeSet> codeset(new CodeSet);
SharedPtr<CodeSet> CodeSet::Create(KernelCore& kernel, std::string name) {
SharedPtr<CodeSet> codeset(new CodeSet(kernel));
codeset->name = std::move(name);
return codeset;
}
CodeSet::CodeSet() {}
CodeSet::~CodeSet() {}
CodeSet::CodeSet(KernelCore& kernel) : Object{kernel} {}
CodeSet::~CodeSet() = default;
u32 Process::next_process_id;
SharedPtr<Process> Process::Create(std::string&& name) {
SharedPtr<Process> process(new Process);
SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
SharedPtr<Process> process(new Process(kernel));
process->name = std::move(name);
process->flags.raw = 0;
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = kernel.CreateNewProcessID();
process_list.push_back(process);
kernel.AppendNewProcess(process);
return process;
}
@@ -128,7 +125,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
vm_manager.LogLayout();
status = ProcessStatus::Running;
Kernel::SetupMainThread(entry_point, main_thread_priority, this);
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, this);
}
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
@@ -231,22 +228,7 @@ ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
return vm_manager.UnmapRange(dst_addr, size);
}
Kernel::Process::Process() {}
Kernel::Process::Process(KernelCore& kernel) : Object{kernel} {}
Kernel::Process::~Process() {}
void ClearProcessList() {
process_list.clear();
}
SharedPtr<Process> GetProcessById(u32 process_id) {
auto itr = std::find_if(
process_list.begin(), process_list.end(),
[&](const SharedPtr<Process>& process) { return process->process_id == process_id; });
if (itr == process_list.end())
return nullptr;
return *itr;
}
} // namespace Kernel

View File

@@ -19,6 +19,8 @@
namespace Kernel {
class KernelCore;
struct AddressMapping {
// Address and size must be page-aligned
VAddr address;
@@ -62,7 +64,7 @@ struct CodeSet final : public Object {
u32 size = 0;
};
static SharedPtr<CodeSet> Create(std::string name);
static SharedPtr<CodeSet> Create(KernelCore& kernel, std::string name);
std::string GetTypeName() const override {
return "CodeSet";
@@ -109,13 +111,13 @@ struct CodeSet final : public Object {
std::string name;
private:
CodeSet();
explicit CodeSet(KernelCore& kernel);
~CodeSet() override;
};
class Process final : public Object {
public:
static SharedPtr<Process> Create(std::string&& name);
static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
std::string GetTypeName() const override {
return "Process";
@@ -129,8 +131,6 @@ public:
return HANDLE_TYPE;
}
static u32 next_process_id;
/// Title ID corresponding to the process
u64 program_id;
@@ -157,8 +157,8 @@ public:
/// Current status of the process
ProcessStatus status;
/// The id of this process
u32 process_id = next_process_id++;
/// The ID of this process
u32 process_id = 0;
/**
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
@@ -206,13 +206,8 @@ public:
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
private:
Process();
explicit Process(KernelCore& kernel);
~Process() override;
};
void ClearProcessList();
/// Retrieves a process from the current list of processes.
SharedPtr<Process> GetProcessById(u32 process_id);
} // namespace Kernel

View File

@@ -9,31 +9,16 @@
namespace Kernel {
static SharedPtr<ResourceLimit> resource_limits[4];
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
ResourceLimit::~ResourceLimit() = default;
ResourceLimit::ResourceLimit() {}
ResourceLimit::~ResourceLimit() {}
SharedPtr<ResourceLimit> ResourceLimit::Create(std::string name) {
SharedPtr<ResourceLimit> resource_limit(new ResourceLimit);
SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string name) {
SharedPtr<ResourceLimit> resource_limit(new ResourceLimit(kernel));
resource_limit->name = std::move(name);
return resource_limit;
}
SharedPtr<ResourceLimit> ResourceLimit::GetForCategory(ResourceLimitCategory category) {
switch (category) {
case ResourceLimitCategory::APPLICATION:
case ResourceLimitCategory::SYS_APPLET:
case ResourceLimitCategory::LIB_APPLET:
case ResourceLimitCategory::OTHER:
return resource_limits[static_cast<u8>(category)];
default:
LOG_CRITICAL(Kernel, "Unknown resource limit category");
UNREACHABLE();
}
}
s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
switch (resource) {
case ResourceType::Commit:
@@ -89,66 +74,4 @@ u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
return 0;
}
}
void ResourceLimitsInit() {
// Create the four resource limits that the system uses
// Create the APPLICATION resource limit
SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create("Applications");
resource_limit->max_priority = 0x18;
resource_limit->max_commit = 0x4000000;
resource_limit->max_threads = 0x20;
resource_limit->max_events = 0x20;
resource_limit->max_mutexes = 0x20;
resource_limit->max_semaphores = 0x8;
resource_limit->max_timers = 0x8;
resource_limit->max_shared_mems = 0x10;
resource_limit->max_address_arbiters = 0x2;
resource_limit->max_cpu_time = 0x1E;
resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
// Create the SYS_APPLET resource limit
resource_limit = ResourceLimit::Create("System Applets");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x5E00000;
resource_limit->max_threads = 0x1D;
resource_limit->max_events = 0xB;
resource_limit->max_mutexes = 0x8;
resource_limit->max_semaphores = 0x4;
resource_limit->max_timers = 0x4;
resource_limit->max_shared_mems = 0x8;
resource_limit->max_address_arbiters = 0x3;
resource_limit->max_cpu_time = 0x2710;
resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
// Create the LIB_APPLET resource limit
resource_limit = ResourceLimit::Create("Library Applets");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x600000;
resource_limit->max_threads = 0xE;
resource_limit->max_events = 0x8;
resource_limit->max_mutexes = 0x8;
resource_limit->max_semaphores = 0x4;
resource_limit->max_timers = 0x4;
resource_limit->max_shared_mems = 0x8;
resource_limit->max_address_arbiters = 0x1;
resource_limit->max_cpu_time = 0x2710;
resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
// Create the OTHER resource limit
resource_limit = ResourceLimit::Create("Others");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x2180000;
resource_limit->max_threads = 0xE1;
resource_limit->max_events = 0x108;
resource_limit->max_mutexes = 0x25;
resource_limit->max_semaphores = 0x43;
resource_limit->max_timers = 0x2C;
resource_limit->max_shared_mems = 0x1F;
resource_limit->max_address_arbiters = 0x2D;
resource_limit->max_cpu_time = 0x3E8;
resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
}
void ResourceLimitsShutdown() {}
} // namespace Kernel

View File

@@ -9,6 +9,8 @@
namespace Kernel {
class KernelCore;
enum class ResourceLimitCategory : u8 {
APPLICATION = 0,
SYS_APPLET = 1,
@@ -34,14 +36,7 @@ public:
/**
* Creates a resource limit object.
*/
static SharedPtr<ResourceLimit> Create(std::string name = "Unknown");
/**
* Retrieves the resource limit associated with the specified resource limit category.
* @param category The resource limit category
* @returns The resource limit associated with the category
*/
static SharedPtr<ResourceLimit> GetForCategory(ResourceLimitCategory category);
static SharedPtr<ResourceLimit> Create(KernelCore& kernel, std::string name = "Unknown");
std::string GetTypeName() const override {
return "ResourceLimit";
@@ -113,14 +108,8 @@ public:
s32 current_cpu_time = 0;
private:
ResourceLimit();
explicit ResourceLimit(KernelCore& kernel);
~ResourceLimit() override;
};
/// Initializes the resource limits
void ResourceLimitsInit();
// Destroys the resource limits
void ResourceLimitsShutdown();
} // namespace Kernel

View File

@@ -17,7 +17,7 @@ namespace Kernel {
std::mutex Scheduler::scheduler_mutex;
Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
Scheduler::Scheduler(Core::ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
Scheduler::~Scheduler() {
for (auto& thread : thread_list) {

View File

@@ -11,13 +11,15 @@
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
namespace Core {
class ARM_Interface;
}
namespace Kernel {
class Scheduler final {
public:
explicit Scheduler(ARM_Interface* cpu_core);
explicit Scheduler(Core::ARM_Interface* cpu_core);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
@@ -70,7 +72,7 @@ private:
SharedPtr<Thread> current_thread = nullptr;
ARM_Interface* cpu_core;
Core::ARM_Interface* cpu_core;
static std::mutex scheduler_mutex;
};

View File

@@ -13,8 +13,8 @@
namespace Kernel {
ServerPort::ServerPort() {}
ServerPort::~ServerPort() {}
ServerPort::ServerPort(KernelCore& kernel) : WaitObject{kernel} {}
ServerPort::~ServerPort() = default;
ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
if (pending_sessions.empty()) {
@@ -36,10 +36,10 @@ void ServerPort::Acquire(Thread* thread) {
}
std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair(
u32 max_sessions, std::string name) {
KernelCore& kernel, u32 max_sessions, std::string name) {
SharedPtr<ServerPort> server_port(new ServerPort);
SharedPtr<ClientPort> client_port(new ClientPort);
SharedPtr<ServerPort> server_port(new ServerPort(kernel));
SharedPtr<ClientPort> client_port(new ClientPort(kernel));
server_port->name = name + "_Server";
client_port->name = name + "_Client";

View File

@@ -15,6 +15,7 @@
namespace Kernel {
class ClientPort;
class KernelCore;
class ServerSession;
class SessionRequestHandler;
@@ -23,12 +24,13 @@ public:
/**
* Creates a pair of ServerPort and an associated ClientPort.
*
* @param kernel The kernel instance to create the port pair under.
* @param max_sessions Maximum number of sessions to the port
* @param name Optional name of the ports
* @return The created port tuple
*/
static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair(
u32 max_sessions, std::string name = "UnknownPort");
KernelCore& kernel, u32 max_sessions, std::string name = "UnknownPort");
std::string GetTypeName() const override {
return "ServerPort";
@@ -69,7 +71,7 @@ public:
void Acquire(Thread* thread) override;
private:
ServerPort();
explicit ServerPort(KernelCore& kernel);
~ServerPort() override;
};

View File

@@ -13,6 +13,7 @@
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
@@ -20,7 +21,7 @@
namespace Kernel {
ServerSession::ServerSession() = default;
ServerSession::ServerSession(KernelCore& kernel) : WaitObject{kernel} {}
ServerSession::~ServerSession() {
// This destructor will be called automatically when the last ServerSession handle is closed by
// the emulated application.
@@ -35,8 +36,8 @@ ServerSession::~ServerSession() {
parent->server = nullptr;
}
ResultVal<SharedPtr<ServerSession>> ServerSession::Create(std::string name) {
SharedPtr<ServerSession> server_session(new ServerSession);
ResultVal<SharedPtr<ServerSession>> ServerSession::Create(KernelCore& kernel, std::string name) {
SharedPtr<ServerSession> server_session(new ServerSession(kernel));
server_session->name = std::move(name);
server_session->parent = nullptr;
@@ -104,11 +105,10 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// The ServerSession received a sync request, this means that there's new data available
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
// similar.
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(),
Kernel::g_handle_table);
kernel.HandleTable());
ResultCode result = RESULT_SUCCESS;
// If the session has been converted to a domain, handle the domain request
@@ -160,10 +160,11 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
return result;
}
ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name,
ServerSession::SessionPair ServerSession::CreateSessionPair(KernelCore& kernel,
const std::string& name,
SharedPtr<ClientPort> port) {
auto server_session = ServerSession::Create(name + "_Server").Unwrap();
SharedPtr<ClientSession> client_session(new ClientSession);
auto server_session = ServerSession::Create(kernel, name + "_Server").Unwrap();
SharedPtr<ClientSession> client_session(new ClientSession(kernel));
client_session->name = name + "_Client";
std::shared_ptr<Session> parent(new Session);

View File

@@ -15,13 +15,14 @@
namespace Kernel {
class ClientSession;
class ClientPort;
class ClientSession;
class HLERequestContext;
class KernelCore;
class ServerSession;
class Session;
class SessionRequestHandler;
class Thread;
class HLERequestContext;
/**
* Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
@@ -50,11 +51,12 @@ public:
/**
* Creates a pair of ServerSession and an associated ClientSession.
* @param kernel The kernal instance to create the session pair under.
* @param name Optional name of the ports.
* @param client_port Optional The ClientPort that spawned this session.
* @return The created session tuple
*/
static SessionPair CreateSessionPair(const std::string& name = "Unknown",
static SessionPair CreateSessionPair(KernelCore& kernel, const std::string& name = "Unknown",
SharedPtr<ClientPort> client_port = nullptr);
/**
@@ -111,16 +113,18 @@ public:
}
private:
ServerSession();
explicit ServerSession(KernelCore& kernel);
~ServerSession() override;
/**
* Creates a server session. The server session can have an optional HLE handler,
* which will be invoked to handle the IPC requests that this session receives.
* @param kernel The kernel instance to create this server session under.
* @param name Optional name of the server session.
* @return The created server session
*/
static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown");
static ResultVal<SharedPtr<ServerSession>> Create(KernelCore& kernel,
std::string name = "Unknown");
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
/// object handle.

View File

@@ -13,14 +13,14 @@
namespace Kernel {
SharedMemory::SharedMemory() {}
SharedMemory::~SharedMemory() {}
SharedMemory::SharedMemory(KernelCore& kernel) : Object{kernel} {}
SharedMemory::~SharedMemory() = default;
SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u64 size,
MemoryPermission permissions,
SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Process> owner_process,
u64 size, MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address,
MemoryRegion region, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
shared_memory->owner_process = std::move(owner_process);
shared_memory->name = std::move(name);
@@ -59,12 +59,10 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
return shared_memory;
}
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block,
u32 offset, u32 size,
MemoryPermission permissions,
MemoryPermission other_permissions,
std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
shared_memory->owner_process = nullptr;
shared_memory->name = std::move(name);
@@ -101,7 +99,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
GetObjectId(), address, name);
return ERR_WRONG_PERMISSION;
return ERR_INVALID_MEMORY_PERMISSIONS;
}
VAddr target_address = address;

View File

@@ -15,6 +15,8 @@
namespace Kernel {
class KernelCore;
/// Permissions for mapped shared memory blocks
enum class MemoryPermission : u32 {
None = 0,
@@ -32,6 +34,7 @@ class SharedMemory final : public Object {
public:
/**
* Creates a shared memory object.
* @param kernel The kernel instance to create a shared memory instance under.
* @param owner_process Process that created this shared memory object.
* @param size Size of the memory block. Must be page-aligned.
* @param permissions Permission restrictions applied to the process which created the block.
@@ -42,14 +45,15 @@ public:
* linear heap.
* @param name Optional object name, used for debugging purposes.
*/
static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u64 size,
MemoryPermission permissions,
static SharedPtr<SharedMemory> Create(KernelCore& kernel, SharedPtr<Process> owner_process,
u64 size, MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address = 0,
MemoryRegion region = MemoryRegion::BASE,
std::string name = "Unknown");
/**
* Creates a shared memory object from a block of memory managed by an HLE applet.
* @param kernel The kernel instance to create a shared memory instance under.
* @param heap_block Heap block of the HLE applet.
* @param offset The offset into the heap block that the SharedMemory will map.
* @param size Size of the memory block. Must be page-aligned.
@@ -58,7 +62,8 @@ public:
* block.
* @param name Optional object name, used for debugging purposes.
*/
static SharedPtr<SharedMemory> CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block,
static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel,
std::shared_ptr<std::vector<u8>> heap_block,
u32 offset, u32 size,
MemoryPermission permissions,
MemoryPermission other_permissions,
@@ -125,7 +130,7 @@ public:
std::string name;
private:
SharedMemory();
explicit SharedMemory(KernelCore& kernel);
~SharedMemory() override;
};

View File

@@ -12,16 +12,20 @@
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_wrap.h"
@@ -64,19 +68,22 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
/// Connect to an OS service given the port name, returns the handle to the port to out
static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) {
if (!Memory::IsValidVirtualAddress(port_name_address))
if (!Memory::IsValidVirtualAddress(port_name_address)) {
return ERR_NOT_FOUND;
}
static constexpr std::size_t PortNameMaxLength = 11;
// Read 1 char beyond the max allowed port name to detect names that are too long.
std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
if (port_name.size() > PortNameMaxLength)
if (port_name.size() > PortNameMaxLength) {
return ERR_PORT_NAME_TOO_LONG;
}
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
auto it = Service::g_kernel_named_ports.find(port_name);
if (it == Service::g_kernel_named_ports.end()) {
auto& kernel = Core::System::GetInstance().Kernel();
auto it = kernel.FindNamedPort(port_name);
if (!kernel.IsValidNamedPort(it)) {
LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
return ERR_NOT_FOUND;
}
@@ -87,13 +94,14 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
CASCADE_RESULT(client_session, client_port->Connect());
// Return the client session
CASCADE_RESULT(*out_handle, g_handle_table.Create(client_session));
CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session));
return RESULT_SUCCESS;
}
/// Makes a blocking IPC call to an OS service.
static ResultCode SendSyncRequest(Handle handle) {
SharedPtr<ClientSession> session = g_handle_table.Get<ClientSession>(handle);
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<ClientSession> session = kernel.HandleTable().Get<ClientSession>(handle);
if (!session) {
LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
@@ -112,7 +120,8 @@ static ResultCode SendSyncRequest(Handle handle) {
static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -125,7 +134,8 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
const SharedPtr<Process> process = g_handle_table.Get<Process>(process_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
if (!process) {
return ERR_INVALID_HANDLE;
}
@@ -168,10 +178,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
using ObjectPtr = SharedPtr<WaitObject>;
std::vector<ObjectPtr> objects(handle_count);
auto& kernel = Core::System::GetInstance().Kernel();
for (u64 i = 0; i < handle_count; ++i) {
const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
const auto object = g_handle_table.Get<WaitObject>(handle);
const auto object = kernel.HandleTable().Get<WaitObject>(handle);
if (object == nullptr) {
return ERR_INVALID_HANDLE;
@@ -219,7 +230,8 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
static ResultCode CancelSynchronization(Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -239,7 +251,9 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
"requesting_current_thread_handle=0x{:08X}",
holding_thread_handle, mutex_addr, requesting_thread_handle);
return Mutex::TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle);
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
requesting_thread_handle);
}
/// Unlock a mutex
@@ -319,8 +333,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*result = Core::CurrentProcess()->is_virtual_address_memory_enabled;
break;
case GetInfoType::TitleId:
LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0");
*result = 0;
*result = Core::CurrentProcess()->program_id;
break;
case GetInfoType::PrivilegedProcessId:
LOG_WARNING(Kernel_SVC,
@@ -353,7 +366,8 @@ static ResultCode GetThreadContext(Handle handle, VAddr addr) {
/// Gets the priority for the specified thread
static ResultCode GetThreadPriority(u32* priority, Handle handle) {
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
if (!thread)
return ERR_INVALID_HANDLE;
@@ -367,7 +381,8 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
return ERR_OUT_OF_RANGE;
}
SharedPtr<Thread> thread = g_handle_table.Get<Thread>(handle);
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
if (!thread)
return ERR_INVALID_HANDLE;
@@ -396,7 +411,8 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
"called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
shared_memory_handle, addr, size, permissions);
SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle);
auto& kernel = Core::System::GetInstance().Kernel();
auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
return ERR_INVALID_HANDLE;
}
@@ -424,7 +440,8 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
shared_memory_handle, addr, size);
SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle);
auto& kernel = Core::System::GetInstance().Kernel();
auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
return shared_memory->Unmap(Core::CurrentProcess().get(), addr);
}
@@ -432,7 +449,9 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
/// Query process memory
static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
Handle process_handle, u64 addr) {
SharedPtr<Process> process = g_handle_table.Get<Process>(process_handle);
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
if (!process) {
return ERR_INVALID_HANDLE;
}
@@ -529,10 +548,11 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
break;
}
auto& kernel = Core::System::GetInstance().Kernel();
CASCADE_RESULT(SharedPtr<Thread> thread,
Thread::Create(name, entry_point, priority, arg, processor_id, stack_top,
Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
Core::CurrentProcess()));
CASCADE_RESULT(thread->guest_handle, g_handle_table.Create(thread));
CASCADE_RESULT(thread->guest_handle, kernel.HandleTable().Create(thread));
*out_handle = thread->guest_handle;
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
@@ -549,7 +569,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
static ResultCode StartThread(Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -596,7 +617,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
ASSERT(thread);
CASCADE_CODE(Mutex::Release(mutex_addr));
@@ -705,8 +727,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
mutex_val | Mutex::MutexHasWaitersFlag));
// The mutex is already owned by some other thread, make this thread wait on it.
auto& kernel = Core::System::GetInstance().Kernel();
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
auto owner = g_handle_table.Get<Thread>(owner_handle);
auto owner = kernel.HandleTable().Get<Thread>(owner_handle);
ASSERT(owner);
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->wakeup_callback = nullptr;
@@ -784,14 +807,20 @@ static u64 GetSystemTick() {
/// Close a handle
static ResultCode CloseHandle(Handle handle) {
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
return g_handle_table.Close(handle);
auto& kernel = Core::System::GetInstance().Kernel();
return kernel.HandleTable().Close(handle);
}
/// Reset an event
static ResultCode ResetSignal(Handle handle) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
auto event = g_handle_table.Get<Event>(handle);
auto& kernel = Core::System::GetInstance().Kernel();
auto event = kernel.HandleTable().Get<Event>(handle);
ASSERT(event != nullptr);
event->Clear();
return RESULT_SUCCESS;
}
@@ -807,7 +836,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -822,7 +852,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
mask, core);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -862,19 +893,23 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
u32 remote_permissions) {
LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size,
local_permissions, remote_permissions);
auto sharedMemHandle =
SharedMemory::Create(g_handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
auto& kernel = Core::System::GetInstance().Kernel();
auto& handle_table = kernel.HandleTable();
auto shared_mem_handle =
SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
static_cast<MemoryPermission>(local_permissions),
static_cast<MemoryPermission>(remote_permissions));
CASCADE_RESULT(*handle, g_handle_table.Create(sharedMemHandle));
CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
return RESULT_SUCCESS;
}
static ResultCode ClearEvent(Handle handle) {
LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
SharedPtr<Event> evt = g_handle_table.Get<Event>(handle);
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle);
if (evt == nullptr)
return ERR_INVALID_HANDLE;
evt->Clear();

View File

@@ -16,22 +16,21 @@
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {
/// Event type for the thread wake up event
static CoreTiming::EventType* ThreadWakeupEventType = nullptr;
bool Thread::ShouldWait(Thread* thread) const {
return status != ThreadStatus::Dead;
}
@@ -40,32 +39,17 @@ void Thread::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
}
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing
// us to simply use a pool index or similar.
static Kernel::HandleTable wakeup_callback_handle_table;
// The first available thread id at startup
static u32 next_thread_id;
/**
* Creates a new thread ID
* @return The new thread ID
*/
inline static u32 const NewThreadId() {
return next_thread_id++;
}
Thread::Thread() {}
Thread::~Thread() {}
Thread::Thread(KernelCore& kernel) : WaitObject{kernel} {}
Thread::~Thread() = default;
void Thread::Stop() {
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
wakeup_callback_handle_table.Close(callback_handle);
CoreTiming::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
callback_handle = 0;
// Clean up thread from ready queue
// This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
// This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
if (status == ThreadStatus::Ready) {
scheduler->UnscheduleThread(this, current_priority);
}
@@ -98,63 +82,6 @@ void ExitCurrentThread() {
Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
}
/**
* Callback that will wake up the thread it was scheduled for
* @param thread_handle The handle of the thread that's been awoken
* @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
*/
static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
const auto proper_handle = static_cast<Handle>(thread_handle);
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>(proper_handle);
if (thread == nullptr) {
LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
return;
}
bool resume = true;
if (thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll ||
thread->status == ThreadStatus::WaitHLEEvent) {
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects)
object->RemoveWaitingThread(thread.get());
thread->wait_objects.clear();
// Invoke the wakeup callback before clearing the wait objects
if (thread->wakeup_callback)
resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
}
if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
thread->wait_handle) {
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
auto lock_owner = thread->lock_owner;
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
// and don't have a lock owner unless SignalProcessWideKey was called first and the thread
// wasn't awakened due to the mutex already being acquired.
if (lock_owner) {
lock_owner->RemoveMutexWaiter(thread);
}
}
if (thread->arb_wait_address != 0) {
ASSERT(thread->status == ThreadStatus::WaitArb);
thread->arb_wait_address = 0;
}
if (resume)
thread->ResumeFromWait();
}
void Thread::WakeAfterDelay(s64 nanoseconds) {
// Don't schedule a wakeup if the thread wants to wait forever
if (nanoseconds == -1)
@@ -162,12 +89,12 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
// This function might be called from any thread so we have to be cautious and use the
// thread-safe version of ScheduleEvent.
CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds), ThreadWakeupEventType,
callback_handle);
CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds),
kernel.ThreadWakeupCallbackEventType(), callback_handle);
}
void Thread::CancelWakeupTimer() {
CoreTiming::UnscheduleEventThreadsafe(ThreadWakeupEventType, callback_handle);
CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
}
static boost::optional<s32> GetNextProcessorId(u64 mask) {
@@ -283,9 +210,9 @@ static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot(
* @param entry_point Address of entry point for execution
* @param arg User argument for thread
*/
static void ResetThreadContext(ARM_Interface::ThreadContext& context, VAddr stack_top,
static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top,
VAddr entry_point, u64 arg) {
memset(&context, 0, sizeof(ARM_Interface::ThreadContext));
memset(&context, 0, sizeof(Core::ARM_Interface::ThreadContext));
context.cpu_registers[0] = arg;
context.pc = entry_point;
@@ -294,9 +221,9 @@ static void ResetThreadContext(ARM_Interface::ThreadContext& context, VAddr stac
context.fpscr = 0;
}
ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority,
u64 arg, s32 processor_id, VAddr stack_top,
SharedPtr<Process> owner_process) {
ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id,
VAddr stack_top, SharedPtr<Process> owner_process) {
// Check if priority is in ranged. Lowest priority -> highest priority id.
if (priority > THREADPRIO_LOWEST) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
@@ -316,9 +243,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
return ResultCode(-1);
}
SharedPtr<Thread> thread(new Thread);
SharedPtr<Thread> thread(new Thread(kernel));
thread->thread_id = NewThreadId();
thread->thread_id = kernel.CreateNewThreadID();
thread->status = ThreadStatus::Dormant;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
@@ -333,7 +260,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
thread->name = std::move(name);
thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
thread->owner_process = owner_process;
thread->scheduler = Core::System::GetInstance().Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
@@ -383,19 +310,19 @@ void Thread::BoostPriority(u32 priority) {
current_priority = priority;
}
SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process) {
// Setup page table so we can write to memory
SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
// Initialize new "main" thread
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
Memory::STACK_AREA_VADDR_END, std::move(owner_process));
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
thread->guest_handle = Kernel::g_handle_table.Create(thread).Unwrap();
thread->guest_handle = kernel.HandleTable().Create(thread).Unwrap();
thread->context.cpu_registers[1] = thread->guest_handle;
@@ -528,13 +455,4 @@ Thread* GetCurrentThread() {
return Core::System::GetInstance().CurrentScheduler().GetCurrentThread();
}
void ThreadingInit() {
ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
next_thread_id = 1;
}
void ThreadingShutdown() {
Kernel::ClearProcessList();
}
} // namespace Kernel

View File

@@ -56,6 +56,7 @@ enum class ThreadWakeupReason {
namespace Kernel {
class KernelCore;
class Process;
class Scheduler;
@@ -63,6 +64,7 @@ class Thread final : public WaitObject {
public:
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param kernel The kernel instance this thread will be created under.
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
@@ -72,8 +74,9 @@ public:
* @param owner_process The parent process for the thread
* @return A shared pointer to the newly created thread
*/
static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority,
u64 arg, s32 processor_id, VAddr stack_top,
static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name,
VAddr entry_point, u32 priority, u64 arg,
s32 processor_id, VAddr stack_top,
SharedPtr<Process> owner_process);
std::string GetName() const override {
@@ -204,7 +207,7 @@ public:
return status == ThreadStatus::WaitSynchAll;
}
ARM_Interface::ThreadContext context;
Core::ARM_Interface::ThreadContext context;
u32 thread_id;
@@ -263,7 +266,7 @@ public:
u64 affinity_mask{0x1};
private:
Thread();
explicit Thread(KernelCore& kernel);
~Thread() override;
std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>();
@@ -271,12 +274,13 @@ private:
/**
* Sets up the primary application thread
* @param kernel The kernel instance to create the main thread under.
* @param entry_point The address at which the thread should start execution
* @param priority The priority to give the main thread
* @param owner_process The parent process for the main thread
* @return A shared pointer to the main thread
*/
SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process);
/**
@@ -294,14 +298,4 @@ void WaitCurrentThread_Sleep();
*/
void ExitCurrentThread();
/**
* Initialize threading
*/
void ThreadingInit();
/**
* Shutdown threading
*/
void ThreadingShutdown();
} // namespace Kernel

View File

@@ -2,36 +2,31 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
namespace Kernel {
/// The event type of the generic timer callback event
static CoreTiming::EventType* timer_callback_event_type = nullptr;
// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future, allowing
// us to simply use a pool index or similar.
static Kernel::HandleTable timer_callback_handle_table;
Timer::Timer(KernelCore& kernel) : WaitObject{kernel} {}
Timer::~Timer() = default;
Timer::Timer() {}
Timer::~Timer() {}
SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) {
SharedPtr<Timer> timer(new Timer);
SharedPtr<Timer> Timer::Create(KernelCore& kernel, ResetType reset_type, std::string name) {
SharedPtr<Timer> timer(new Timer(kernel));
timer->reset_type = reset_type;
timer->signaled = false;
timer->name = std::move(name);
timer->initial_delay = 0;
timer->interval_delay = 0;
timer->callback_handle = timer_callback_handle_table.Create(timer).Unwrap();
timer->callback_handle = kernel.CreateTimerCallbackHandle(timer).Unwrap();
return timer;
}
@@ -58,13 +53,13 @@ void Timer::Set(s64 initial, s64 interval) {
// Immediately invoke the callback
Signal(0);
} else {
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(initial), timer_callback_event_type,
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(initial), kernel.TimerCallbackEventType(),
callback_handle);
}
}
void Timer::Cancel() {
CoreTiming::UnscheduleEvent(timer_callback_event_type, callback_handle);
CoreTiming::UnscheduleEvent(kernel.TimerCallbackEventType(), callback_handle);
}
void Timer::Clear() {
@@ -89,28 +84,8 @@ void Timer::Signal(int cycles_late) {
if (interval_delay != 0) {
// Reschedule the timer with the interval delay
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(interval_delay) - cycles_late,
timer_callback_event_type, callback_handle);
kernel.TimerCallbackEventType(), callback_handle);
}
}
/// The timer callback event, called when a timer is fired
static void TimerCallback(u64 timer_handle, int cycles_late) {
SharedPtr<Timer> timer =
timer_callback_handle_table.Get<Timer>(static_cast<Handle>(timer_handle));
if (timer == nullptr) {
LOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle);
return;
}
timer->Signal(cycles_late);
}
void TimersInit() {
timer_callback_handle_table.Clear();
timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
}
void TimersShutdown() {}
} // namespace Kernel

View File

@@ -10,15 +10,19 @@
namespace Kernel {
class KernelCore;
class Timer final : public WaitObject {
public:
/**
* Creates a timer
* @param kernel The kernel instance to create the timer callback handle for.
* @param reset_type ResetType describing how to create the timer
* @param name Optional name of timer
* @return The created Timer
*/
static SharedPtr<Timer> Create(ResetType reset_type, std::string name = "Unknown");
static SharedPtr<Timer> Create(KernelCore& kernel, ResetType reset_type,
std::string name = "Unknown");
std::string GetTypeName() const override {
return "Timer";
@@ -68,7 +72,7 @@ public:
void Signal(int cycles_late);
private:
Timer();
explicit Timer(KernelCore& kernel);
~Timer() override;
ResetType reset_type; ///< The ResetType of this timer
@@ -83,9 +87,4 @@ private:
Handle callback_handle;
};
/// Initializes the required variables for timers
void TimersInit();
/// Tears down the timer variables
void TimersShutdown();
} // namespace Kernel

View File

@@ -12,6 +12,9 @@
namespace Kernel {
WaitObject::WaitObject(KernelCore& kernel) : Object{kernel} {}
WaitObject::~WaitObject() = default;
void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) {
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
if (itr == waiting_threads.end())

View File

@@ -11,11 +11,15 @@
namespace Kernel {
class KernelCore;
class Thread;
/// Class that represents a Kernel object that a thread can be waiting on
class WaitObject : public Object {
public:
explicit WaitObject(KernelCore& kernel);
~WaitObject() override;
/**
* Check if the specified thread should wait until the object is available
* @param thread The thread about which we're deciding.

View File

@@ -227,7 +227,7 @@ public:
}
}
ResultVal(ResultVal&& o) : result_code(o.result_code) {
ResultVal(ResultVal&& o) noexcept : result_code(o.result_code) {
if (!o.empty()) {
new (&object) T(std::move(o.object));
}

View File

@@ -18,6 +18,7 @@
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/set/set.h"
#include "core/settings.h"
@@ -159,8 +160,9 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
launchable_event =
Kernel::Event::Create(Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent");
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent");
}
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
@@ -309,7 +311,7 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
{5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"},
{6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"},
{7, nullptr, "GetCradleStatus"},
{8, nullptr, "GetBootMode"},
{8, &ICommonStateGetter::GetBootMode, "GetBootMode"},
{9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"},
{10, nullptr, "RequestToAcquireSleepLock"},
{11, nullptr, "ReleaseSleepLock"},
@@ -331,7 +333,17 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
};
RegisterHandlers(functions);
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "ICommonStateGetter:Event");
auto& kernel = Core::System::GetInstance().Kernel();
event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ICommonStateGetter:Event");
}
void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode
LOG_DEBUG(Service_AM, "called");
}
void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
@@ -495,7 +507,8 @@ public:
};
RegisterHandlers(functions);
state_changed_event = Kernel::Event::Create(Kernel::ResetType::OneShot,
auto& kernel = Core::System::GetInstance().Kernel();
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
"ILibraryAppletAccessor:StateChangedEvent");
}

View File

@@ -116,6 +116,7 @@ private:
void GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx);
void GetOperationMode(Kernel::HLERequestContext& ctx);
void GetPerformanceMode(Kernel::HLERequestContext& ctx);
void GetBootMode(Kernel::HLERequestContext& ctx);
Kernel::SharedPtr<Kernel::Event> event;
};

View File

@@ -47,7 +47,9 @@ public:
RegisterHandlers(functions);
// This is the event handle used to check if the audio buffer was released
buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
auto& kernel = Core::System::GetInstance().Kernel();
buffer_event =
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
"IAudioOut", [=]() { buffer_event->Signal(); });

View File

@@ -35,8 +35,9 @@ public:
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
system_event =
Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
}
@@ -121,8 +122,9 @@ public:
};
RegisterHandlers(functions);
buffer_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
auto& kernel = Core::System::GetInstance().Kernel();
buffer_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
"IAudioOutBufferReleasedEvent");
}
private:

View File

@@ -60,17 +60,20 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (path.empty()) {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
return RESULT_SUCCESS;
}
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
}
if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
@@ -254,7 +257,7 @@ ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
bis_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registred BIS");
LOG_DEBUG(Service_FS, "Registered BIS");
return RESULT_SUCCESS;
}
@@ -305,17 +308,38 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
}
std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() {
LOG_TRACE(Service_FS, "Opening System NAND Contents");
if (bis_factory == nullptr)
return nullptr;
return bis_factory->GetSystemNANDContents();
}
std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() {
LOG_TRACE(Service_FS, "Opening User NAND Contents");
if (bis_factory == nullptr)
return nullptr;
return bis_factory->GetUserNANDContents();
}
void RegisterFileSystems(const FileSys::VirtualFilesystem& vfs) {
romfs_factory = nullptr;
save_data_factory = nullptr;
sdmc_factory = nullptr;
std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() {
LOG_TRACE(Service_FS, "Opening SDMC Contents");
if (sdmc_factory == nullptr)
return nullptr;
return sdmc_factory->GetSDMCContents();
}
void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
if (overwrite) {
bis_factory = nullptr;
save_data_factory = nullptr;
sdmc_factory = nullptr;
}
auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
FileSys::Mode::ReadWrite);
@@ -324,16 +348,15 @@ void RegisterFileSystems(const FileSys::VirtualFilesystem& vfs) {
if (bis_factory == nullptr)
bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory);
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
save_data_factory = std::move(savedata);
auto sdcard = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
sdmc_factory = std::move(sdcard);
if (save_data_factory == nullptr)
save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
if (sdmc_factory == nullptr)
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
}
void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) {
RegisterFileSystems(vfs);
romfs_factory = nullptr;
CreateFactories(vfs, false);
std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
std::make_shared<FSP_PR>()->InstallAsService(service_manager);
std::make_shared<FSP_SRV>()->InstallAsService(service_manager);

View File

@@ -7,6 +7,7 @@
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace FileSys {
@@ -46,8 +47,12 @@ ResultVal<FileSys::VirtualDir> OpenSDMC();
std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents();
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
// above is called.
void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true);
/// Registers all Filesystem services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs);
// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of

View File

@@ -26,6 +26,17 @@
namespace Service::FileSystem {
enum class FileSystemType : u8 {
Invalid0 = 0,
Invalid1 = 1,
Logo = 2,
ContentControl = 3,
ContentManual = 4,
ContentMeta = 5,
ContentData = 6,
ApplicationPackage = 7,
};
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(FileSys::VirtualFile backend_)
@@ -420,7 +431,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{0, nullptr, "MountContent"},
{1, &FSP_SRV::Initialize, "Initialize"},
{2, nullptr, "OpenDataFileSystemByCurrentProcess"},
{7, nullptr, "OpenFileSystemWithPatch"},
{7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"},
{8, nullptr, "OpenFileSystemWithId"},
{9, nullptr, "OpenDataFileSystemByApplicationId"},
{11, nullptr, "OpenBisFileSystem"},
@@ -444,7 +455,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{34, nullptr, "GetCacheStorageSize"},
{51, &FSP_SRV::MountSaveData, "MountSaveData"},
{52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"},
{53, nullptr, "OpenReadOnlySaveDataFileSystem"},
{53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
{57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
{58, nullptr, "ReadSaveDataFileSystemExtraData"},
{59, nullptr, "WriteSaveDataFileSystemExtraData"},
@@ -516,6 +527,16 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto type = rp.PopRaw<FileSystemType>();
const auto title_id = rp.PopRaw<u64>();
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(ResultCode(-1));
}
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
@@ -563,6 +584,11 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
}
void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
MountSaveData(ctx);
}
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");

View File

@@ -20,9 +20,11 @@ public:
private:
void Initialize(Kernel::HLERequestContext& ctx);
void OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx);
void MountSdCard(Kernel::HLERequestContext& ctx);
void CreateSaveData(Kernel::HLERequestContext& ctx);
void MountSaveData(Kernel::HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);

View File

@@ -4,6 +4,7 @@
#include <atomic>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/frontend/emu_window.h"
@@ -17,6 +18,7 @@
#include "core/hle/service/hid/irs.h"
#include "core/hle/service/hid/xcd.h"
#include "core/hle/service/service.h"
#include "core/settings.h"
namespace Service::HID {
@@ -34,9 +36,10 @@ public:
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
shared_mem = Kernel::SharedMemory::Create(
nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::Read,
0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
kernel, nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
// Register update callbacks
pad_update_event = CoreTiming::RegisterEvent(
@@ -401,7 +404,8 @@ public:
RegisterHandlers(functions);
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "hid:EventHandle");
auto& kernel = Core::System::GetInstance().Kernel();
event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "hid:EventHandle");
}
~Hid() = default;

View File

@@ -4,8 +4,10 @@
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/service/service.h"
#include "core/settings.h"
namespace Service::HID {

View File

@@ -46,11 +46,13 @@ public:
};
RegisterHandlers(functions);
activate_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:ActivateEvent");
auto& kernel = Core::System::GetInstance().Kernel();
activate_event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:ActivateEvent");
deactivate_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
availability_change_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent");
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
"IUser:AvailabilityChangeEvent");
}
private:

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nifm/nifm.h"
@@ -54,8 +55,9 @@ public:
};
RegisterHandlers(functions);
event1 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event1");
event2 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event2");
auto& kernel = Core::System::GetInstance().Kernel();
event1 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event1");
event2 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event2");
}
private:

View File

@@ -35,6 +35,14 @@ static constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONT
std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"),
std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf")};
static constexpr std::array<const char*, 7> SHARED_FONTS_TTF{"FontStandard.ttf",
"FontChineseSimplified.ttf",
"FontExtendedChineseSimplified.ttf",
"FontChineseTraditional.ttf",
"FontKorean.ttf",
"FontNintendoExtended.ttf",
"FontNintendoExtended2.ttf"};
// The below data is specific to shared font data dumped from Switch on f/w 2.2
// Virtual address and offsets/sizes likely will vary by dump
static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
@@ -76,6 +84,17 @@ void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, s
offset += transformed_font.size() * sizeof(u32);
}
static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output,
size_t& offset) {
ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!");
const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT;
std::memcpy(output.data() + offset, &EXPECTED_RESULT, sizeof(u32)); // Magic header
const u32 ENC_SIZE = static_cast<u32>(input.size()) ^ KEY;
std::memcpy(output.data() + offset + sizeof(u32), &ENC_SIZE, sizeof(u32));
std::memcpy(output.data() + offset + (sizeof(u32) * 2), input.data(), input.size());
offset += input.size() + (sizeof(u32) * 2);
}
static u32 GetU32Swapped(const u8* data) {
u32 value;
std::memcpy(&value, data, sizeof(value));
@@ -109,10 +128,10 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
RegisterHandlers(functions);
// Attempt to load shared font data from disk
const auto nand = FileSystem::GetSystemNANDContents();
size_t offset = 0;
// Rebuild shared fonts from data ncas
if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
FileSys::ContentRecordType::Data)) {
size_t offset = 0;
shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE);
for (auto font : SHARED_FONTS) {
const auto nca =
@@ -152,18 +171,45 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
DecryptSharedFont(font_data_u32, *shared_font, offset);
SHARED_FONT_REGIONS.push_back(region);
}
} else {
const std::string filepath{FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) +
SHARED_FONT};
shared_font = std::make_shared<std::vector<u8>>(
SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size
const std::string user_path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir);
const std::string filepath{user_path + SHARED_FONT};
// Create path if not already created
if (!FileUtil::CreateFullPath(filepath)) {
LOG_ERROR(Service_NS, "Failed to create sharedfonts path \"{}\"!", filepath);
return;
}
bool using_ttf = false;
for (const char* font_ttf : SHARED_FONTS_TTF) {
if (FileUtil::Exists(user_path + font_ttf)) {
using_ttf = true;
FileUtil::IOFile file(user_path + font_ttf, "rb");
if (file.IsOpen()) {
std::vector<u8> ttf_bytes(file.GetSize());
file.ReadBytes<u8>(ttf_bytes.data(), ttf_bytes.size());
FontRegion region{
static_cast<u32>(offset + 8),
static_cast<u32>(ttf_bytes.size())}; // Font offset and size do not account
// for the header
EncryptSharedFont(ttf_bytes, *shared_font, offset);
SHARED_FONT_REGIONS.push_back(region);
} else {
LOG_WARNING(Service_NS, "Unable to load font: {}", font_ttf);
}
} else if (using_ttf) {
LOG_WARNING(Service_NS, "Unable to find font: {}", font_ttf);
}
}
if (using_ttf)
return;
FileUtil::IOFile file(filepath, "rb");
shared_font = std::make_shared<std::vector<u8>>(
SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size
if (file.IsOpen()) {
// Read shared font data
ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE);
@@ -220,8 +266,9 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
SHARED_FONT_MEM_VADDR, shared_font, 0, SHARED_FONT_MEM_SIZE, Kernel::MemoryState::Shared);
// Create shared font memory object
auto& kernel = Core::System::GetInstance().Kernel();
shared_font_mem = Kernel::SharedMemory::Create(
Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
"PL_U:shared_font_mem");

View File

@@ -7,6 +7,7 @@
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/perf_stats.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
@@ -31,7 +32,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
transform, crop_rect};
auto& instance = Core::System::GetInstance();
instance.perf_stats.EndGameFrame();
instance.GetPerfStats().EndGameFrame();
instance.Renderer().SwapBuffers(framebuffer);
}

View File

@@ -56,9 +56,9 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
auto& gpu = Core::System::GetInstance().GPU();
const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
if (params.flags & 1) {
params.offset = gpu.memory_manager->AllocateSpace(params.offset, size, 1);
params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1);
} else {
params.offset = gpu.memory_manager->AllocateSpace(size, params.align);
params.offset = gpu.MemoryManager().AllocateSpace(size, params.align);
}
std::memcpy(output.data(), &params, output.size());
@@ -88,7 +88,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
u64 size = static_cast<u64>(entry.pages) << 0x10;
ASSERT(size <= object->size);
Tegra::GPUVAddr returned = gpu.memory_manager->MapBufferEx(object->addr, offset, size);
Tegra::GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size);
ASSERT(returned == offset);
}
std::memcpy(output.data(), entries.data(), output.size());
@@ -125,9 +125,9 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
auto& gpu = Core::System::GetInstance().GPU();
if (params.flags & 1) {
params.offset = gpu.memory_manager->MapBufferEx(object->addr, params.offset, object->size);
params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size);
} else {
params.offset = gpu.memory_manager->MapBufferEx(object->addr, object->size);
params.offset = gpu.MemoryManager().MapBufferEx(object->addr, object->size);
}
// Create a new mapping entry for this operation.
@@ -161,7 +161,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
itr->second.size);
auto& gpu = system_instance.GPU();
params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size);
params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
buffer_mappings.erase(itr->second.offset);

View File

@@ -10,6 +10,7 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "video_core/memory_manager.h"
namespace Service::Nvidia::Devices {

View File

@@ -4,6 +4,7 @@
#include <cinttypes>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nvdrv/interface.h"
@@ -107,7 +108,8 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
};
RegisterHandlers(functions);
query_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "NVDRV::query_event");
auto& kernel = Core::System::GetInstance().Kernel();
query_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "NVDRV::query_event");
}
} // namespace Service::Nvidia

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