Compare commits

..

353 Commits

Author SHA1 Message Date
N00byKing
3cdf6c536d Use HTTPS for Submodule lz4 2018-03-21 19:01:54 +01:00
bunnei
4721ea4523 Merge pull request #257 from mailwl/vi-module
Service/vi: convert services to module
2018-03-21 10:48:15 -04:00
mailwl
6673ed1274 Service/vi: convert services to module 2018-03-21 13:09:40 +03:00
bunnei
0b3ab30762 Merge pull request #254 from bunnei/port-citra-renderer
Port Citra OpenGL rasterizer code
2018-03-20 21:37:43 -04:00
bunnei
8c8de2efe9 Merge pull request #256 from mailwl/fatal
Service: add fatal:u, fatal:p services
2018-03-20 11:12:39 -04:00
mailwl
dca7cfb9cf Service: add fatal:u, fatal:p services 2018-03-20 16:59:02 +03:00
bunnei
6e3222363c renderer_gl: Port boilerplate rasterizer code over from Citra. 2018-03-20 00:07:32 -04:00
bunnei
9c468e0c55 gl_shader_util: Sync latest version with Citra. 2018-03-20 00:07:31 -04:00
bunnei
d7b1ebe4a8 renderer_gl: Port over gl_shader_gen module from Citra. 2018-03-20 00:07:30 -04:00
Mat M
f4700ccabf Merge pull request #253 from Subv/rt_depth
GPU: Added registers for color and Z buffers.
2018-03-19 23:37:47 -04:00
bunnei
4bdb46e4c2 renderer_gl: Port over gl_shader_decompiler module from Citra. 2018-03-19 23:14:03 -04:00
bunnei
a3e10b1a72 renderer_gl: Port over gl_rasterizer_cache module from Citra. 2018-03-19 23:14:03 -04:00
bunnei
db0cfb8e8b gl_resource_manager: Sync latest version with Citra. 2018-03-19 23:14:02 -04:00
bunnei
0e4b9cdde4 renderer_gl: Port over gl_stream_buffer module from Citra. 2018-03-19 23:14:02 -04:00
bunnei
a1cf5020e6 externals: Update Glad to latest version used by Citra. 2018-03-19 23:14:01 -04:00
bunnei
6a0902e56d gl_state: Sync latest version with Citra. 2018-03-19 23:13:49 -04:00
Subv
7a27a11770 GPU: Added Z buffer registers to Maxwell3D's reg structure. 2018-03-19 16:55:33 -05:00
Subv
21d9519032 GPU: Added the render target (RT) registers to Maxwell3D's reg structure. 2018-03-19 16:46:29 -05:00
bunnei
a90ab1dec7 Merge pull request #252 from N00byKing/3064
Implement Pull #3064 from citra: Clean all format warnings (Yuzu-specific format warnings cleared too)
2018-03-19 16:29:03 -04:00
N00byKing
1d8b6ad13b Clang Fixes 2018-03-19 17:53:35 +01:00
N00byKing
d16e08454d oops 2018-03-19 17:43:04 +01:00
N00byKing
0e72d0d826 More Warning cleanups 2018-03-19 17:27:04 +01:00
N00byKing
ef875d6a35 Clean Warnings (?) 2018-03-19 17:07:08 +01:00
bunnei
b2d7c92cae Merge pull request #251 from Subv/tic_tsc
GPU: Added TIC and TSC registers to the Maxwell3D register structure.
2018-03-19 10:33:21 -04:00
Subv
dcae0c9a4f GPU: Added the TSC registers to the Maxwell3D register structure. 2018-03-19 00:36:25 -05:00
Subv
cff7b29bba GPU: Added the TIC registers to the Maxwell3D register structure. 2018-03-19 00:32:57 -05:00
bunnei
23a0d2d7b7 Merge pull request #193 from N00byKing/3184_2_robotic_boogaloo
Implement Pull #3184 from citra: core/arm: Improve timing accuracy before service calls in JIT (Rebased)
2018-03-18 22:35:47 -04:00
bunnei
2dc3a56e96 Merge pull request #250 from bunnei/buffer-dequeue-wait
vi: TransactParcel DequeueBuffer should wait current thread
2018-03-18 22:25:09 -04:00
bunnei
2332a44b68 Merge pull request #249 from Subv/macro_E1A
GPU: Implement macro 0xE1A BindTextureInfoBuffer in HLE.
2018-03-18 21:04:29 -04:00
bunnei
c1c92c30f9 vi: Remove DequeueBuffer and wait until next available buffer. 2018-03-18 20:56:35 -04:00
bunnei
c86af6939c hle_ipc: Add SleepClientThread to block current thread within HLE routines. 2018-03-18 20:56:34 -04:00
bunnei
2faa83ca13 hle_ipc: Use shared_ptr instead of unique_ptr to allow copies. 2018-03-18 20:56:33 -04:00
bunnei
019f1a0cf0 hle_ipc: Remove GetPointer(..) usage with WriteToOutgoingCommandBuffer. 2018-03-18 20:56:33 -04:00
bunnei
e353b9fb3d thread: Add THREADSTATUS_WAIT_HLE_EVENT, remove THREADSTATUS_WAIT_ARB. 2018-03-18 20:56:32 -04:00
Subv
03156d0c9a GPU: Implement macro 0xE1A BindTextureInfoBuffer in HLE.
This macro simply sets the current CB_ADDRESS to the texture buffer address for the input shader stage.
2018-03-18 19:03:40 -05:00
bunnei
6317a0b2ca Merge pull request #248 from Subv/cb_data
GPU: Handle writes to the CB_DATA method.
2018-03-18 19:45:40 -04:00
Subv
7b6868e908 GPU: Implement the BindStorageBuffer macro method in HLE.
This macro binds the SSBO Info Buffer as the current ConstBuffer.
This buffer is usually bound to c0 during shader execution.
Games seem to use this macro instead of directly writing the address for some reason.
2018-03-18 16:50:42 -05:00
Subv
85d820b1b4 GPU: Handle writes to the CB_DATA method.
Writing to this method will cause the written value to be stored in the currently-set ConstBuffer plus CB_POS.

This method is usually used to upload uniforms or other shader-visible data.
2018-03-18 15:23:24 -05:00
Subv
a64b936cbe GPU: Move the GPU's class constructor and destructors to a cpp file.
This should reduce recompile times when editing the Maxwell3D register structure.
2018-03-18 15:23:24 -05:00
Sebastian Valle
46f9d4b4a3 Merge pull request #246 from Subv/gpu_macro_calls
GPU: Store uploaded GPU macros and keep track of the number of method arguments.
2018-03-18 15:13:40 -05:00
Subv
aa586fa268 GPU: Store uploaded GPU macros and keep track of the number of method parameters. 2018-03-18 11:51:46 -05:00
Subv
7ac8657432 GPU: Macros are specific to the Maxwell3D engine, so handle them internally. 2018-03-18 11:51:45 -05:00
bunnei
29981fa2eb Merge pull request #245 from Subv/set_shader2
GPU: Store shader constbuffer bindings in the GPU state.
2018-03-17 21:19:39 -04:00
Subv
ccb8da1512 GPU: Renamed ShaderType to ShaderStage as that is less confusing. 2018-03-17 18:32:57 -05:00
Subv
88698c156f GPU: Store shader constbuffer bindings in the GPU state. 2018-03-17 18:32:57 -05:00
Subv
66dae22790 GPU: Corrected some register offsets and removed superfluous macro registers. 2018-03-17 18:32:56 -05:00
Subv
1d9d9c16e8 GPU: Make the SetShader macro call do the same as the real macro's code.
It'll now set the CB_SIZE, CB_ADDRESS and CB_BIND registers when it's called.

Presumably this SetShader function is binding the constant shader uniforms to buffer 1 (c1[]).
2018-03-17 18:32:55 -05:00
Subv
579000e747 GPU: Corrected the parameter documentation for the SetShader macro call.
Register 0xE24 is actually a macro that sets some shader parameters in the register structure.

Macros are uploaded to the GPU at startup and have their own ISA, we'll probably write an interpreter for this in the future.
2018-03-17 13:55:42 -05:00
bunnei
516ef4f19f Merge pull request #242 from Subv/set_shader
GPU: Handle the SetShader method call (0xE24) and store the shader config.
2018-03-17 00:34:17 -04:00
bunnei
c286921739 Merge pull request #243 from Subv/vertex_buffer
GPU: Added the vertex array registers.
2018-03-17 00:04:31 -04:00
Subv
f93d769a1c GPU: Handle the SetShader method call (0xE24) and store the shader config. 2018-03-16 22:51:06 -05:00
Subv
d2888f7e90 GPU: Added the vertex array registers. 2018-03-16 22:47:45 -05:00
bunnei
cd4e8a989c Merge pull request #241 from Subv/gpu_method_call
GPU: Process command mode 5 (IncreaseOnce) differently from other commands
2018-03-16 22:28:22 -04:00
Subv
29feece4b8 GPU: Process command mode 5 (IncreaseOnce) differently from other commands.
Accumulate all arguments before calling the desired method.

Note: Maybe we should do the same for the NonIncreasing mode?
2018-03-16 20:32:44 -05:00
bunnei
0eff775264 Merge pull request #239 from Subv/shaders
GPU: Added some shader-related registers.
2018-03-16 21:09:35 -04:00
bunnei
e453b09a61 Merge pull request #238 from bunnei/fix-buffer-check
nvflinger: Remove superfluous buffer format check.
2018-03-16 21:04:39 -04:00
Subv
bf310a41b8 GPU: Assert that we get a 0 CODE_ADDRESS register in the 3D engine.
Shader address calculation depends on this value to some extent, we do not currently know what it being 0 entails.
2018-03-16 19:24:41 -05:00
Subv
cbec739e7b GPU: Added Maxwell registers for Shader Program control. 2018-03-16 19:23:11 -05:00
bunnei
494275fd38 nvflinger: Remove superfluous buffer format check. 2018-03-16 20:11:50 -04:00
bunnei
e7ba2a4447 Merge pull request #232 from bunnei/heap-fixes
Various heap fixes for libtransistor
2018-03-16 20:06:27 -04:00
bunnei
cc6f22e0e4 process: MirrorMemory should use MemoryState::Mapped. 2018-03-16 19:24:54 -04:00
bunnei
e9a857ce82 process: Unmap previously allocated heap. 2018-03-16 18:32:25 -04:00
bunnei
403f8e79ea arm_interface: Support unmapping previously mapped memory. 2018-03-16 18:32:24 -04:00
bunnei
34a29ad051 svc: Use more correct values for GetInfo MapRegion and NewMapRegion. 2018-03-16 18:32:23 -04:00
bunnei
8581404482 kernel: Move stack region outside of application heap. 2018-03-16 18:32:23 -04:00
bunnei
69ee9edd8d memory: Add regions for map region, "new" map region, etc. 2018-03-16 18:32:22 -04:00
bunnei
3923b0f589 process: Fix stack memory state. 2018-03-16 18:32:21 -04:00
bunnei
8be7131033 MemoryState: Add additional memory states and improve naming. 2018-03-16 18:32:21 -04:00
bunnei
07ae1f972d Merge pull request #237 from mailwl/nifm-module
Service/NIFM: convert to module
2018-03-16 18:26:02 -04:00
mailwl
fbfa7ddd62 IGeneralService: fix function list 2018-03-16 16:34:12 +03:00
mailwl
9289255314 Service/NIFM: stub cancel function 2018-03-16 11:08:22 +03:00
mailwl
ec030a542f Service/NIFM: convert to module 2018-03-16 11:00:29 +03:00
bunnei
cde9386e0f Merge pull request #236 from bunnei/refactor-process-creation
core: Move process creation out of global state.
2018-03-14 19:33:27 -04:00
bunnei
7d6653268f core: Move process creation out of global state. 2018-03-14 18:42:19 -04:00
bunnei
8538e0bc3d Merge pull request #213 from Hexagon12/dynarmic-default
Make Dynarmic the default CPU core
2018-03-07 18:21:13 -05:00
bunnei
5750f6f046 Merge pull request #230 from Subv/gpu_draw
GPU: Intercept writes to the VERTEX_END_GL register.
2018-03-05 09:58:58 -05:00
Subv
5fb4c718cc GPU: Intercept writes to the VERTEX_END_GL register.
This is the register that gets written after a game calls DrawArrays().

We should collect all GPU state and draw using our graphics API here.
2018-03-04 19:14:04 -05:00
bunnei
80562aaf64 Merge pull request #229 from Subv/ensuresavedata_impl
FS: Make EnsureSaveData create the save data if it doesn't already exist.
2018-03-04 15:49:42 -05:00
Subv
84e1c0a430 FS: Use the correct error code when trying to open files that don't exist. 2018-03-04 14:34:48 -05:00
Subv
e4b7a1d160 FS: Stubbed CreateSaveData. It currently does nothing. 2018-03-04 14:31:57 -05:00
Subv
0eefe6e4d1 FS: Make EnsureSaveData create the savedata folder when called for the first time. 2018-03-04 14:30:07 -05:00
bunnei
3c3d5eeddf Merge pull request #228 from Subv/unschedule_events
CoreTiming: Unschedule the pending events when an Interface is destroyed
2018-03-04 11:52:08 -05:00
Subv
248881fa7f CoreTiming: Unschedule the pending events when an Interface is destroyed. 2018-03-04 10:34:25 -05:00
bunnei
7e7110b3b9 Merge pull request #226 from Subv/buffer_queue_event
Vi: Signal the BufferQueue's Native Handle right after ReleaseBuffer is called
2018-03-03 12:38:18 -05:00
bunnei
aef7a15b93 Merge pull request #225 from mailwl/settings
Service/Set: add more services
2018-03-03 12:25:13 -05:00
Subv
656e7aab29 Vi: Signal the BufferQueue's Native Handle right after ReleaseBuffer is called.
This prevents a thread starvation issue in Puyo Puyo Tetris.
We should hwtest this behavior and figure out where exactly this event is signaled.
2018-03-03 11:51:36 -05:00
mailwl
28669872d9 Service/Set: add more services 2018-03-03 09:03:49 +03:00
bunnei
46fc7d8502 Merge pull request #216 from Subv/savedata
Implemented the SaveData archive and MountSaveData.
2018-03-01 22:14:31 -05:00
Subv
3209cff530 SaveData: Use the current titleid when opening the savedata archive. 2018-03-01 19:03:54 -05:00
Subv
827f8ca3c7 Kernel: Store the program id in the Process class instead of the CodeSet class.
There may be many CodeSets per Process, so it's wasteful and overcomplicated to store the program id in each of them.
2018-03-01 19:03:53 -05:00
Subv
cc6e4ae6cf FS: Implement MountSaveData and some of the IFile interface. 2018-03-01 19:03:53 -05:00
Subv
d140c8ecf7 Filesystem: Added a SaveData Factory and associated Disk_FileSystem. 2018-03-01 19:03:52 -05:00
bunnei
b1709410dd Merge pull request #224 from Armada651/clear-process
thread: Clear the process list on shutdown.
2018-02-27 22:01:55 -08:00
Jules Blok
c74af07c49 thread: Clear the process list on shutdown. 2018-02-27 17:13:39 +01:00
Subv
6e38081165 ResultCode: Mark any error code that isn't 0 as an error. 2018-02-27 09:44:35 -05:00
Vishal Sharma
65f3119074 Removes the use of QKeySequence::Cancel (#186)
* Removes the use of QKeySequence::Cancel to remove issues while running make

* Corrects characters in a line for travis failure

* Corrects space in a line for travis failure
2018-02-26 21:03:02 -08:00
bunnei
926604fc14 Merge pull request #207 from mailwl/duplicatesession
IPC: add domain header to response if only it exists in request
2018-02-26 20:52:56 -08:00
bunnei
f1beb69899 Merge pull request #215 from N00byKing/umapsharedmmry
UnmapSharedMemory
2018-02-25 21:04:24 -08:00
bunnei
7e45669ccb Merge pull request #222 from shinyquagsire23/npdm-parsing
NPDM Parsing
2018-02-25 16:44:51 -08:00
shinyquagsire23
e29710818f file_sys: Style tweaks
Asdf
2018-02-25 16:34:29 -07:00
shinyquagsire23
487f8bc018 loader: Check error on NPDM load, use TID for CodeSet 2018-02-25 07:41:36 -07:00
shinyquagsire23
fd3806fd30 loader: Use NPDM information when loading NSOs 2018-02-25 07:02:47 -07:00
shinyquagsire23
2b28fd7809 file_sys: Add support for parsing NPDM files 2018-02-25 07:02:39 -07:00
N00byKing
08c6ac02cf (Hopefully) Fix MinGW Build 2018-02-25 13:40:22 +01:00
N00byKing
bc88cae0c7 Implements citra-emu/citra#3184 2018-02-25 11:44:21 +01:00
N00byKing
2b41c6e573 Add UnmapSharedMemory
C++11 requires spaces on the Identifier

Add inttypes include

clang
2018-02-25 11:38:06 +01:00
bunnei
c45173c9a6 Merge pull request #212 from mailwl/stubs
Stub some functions
2018-02-23 21:09:56 -08:00
bunnei
32c509b82d Merge pull request #217 from shinyquagsire23/time-s-missing
time: Add missing time:s functions, used for libnx
2018-02-23 08:52:37 -08:00
bunnei
6bf7108545 Merge pull request #210 from MerryMage/f/dynarmic/sysreg
arm_dynarmic: Implement system registers and provide more hooks
2018-02-23 08:51:52 -08:00
shinyquagsire23
a63d4fa5b4 time: Add missing time:s functions, used for libnx 2018-02-23 00:34:15 -07:00
mailwl
e4f94ee30b Stub more functions 2018-02-22 17:28:15 +03:00
mailwl
910198a29a Stub am::SetScreenShotPermission, and bsd::StartMonitoring functions 2018-02-22 13:04:23 +03:00
bunnei
7f0ecbf859 Merge pull request #211 from shinyquagsire23/time_local
time: Add GetStandardLocalSystemClock, used by libnx
2018-02-22 01:09:01 -05:00
bunnei
fa28dbe0f3 Merge pull request #209 from MerryMage/f/scheduler-shutdown
core: Fix scheduler-shutdown related crash
2018-02-22 01:07:54 -05:00
shinyquagsire23
944132dbe5 time: Add GetStandardLocalSystemClock, used by libnx 2018-02-21 18:43:05 -07:00
MerryMage
32d127ad3e dynarmic: Update to 6b4c6b0
6b4c6b0 impl: Update PC when raising exception
7a1313a A64: Implement FDIV (vector)
b2d781d system: Raise exception for YIELD, WFE, WFI, SEV, SEVL
b277bf5 Correct FPSR and FPCR
7673933 A64: Implement USHL
8d0e558 A64: Implement UCVTF (vector, integer), scalar variant
da9a4f8 A64: Partially implement FCVTZU (scalar, fixed-point) and FCVTZS (scalar, fixed-point)
7479684 A64: Implement system register TPIDR_EL0
0fd75fd A64: Implement system registers FPCR and FPSR
31e370c A64: Implement system register CNTPCT_EL0
9a88fd3 A64: Implement system register CTR_EL0
1d16896 A64: Implement NEG (vector)
3184edf IR: Add IR instruction ZeroVector
31f8fbc emit_x64_floating_point: Add maybe_unused to preprocess parameter
567eb1a A64: Implement FMINNM (scalar)
c6d8fa1 A64: Implement FMAXNM (scalar)
616056d constant_pool: Add frame parameter
a3747cb A64: Implement ADDP (scalar)
5cd5d9f reg_alloc: Only exchange GPRs
dd0452a A64: Implement DUP (element), scalar variant
e5732ea emit_x64_floating_point: Correct FP{Max,Min}{32,64} implementations for -0/+0
40eb9c3 A64: Implement FMAX (scalar), FMIN (scalar)
7cef39b fuzz_with_unicorn: QEMU's implementation of FCVT is incorrect
826dce2 travis: Switch unicorn repository
9605f28 a64/config: Allow NaN emulation accuracy to be set
e9435bc a64_emit_x64: Add conf to A64EmitContext
30b596d fuzz_with_unicorn: Explicitly test floating point instructions
be292a8 A64: Implement FSQRT (scalar)
3c42d48 backend_x64: Accurately handle NaNs
4aefed0 fuzz_with_unicorn: Print AArch64 disassembly
2018-02-21 21:39:07 +00:00
MerryMage
e8b9731af3 arm_dynarmic: LOG_INFO on unicorn fallback 2018-02-21 21:39:07 +00:00
MerryMage
cc368de1a0 memory: LOG_ERROR when falling off end of page table 2018-02-21 21:39:07 +00:00
MerryMage
1cdc74c5e9 core: Fix scheduler-shutdown related crash 2018-02-21 16:38:18 +00:00
mailwl
1289a3c3c1 Add warning if Domain request has no domain message header 2018-02-20 22:51:54 +03:00
mailwl
827152d1fd Fix: change check for domain order and existance of domain message header 2018-02-20 21:59:58 +03:00
mailwl
1572c45aa0 IPC: add domain header to response if only it exists in request 2018-02-20 19:27:49 +03:00
bunnei
6a2197806e Merge pull request #206 from mailwl/aoc-listaddoncontent
Service/AOC: stub ListAddOnContent function
2018-02-20 10:45:50 -05:00
bunnei
587f22b610 Merge pull request #205 from bunnei/more-puyo-stubs
Stub several friend:a and acc:u0 service functions
2018-02-20 10:44:55 -05:00
mailwl
46931a9566 Service/AOC: stub ListAddOnContent function 2018-02-20 10:30:12 +03:00
bunnei
678574972a acc_u0: Stub ListOpenUsers service function. 2018-02-19 17:39:41 -05:00
bunnei
7bee3427d0 service: Add Friend service interface. 2018-02-19 17:34:02 -05:00
bunnei
1d491d636d logging: Add category for Friend service. 2018-02-19 17:31:54 -05:00
bunnei
23fe6f5be3 Merge pull request #202 from bunnei/scheduler-cleanup
Scheduler cleanup
2018-02-19 17:23:05 -05:00
bunnei
8db80d8389 scheduler: Cleanup based on PR feedback. 2018-02-19 16:46:42 -05:00
bunnei
ad55d22130 Merge pull request #203 from Subv/ensure_save_data
AM: Corrected the response in EnsureSaveData.
2018-02-19 15:32:54 -05:00
Subv
5ab285f1f9 AM: Corrected the response in EnsureSaveData.
The values are still unknown and the function is still considered a stub.
Puyo Puyo Tetris now tries to call fsp-srv:MountSaveData.
2018-02-18 18:09:52 -05:00
bunnei
ac81c02ed9 kernel: Use Scheduler class for threading. 2018-02-18 15:17:16 -05:00
bunnei
c78d495161 kernel: Add Scheduler, which encapsulates the scheduling loading from Thread module. 2018-02-18 14:58:40 -05:00
bunnei
2d4a6883bc core: Use shared_ptr for cpu_core. 2018-02-18 14:52:09 -05:00
bunnei
cec0d4f191 kernel: Remove unused address_arbiter code. 2018-02-18 14:46:11 -05:00
bunnei
de43db3400 Merge pull request #198 from N00byKing/clang
Use Docker for Build Target clang-format for travis.
2018-02-18 14:12:11 -05:00
bunnei
ec39c9eb32 Merge pull request #201 from Subv/ipc_delay_
Kernel/IPC: Add a small delay after each SyncRequest to prevent thread starvation.
2018-02-18 14:11:34 -05:00
bunnei
5babad5de5 Merge pull request #200 from Subv/bufferproducerfence
Make the fence handling in Vi a little less of a hack.
2018-02-18 14:11:04 -05:00
Subv
94ee8fc97b Kernel/IPC: Add a small delay after each SyncRequest to prevent thread starvation.
Ported from citra PR #3091

The delay specified here is from a Nintendo 3DS, and should be measured in a Nintendo Switch.

This change is enough to prevent Puyo Puyo Tetris's main thread starvation.
2018-02-18 13:25:48 -05:00
N00byKing
b36ce74d18 Update build.sh 2018-02-18 01:01:24 +01:00
bunnei
f6e548fbc0 Merge pull request #199 from FernandoS27/update_dynarmic
Updated Dynarmic
2018-02-17 14:32:17 -05:00
Subv
416f692f6e nvmap: Make IocFromId return the same existing handle instead of creating a new one.
Games like Puyo Puyo Tetris and BOTW seem to depend on the buffer always having the same handle
2018-02-17 14:01:01 -05:00
Subv
d758332425 Parcel: Ensure we don't read past the end of the parcels in Vi. 2018-02-17 14:00:44 -05:00
Subv
2662de6e52 Vi: Mark all fences as NO_FENCE in the DequeueBuffer response parcel. 2018-02-17 14:00:30 -05:00
Subv
1b64160d83 Vi: Always write the IGBPBuffer in the RequestBuffer response parcel.
This may break libnx homebrew due to a bug in libnx but is required by official games since they always assume that the buffer will be there.
2018-02-17 13:59:45 -05:00
FernandoS27
bd6432f1ff updated dynarmic 2018-02-17 14:57:32 -04:00
bunnei
3388208597 Merge pull request #197 from mailwl/hid
Service/hid: stub some functions
2018-02-16 20:55:26 -05:00
N00byKing
947831ff1e Use Docker for Build Target clang-format for travis.
This uses the (apparently) more stable Ubuntu Repo instead of the LLVM one.
2018-02-16 22:21:31 +01:00
mailwl
6797d4a907 Service/hid: stub some functions 2018-02-16 06:15:05 +03:00
bunnei
98ffad4303 Merge pull request #195 from bunnei/shared-font
pl_u: Add basic support for shared fonts.
2018-02-15 17:22:59 -05:00
Subv
7a1917e0fd nvhost-ctrl: Stub NVHOST_IOCTL_CTRL_EVENT_WAIT. 2018-02-14 22:57:57 -05:00
Subv
35d0d06885 Vi: Mark the fences as valid in the DequeueBuffer response parcel. 2018-02-14 22:57:56 -05:00
Subv
d18446f63a Vi: Added a missing u32 in the DequeueBuffer response parcel. 2018-02-14 22:57:56 -05:00
Subv
b78ffc4abf Vi: Don't write the IGBPBuffer in the IGBPRequestBufferResponseParcel. 2018-02-14 22:57:55 -05:00
Subv
8dee5663b3 Vi: Properly write the BufferProducerFence object in the DequeueBuffer response parcel. 2018-02-14 22:57:54 -05:00
bunnei
df008a159b shared_memory: Remove some checks. 2018-02-14 22:24:06 -05:00
bunnei
42c062c620 pl_u: Implement basic shared font loading from RAM dump. 2018-02-14 22:22:41 -05:00
bunnei
fa58d95027 log: Add logging category for NS services. 2018-02-14 21:43:11 -05:00
bunnei
e017184445 hid: Stub GetVibrationDeviceInfo and SendVibrationValues. 2018-02-14 21:16:27 -05:00
bunnei
db873a232c Merge pull request #188 from bunnei/refactor-buffer-descriptor
Refactor IPC buffer descriptor interface
2018-02-14 18:31:53 -05:00
bunnei
88bfec37ce hle_ipc: Remove const from WriteBuffer size. 2018-02-14 14:21:10 -05:00
bunnei
756e9f1484 Merge pull request #192 from jroweboy/fix-fps
Fix fps counter to correctly measure frame end when there was no frame to draw
2018-02-14 13:48:33 -05:00
Hexagon12
a8d8c21e00 pls, that was easy 2018-02-14 19:47:51 +02:00
James Rowe
61c39f0fdd Fix fps counter to correctly measure frame end when there was no frame to draw 2018-02-14 10:16:39 -07:00
bunnei
b65c096be5 Merge pull request #190 from bunnei/fix-qt-waittree
debugger: Fix wait_tree crash.
2018-02-14 10:18:56 -05:00
bunnei
d939792b9b Merge pull request #191 from lioncash/log
core: Silence formatting specifier warnings
2018-02-14 10:07:03 -05:00
Lioncash
fe0775d2f4 memory: Silence formatting sepecifier warnings 2018-02-14 01:54:54 -05:00
Lioncash
e6bf72877f nso: Silence formatting specifier warnings 2018-02-14 01:52:55 -05:00
Lioncash
2ade136ff4 deconstructed_rom_directory: Silence formatting specifier warnings 2018-02-14 01:52:55 -05:00
Lioncash
eba57fce88 nvdrv/interface: Silence formatting specifier warnings 2018-02-14 01:52:55 -05:00
Lioncash
ee170cbcea nvmap: Silence formatting specifier warnings 2018-02-14 01:52:55 -05:00
Lioncash
983777a317 nvhost_gpu: Silence formatting specifier warnings 2018-02-14 01:52:55 -05:00
Lioncash
2d388a75f0 nvhost_ctrl: Silence formatting specifier warnings 2018-02-14 01:52:54 -05:00
Lioncash
ce1fe0387f nvhost_ctrl_gpu: Silence formatting specifier warnings 2018-02-14 01:52:54 -05:00
Lioncash
dc97117a0b nvhost_as_gpu: Silence formatting specifier warnings 2018-02-14 01:52:49 -05:00
Lioncash
c1146d2a5f thread: Silence formatting specifier warnings 2018-02-14 01:50:14 -05:00
Lioncash
1e33db8573 vm_manager: Silence formatting specifier warnings 2018-02-14 01:50:14 -05:00
Lioncash
51ce224a96 gdbstub: Silence formatting specifier warnings 2018-02-14 01:50:01 -05:00
bunnei
f1b82634bc Merge pull request #189 from lioncash/misc
maxwell_3d: Make constructor explicit
2018-02-14 00:49:03 -05:00
bunnei
6fd19f05f1 hle_ipc: Add GetReadBufferSize and check write buffer size. 2018-02-14 00:14:17 -05:00
bunnei
c85e3a2234 debugger: Fix wait_tree crash. 2018-02-14 00:02:14 -05:00
bunnei
516a95721c service: Remove remaining uses of BufferDescriptor*. 2018-02-13 23:54:13 -05:00
bunnei
d6e52581ac audio: Use WriteBuffer instead of BufferDescriptorB. 2018-02-13 23:54:12 -05:00
bunnei
f16bb1dfcf vi: Eliminate direct usage of BufferDescriptorB. 2018-02-13 23:54:12 -05:00
bunnei
d42e77797e nvdrv: Use ReadBuffer/WriteBuffer functions for Ioctl. 2018-02-13 23:54:12 -05:00
bunnei
8f84665775 vi: Use ReadBuffer/WriteBuffer functions for TransactParcel. 2018-02-13 23:54:11 -05:00
bunnei
fc1b425520 hle_ipc: Add helper functions for reading and writing buffers. 2018-02-13 23:54:07 -05:00
Lioncash
490d0e36a0 maxwell_3d: Make constructor explicit 2018-02-13 23:47:51 -05:00
bunnei
1ce6fff064 hle_ipc: Add helper functions for reading and writing buffers. 2018-02-13 23:26:03 -05:00
bunnei
4f8ee5e456 vi: Fix TransactParcelAuto to support both buffer formats. 2018-02-13 23:26:01 -05:00
bunnei
af8ae770ef Merge pull request #187 from Subv/maxwell3d_query
GPU: Partially implemented the QUERY_* registers in the Maxwell3D engine.
2018-02-13 23:25:07 -05:00
bunnei
0a55eb588b audren_u: Schedule reoccuring event. (#183)
* audren_u: Schedule reoccuring event.

* audren_u: Stub GetAudioRenderersProcessMasterVolume, and misc. changes.
2018-02-13 20:47:33 -05:00
bunnei
826e9c9782 Merge pull request #181 from bunnei/vi-fixes-2
VI cleanup and add a hack for booting games
2018-02-13 19:17:27 -05:00
bunnei
87c3c93464 Merge pull request #184 from mailwl/lm
Service/lm: add support to multiline logs
2018-02-13 10:29:33 -05:00
mailwl
55de13efcc Service/lm: add support to multiline logs 2018-02-13 09:44:53 +03:00
bunnei
91e19deb39 vi: Add FENCE_HACK, which is useful for booting BOTW. 2018-02-12 21:24:40 -05:00
bunnei
a9e4e8294a vi: Stub TransactParcel CancelBuffer. 2018-02-12 21:24:39 -05:00
bunnei
4f969e2271 TransactParcel: Move WriteBlock to narrowest scope. 2018-02-12 21:24:38 -05:00
bunnei
0a87eb71ba Merge pull request #180 from MerryMage/f/dynarmic/direct-page-table
arm_dynarmic: Support direct page table access
2018-02-12 19:17:31 -05:00
MerryMage
6085d32cf5 arm_dynarmic: Support direct page table access 2018-02-12 21:53:32 +00:00
bunnei
ce8006e851 Merge pull request #179 from gdkchan/audren_stubs
Stub RequestUpdateAudioRenderer, StartAudioRenderer and StopAudioRenderer
2018-02-12 16:33:30 -05:00
gdkchan
3160f83607 Add RequestUpdateAudioRenderer, StartAudioRenderer and StopAudioRenderer stubs to audren:u 2018-02-12 17:44:55 -03:00
bunnei
be5ba4d952 Merge pull request #178 from Subv/command_buffers
GPU: Added a command processor to decode the GPU pushbuffers and forward the commands to their respective engines
2018-02-12 13:51:52 -05:00
Subv
ac61a7d1e6 GPU: Partially implemented the QUERY_* registers in the Maxwell3D engine.
Only QueryMode::Write is supported at the moment.
2018-02-12 12:34:41 -05:00
Subv
6cddf9d88e Make a GPU class in VideoCore to contain the GPU state.
Also moved the GPU MemoryManager class to video_core since it makes more sense for it to be there.
2018-02-11 23:44:12 -05:00
Subv
e01a8f2187 GPU: Added a command processor to decode the GPU pushbuffers and forward the commands to their respective engines. 2018-02-11 22:42:48 -05:00
bunnei
890e98a33e Merge pull request #177 from bunnei/vi-fixes
Several misc. VI fixes
2018-02-11 21:47:35 -05:00
Subv
ba2426aa3f nvdrv: Make the GPU memory manager available to nvhost-gpu. 2018-02-11 21:30:23 -05:00
bunnei
deadcb39c2 renderer_opengl: Support framebuffer flip vertical. 2018-02-11 21:03:55 -05:00
bunnei
6fce1414c3 vi: Parse IGBPQueueBufferRequestParcel params and expose buffer flip vertical. 2018-02-11 21:00:41 -05:00
bunnei
068744db1b vi: Fix OpenLayer and CreateStrayLayer. 2018-02-11 17:28:07 -05:00
bunnei
b26cdf1fe5 Merge pull request #175 from bunnei/libnx-fixes-2
More fixes for Libnx
2018-02-10 01:14:40 -05:00
bunnei
8e7da73214 fsp_srv: Stub MountSdCard. 2018-02-09 23:33:50 -05:00
bunnei
0532de6559 apm: Refactor service impl. to support multiple ports. 2018-02-09 23:33:49 -05:00
bunnei
c83a1b2320 vi: Implement TransactParcelAuto. 2018-02-09 23:33:49 -05:00
bunnei
725304094e nvflinger: (Hack) Use first available buffer if none are found. 2018-02-09 23:33:49 -05:00
bunnei
63de56ee0f IGBPQueueBufferRequestParcel: Don't enforce buffer length.
- Another fix for libnx.
2018-02-09 23:33:49 -05:00
bunnei
309276a317 IGBPRequestBufferResponseParcel: Fix response for libnx. 2018-02-09 23:33:43 -05:00
bunnei
1add3b20c4 Merge pull request #171 from bunnei/libnx-fixes
Various fixes for libnx, etc.
2018-02-09 15:51:43 -05:00
bunnei
3b35202280 Merge pull request #173 from MerryMage/feature/dynarmic-fix-windows
dynarmic: Fix bug due to Windows ABI mismatch
2018-02-09 13:07:15 -05:00
MerryMage
45e5a67676 dynarmic: Fix bug due to Windows ABI mismatch 2018-02-09 16:05:28 +00:00
bunnei
22caeee64f nvdrv: Fix QueryEvent for libnx. 2018-02-09 00:56:45 -05:00
bunnei
576f0cf027 IApplicationDisplayService::CloseDisplay: Fix response params size. 2018-02-08 23:20:23 -05:00
bunnei
ca99063600 nvhost_ctrl_gpu: Implement ZCullGetInfo. 2018-02-08 23:17:59 -05:00
bunnei
205daab50d Merge pull request #170 from MerryMage/feature/dynarmic-update-201802
dynarmic: Update to 41ae12263
2018-02-08 23:12:57 -05:00
MerryMage
d3bbed5e78 dynarmic: Update to 41ae12263
Changes: Primarily implementing more A64 instructions
2018-02-09 00:29:36 +00:00
bunnei
dc0a137e5b acc_u0: Implement ListAllUsers. 2018-02-08 18:59:23 -05:00
bunnei
db11c9a0b9 Merge pull request #169 from bunnei/gpu-mem
nvdrv: Implement AllocateSpace and MapBufferEx
2018-02-07 22:10:42 -08:00
bunnei
a39a65cbe0 nvhost_as_gpu: Implement AllocateSpace and MapBufferEx. 2018-02-07 23:31:28 -05:00
bunnei
c711253798 nvdrv: Add MemoryManager class to track GPU memory. 2018-02-07 23:31:26 -05:00
bunnei
196f8dff08 nvmap: Refactor to expose nvmap objects. 2018-02-07 22:55:12 -05:00
bunnei
703880c9ab nvhost_as_gpu: Add nvmap as a class member. 2018-02-07 22:55:09 -05:00
bunnei
869d65e923 Merge pull request #168 from mailwl/new-stubs
Service: stub some functions in am, audio, time, vi services
2018-02-07 07:51:35 -08:00
mailwl
335096e19a Service: stub some functions in am, audio, time, vi services 2018-02-07 15:11:17 +03:00
bunnei
2b75b52489 Merge pull request #166 from mailwl/hid-SetNpadHandhelpActivationMode
Service/hid: stub SetNpadHandheldActivationMode
2018-02-06 07:36:17 -08:00
mailwl
8d300b2d7e Service/hid: stub SetNpadHandheldActivationMode 2018-02-06 10:47:00 +03:00
bunnei
1cd9438945 Merge pull request #165 from bunnei/puyo-fixes
Stubs for HID, AM, and a mutex fix
2018-02-05 20:14:40 -08:00
bunnei
903beb43a8 mutex: Update hasWaiters on release. 2018-02-05 21:54:10 -05:00
bunnei
1963222933 hid: Stub ActivateTouchScreen and SetNpadJoyHoldType. 2018-02-05 21:53:11 -05:00
David
d129905a66 Extra nvdrv support (#162)
* FinishInitalize needed for 3.0.1+ games

* nvdrv:s and nvdrv:t both use NVDRV

* Most settings return 0 on hardware, disabled NV_MEMORY_PROFILER for now.

NVN_THROUGH_OPENGL & NVRM_GPU_PREVENT_USE are a few interesting settings to look at. Carefully choosing settings can help with drawing graphics later on

* Initial /dev/nvhost-gpu support

* ZCullBind

* Stubbed SetErrorNotifier

* Fixed SetErrorNotifier log, Added SetChannelPriority

* Allocate GPFIFO Ex2, Allocate Obj Ctx, Submit GPFIFO

* oops

* Fixed up naming/structs/enums. Used vector instead of array for "gpfifo_entry"

* Added missing fixes

* /dev/nvhost-ctrl-gpu

* unneeded struct

* Forgot u32 in enum class

* Automatic descriptor swapping for ioctls, fixed nvgpu_gpu_get_tpc_masks_args being incorrect size

* nvdrv#QueryEvent

* Renamed logs for nvdrv

* Refactor ioctl so nv_result isn't needed

* /dev/nvhost-as-gpu

* Fixed Log service naming, CtxObjects now u32, renamed all structs, added static_asserts to structs, used INSERT_PADDING_WORDS instead of u32s

* nvdevices now uses "Ioctl" union,

* IoctlGpfifoEntry now uses bit field

* final changes
2018-02-05 18:19:31 -08:00
bunnei
c83f69841f IApplicationFunctions: Stub out EnsureSaveData. 2018-02-05 20:58:11 -05:00
bunnei
294b2b2c17 Merge pull request #164 from ogniK5377/libnx_sm_fix
Don't call UNIMPLEMENTED for 'empty services', just return error code
2018-02-05 14:01:46 -08:00
bunnei
e33117c00a Merge pull request #163 from ogniK5377/istorage_to_romfs
Changed .istorage to .romfs
2018-02-05 08:02:14 -08:00
David Marcec
22bc951d7e Dont call UNIMPLEMENTED for 'empty services', just return error code 2018-02-05 02:03:22 -08:00
David Marcec
f9ba5a7e11 Changed .istorage to .romfs 2018-02-05 00:01:10 -08:00
bunnei
35517ca92c Merge pull request #161 from bunnei/service-improvements
Many service improvements
2018-02-04 21:10:29 -08:00
bunnei
1d51b25ed1 set: GetAvailableLanguageCodes should not return lang_codes size. 2018-02-04 23:42:43 -05:00
bunnei
fe99052599 nvflinger: Signal BufferQueue native handle event.
- This gets BOTW booting.
2018-02-04 23:00:35 -05:00
bunnei
8e1dbb26bd logger: Add Time service logging category. 2018-02-04 22:59:52 -05:00
bunnei
c689fe8424 logger: Add SET service logging category. 2018-02-04 22:55:45 -05:00
bunnei
fc1359dc03 logger: Add PCTL service logging category. 2018-02-04 22:44:00 -05:00
bunnei
649960b4eb logger: Add LM service logging category. 2018-02-04 22:41:55 -05:00
bunnei
8d2e4c3d39 logger: Add APM service logging category. 2018-02-04 22:39:47 -05:00
bunnei
69697535bf lm: Ensure log string is non-empty before checking back(). 2018-02-04 22:36:57 -05:00
bunnei
485c6541cf logger: Add NIFM service logging category. 2018-02-04 22:35:42 -05:00
bunnei
8a5833f7ad logger: Add VI service logging category. 2018-02-04 22:26:44 -05:00
bunnei
119f02a439 hid: Stub out several functions. 2018-02-04 22:24:20 -05:00
bunnei
ad97414057 hid: Implement CreateActiveVibrationDeviceList. 2018-02-04 17:06:14 -05:00
bunnei
ea615ef5a4 logger: Use Service_HID category where applicable. 2018-02-04 17:02:39 -05:00
bunnei
764bbaa19c logger: Use Service_NVDRV category where applicable. 2018-02-04 17:00:33 -05:00
bunnei
65cfe09b62 logger: Add AM service logging category. 2018-02-04 16:58:12 -05:00
bunnei
a947f16b63 logger: Add "account" service logging category. 2018-02-04 16:40:12 -05:00
bunnei
6674e8e048 acc_u0: Stub out GetLastOpenedUser. 2018-02-04 16:32:01 -05:00
bunnei
1ddc18454e Merge pull request #160 from bunnei/svc-improvements
Several SVC fixes and improvements
2018-02-04 13:25:05 -08:00
bunnei
42fc437268 GetInfo: Implement IsCurrentProcessBeingDebugged. 2018-02-04 12:34:45 -05:00
bunnei
0b6b147939 WaitProcessWideKeyAtomic: Handle case where condition variable was already created. 2018-02-04 12:30:51 -05:00
bunnei
eaf75ea970 Merge pull request #159 from mailwl/acc0-fix
acc:u0 : stub GetAccountId
2018-02-04 09:04:32 -08:00
mailwl
272058d7d9 acc:u0 : stub GetAccountId 2018-02-04 09:55:25 +03:00
bunnei
647364db8f svc: SharedMemory size should be 64-bits and cleanup. 2018-02-03 13:36:54 -05:00
bunnei
f9c9ce2005 ArbitrateLock: Assert that requesting_thread is current_thread. 2018-02-03 13:29:18 -05:00
bunnei
abc4be8e0f Merge pull request #157 from bunnei/fix-duplicate-session
controller: DuplicateSession should return a ClientSession.
2018-02-03 10:25:01 -08:00
bunnei
b5bdaf3441 Merge pull request #156 from mailwl/nifm
Service/nifm: add nifm:a, nifm:s and nifm:u services
2018-02-03 10:20:52 -08:00
bunnei
72c5bfb1fa controller: DuplicateSession should return a ClientSession. 2018-02-03 12:09:33 -05:00
mailwl
f67a8d87a0 Service:nifm: add nifm:a, nifm:s and nifm:u services 2018-02-03 18:09:51 +03:00
mailwl
1a8f5bfb8e Service/am: Add AppletAE service (#153)
* Add AppletAE, step 1: move common interfaces to am.h

* Add AppletAE, step 2
2018-02-02 13:03:40 -08:00
bunnei
5ad9b3e19d Merge pull request #154 from mailwl/vi_create_stray_array
vi::CreateStrayLayer : add padding to request
2018-02-02 09:01:21 -08:00
bunnei
1909802156 Merge pull request #155 from mailwl/vi-services
Services/vi: add vi:s and vi:u services
2018-02-02 09:00:31 -08:00
bunnei
2e6776909b Merge pull request #152 from shinyquagsire23/sharedmem-valid-bounds
shared_memory: Only mark addresses as invalid if they are within the heap
2018-02-02 08:59:45 -08:00
mailwl
524c12a5f8 Services/vi: add vi:s and vi:u services 2018-02-02 12:59:50 +03:00
mailwl
58601abd1c vi::CreateStrayLayer : add padding to request 2018-02-02 12:03:02 +03:00
shinyquagsire23
96c444d1ff shared_memory: Only mark addresses as invalid if they are within the heap 2018-01-29 23:38:56 -07:00
mailwl
eaa9f968a6 [WIP] sfdnsres: stub (#146)
sfdnsres: Add several stubs
2018-01-29 22:29:47 -08:00
bunnei
449e32bb81 Merge pull request #151 from lioncash/catch
externals: Update catch to v2.1.1
2018-01-28 02:50:38 -05:00
Lioncash
090da0b5c1 externals: Update catch to v2.1.1 2018-01-27 15:42:56 -05:00
bunnei
c1a8e4bfe4 Merge pull request #148 from MerryMage/feature/special-memory
memory: Replace all memory hooking with Special regions
2018-01-27 15:20:53 -05:00
bunnei
81be2027ad Merge pull request #149 from MerryMage/feature/remove-x86_64h
travis: Remove CMAKE_OSX_ARCHITECTURES argument
2018-01-27 15:17:12 -05:00
MerryMage
6755c0d1cf travis: Remove CMAKE_OSX_ARCHITECTURES argument
Unicorn only builds a x86_64 library, without a x86_64h slice. We can't link against
x86_64-only in this manner for static libraries.
2018-01-27 15:36:38 +00:00
MerryMage
738f91a57d memory: Replace all memory hooking with Special regions 2018-01-27 15:16:39 +00:00
Flame Sage
12d95f0214 Merge pull request #147 from chris062689/master
Added webhook notifications to TravisCI build.
2018-01-26 22:01:03 -05:00
Flame Sage
c93136a2bf Added webhook notifications to TravisCI build. 2018-01-27 02:53:25 +00:00
bunnei
4cd2b475cb Merge pull request #144 from KAMiKAZOW/patch-1
Install Linux icon in hicolor instead of pixmaps
2018-01-26 18:33:51 -05:00
bunnei
e26e95fc37 Merge pull request #145 from jroweboy/oops
Fix typo for dependent options
2018-01-26 09:52:03 -05:00
James Rowe
9a005d5239 Fix typo for dependent options 2018-01-25 22:04:04 -07:00
KAMiKAZOW
9e41053ead Install Linux icon in hicolor instead of pixmaps
hicolor is the preferred location for applications. See https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#directory_layout

Same as https://github.com/citra-emu/citra/pull/3007
2018-01-26 04:00:12 +01:00
bunnei
767ce8abc8 Merge pull request #142 from bunnei/improve-time
time: Implement ISteadyClock::GetCurrentTimePoint
2018-01-25 21:56:04 -05:00
bunnei
3258db29da time: Implement ISteadyClock::GetCurrentTimePoint. 2018-01-25 21:29:39 -05:00
bunnei
748c0de539 Merge pull request #137 from bunnei/improve-ipc
Improve IPC, unify Domains and Sessions, and add validation
2018-01-24 23:09:03 -05:00
bunnei
de177f6692 audout_u: Various cleanups. 2018-01-24 22:38:19 -05:00
bunnei
714a576113 ResponseBuilder: Use a bit field for customizing instead of always_move_handles. 2018-01-24 22:24:20 -05:00
bunnei
f0b6baf3ad time: Stub GetSystemClockContext function. 2018-01-24 22:24:18 -05:00
bunnei
7588b24f46 server_session: Fix scenario where all domain handlers are closed. 2018-01-24 22:24:16 -05:00
bunnei
1b1d399e5f hle: Rename RequestBuilder to ResponseBuilder. 2018-01-24 22:24:10 -05:00
bunnei
f9dae99006 service: Fix all incorrect IPC response headers. 2018-01-24 22:21:33 -05:00
bunnei
f0035420d7 ipc_helpers: Make interface domain agnostic and add header validation. 2018-01-24 22:19:01 -05:00
bunnei
27bad0598a hle: Integrate Domain handling into ServerSession. 2018-01-24 22:18:56 -05:00
bunnei
67758857e4 hle: Remove Domain and SyncObject kernel objects. 2018-01-24 22:18:54 -05:00
bunnei
932fa94af7 handle_table: Remove ConvertSessionToDomain. 2018-01-24 22:18:51 -05:00
st4rk
44eb840232 audout:u OpenAudioOut and IAudioOut (#138)
* Updated the audout:u and IAudioOut, now it might work with RetroArch without trigger an assert, however it's not the ideal implementation

* Updated the audout:u and IAudioOut, now it might work with RetroArch without trigger an assert, however it's not the ideal implementation

* audout:u OpenAudioOut implementation and IAudioOut cmd 1,2,3,4,5 implementation

* using an enum for audio_out_state as well as changing its initialize to member initializer list

* Minor fixes, added Service_Audio for LOG_*, changed PcmFormat enum to EnumClass

* Minor fixes, added Service_Audio for LOG_*, changed PcmFormat enum to EnumClass

* added missing Audio loggin subclass, minor fixes, clang comment breakline

* Solving backend logging conflict

* minor fix

* Fixed duplicated Service NVDRV in backend.cpp, my bad
2018-01-24 22:17:54 -05:00
bunnei
b35cf672c0 Merge pull request #140 from gdkchan/time_fix
Fix time returning epoch time in milliseconds rather than in seconds
2018-01-24 10:45:07 -05:00
gdkchan
703be1931a Fix time returning epoch time in milliseconds rather than in seconds 2018-01-24 11:54:47 -03:00
bunnei
a93ff5ed0f Merge pull request #139 from Rozelette/log_nvdrv
logging: add missing NVDRV subclass to macro list
2018-01-24 00:03:33 -05:00
Rozlette
ad64e7e86d logging: add missing NVDRV subclass to macro list 2018-01-23 22:18:23 -06:00
bunnei
337664ae7c Merge pull request #136 from N00byKing/patch-1
Correct Spelling
2018-01-23 20:42:45 -05:00
N00byKing
afa1ed6ad9 Correct Spelling 2018-01-23 14:20:18 +01:00
bunnei
d1b64cdc07 Merge pull request #135 from Subv/no_ports
IPC: Don't create unnecessary ports when returning sub interfaces.
2018-01-22 21:37:15 -05:00
Subv
b0489c9a64 Services: Added a todo about returning interfaces as domain objects in lm, hid and time. 2018-01-22 20:40:43 -05:00
bunnei
d8bd70d396 Merge pull request #133 from Subv/nvflinger2
AppletOE: Stubbed CreateManagedDisplayLayer to create a new layer in the default display.
2018-01-22 17:52:47 -05:00
Subv
7efa6e8801 Time: Don't create unnecessary ports when retrieving the clock service sessions. 2018-01-22 17:42:11 -05:00
Subv
1003996e80 HID: Don't create an unnecessary port in CreateAppletResource. 2018-01-22 17:41:31 -05:00
Subv
1a9c96e4de LM: Don't create an unnecessary port in Initialize. 2018-01-22 17:35:40 -05:00
Subv
67c43e9200 IPC: Don't create an unnecessary port when using PushIpcInterface outside of a domain. 2018-01-22 17:31:23 -05:00
bunnei
de2e5a0855 Merge pull request #134 from gdkchan/audout_hid_fix
Stub OpenAudioOut and fix a issue with HID IAppletResource
2018-01-22 17:09:09 -05:00
gdkchan
832009bfdb Stub OpenAudioOut and fix a issue with HID IAppletResource being created more than once 2018-01-22 17:27:55 -03:00
Subv
10c67bf395 AppletOE: Stubbed CreateManagedDisplayLayer to create a new layer in the Default display.
This function is used by libnx to obtain a new layer.
2018-01-22 13:50:22 -05:00
Subv
eb58f852f8 AppletOE: Make ISelfController keep a reference to nvflinger.
It'll be needed when we implement CreateManagedDisplayLayer.
2018-01-22 13:46:36 -05:00
Subv
42859461f3 Services: Vi shouldn't be responsible for creating nvflinger.
It is now created during Service initialization and passed to all the services that need it.
2018-01-22 13:40:02 -05:00
bunnei
7f19a7d305 Merge pull request #132 from Subv/nvflinger
VI: Move BufferQueue and NVFlinger to their own folder/namespace.
2018-01-22 13:22:20 -05:00
Subv
8d7686ff8e VI: Move BufferQueue and NVFlinger to their own folder/namespace. 2018-01-22 11:54:58 -05:00
st4rk
07355cf7cc Added stubs for audio services. (#116)
* stubs for audout:u, audin:u, audrec:u, audren:u, codecctl and decoding tables with nullptr for future implementations

* fixing the changes requested (remove private, explicit)
2018-01-21 22:03:36 -05:00
bunnei
fdbb039427 Merge pull request #131 from lioncash/enum
nvmap: Make IoctlCommands an enum class
2018-01-21 22:01:27 -05:00
bunnei
32d91fa6d2 Merge pull request #130 from MerryMage/dynarmic
externals: Update dynarmic
2018-01-21 18:08:18 -05:00
David
eeb3b5eed7 Added nvmemp, Added /dev/nvhost-ctrl, SetClientPID now stores pid (#114)
* Added nvmemp, Added /dev/nvhost-ctrl, SetClientPID now stores pid

* used clang-format-3.9 instead

* lowercase pid

* Moved nvmemp handlers to cpp

* Removed unnecessary logging for NvOsGetConfigU32. Cleaned up log and changed to LOG_DEBUG

* using std::arrays instead of c arrays

* nvhost get config now uses std::array completely

* added pid logging back

* updated cmakelist

* missing includes

* added array, removed memcpy

* clang-format6.0
2018-01-21 17:59:50 -05:00
bunnei
2403143ff1 Merge pull request #128 from Subv/parcel_query
VI: Implement the Query transaction of IHOSBinderDriver, and stubbed some results.
2018-01-21 17:56:06 -05:00
Lioncash
06d2e1bd23 nvmap: Add a return 0 underneath the UNIMPLEMENTED macro
This macro resolves to an empty macro in release builds.
2018-01-21 17:07:47 -05:00
Lioncash
687a17acae nvmap: Make IoctlCommands an enum class
Prevents the enum values from polluting the surrounding scope
2018-01-21 17:07:13 -05:00
MerryMage
e7cb20fbf0 externals: Update dynarmic
a6d17e A64: Implement AND (vector)
963453 tests/A64: Randomize vectors
adcd34 tests/A64/unicorn: Print interrupt number when InterruptHook is hit
304c91 tests/A64: Allow RunTestInstance to start from an arbitrary offset
d333b5 A64: Implement ADD (vector, vector)
1cf87a A64: Implement REV, REV32, and REV16 (#126)
9fc157 IR: Simplify types. F32 -> U32, F64 -> U64, F128 -> U128
50c181 reg_alloc: GetBitWidth: Add UNREACHABLE
adccbf reg_alloc: Consider bitwidth of data and registers when emitting instructions
7b7f23 A64: Implement CSEL
2f8413 IR: Implement Conditional Select
ebb3e8 A64/tests: Split unicorn sanity checking from other tests
5740a0 tests/A64: Single random instruction: Test branch instructions as well
0892b4 A64/translate/branch: bug: Read-after-write error in BLR
e77bc2 A64: Implement SBFM, BFM, UBFM
0c37ca A64: Implement MOVN, MOVZ, MOVK
b6bb59 travis: Print current test information
e77207 fuzz_thumb: Off by one error
a04ca2 ir/location_descriptor: Add missing <functional> header for std::hash
1e0f5c travis: Run A64 tests
2018-01-21 22:05:43 +00:00
bunnei
ab8525705b Merge pull request #123 from bunnei/fs
Initial implementation of RomFS filesystem and fsp-srv
2018-01-21 16:15:15 -05:00
bunnei
5035d18baa file_sys: Clang format fixes. 2018-01-21 15:51:45 -05:00
bunnei
8e50d6002b fsp_srv: Various improvements to IStorage:Read implementation. 2018-01-21 15:51:43 -05:00
bunnei
d9a91d7678 deconstructed_rom_directory: Implement istorage loading for RomFS. 2018-01-21 15:39:31 -05:00
David Marcec
d64b7d7dfd filesystem: Implement basic IStorage functionality. 2018-01-21 15:39:28 -05:00
bunnei
00851a5ef4 file_sys: Cleanup to better match Switch file system constructs.
file_sys: Add factory class for RomFS file system.
2018-01-21 15:39:26 -05:00
bunnei
1c06c918af file_sys: Remove disk_archive, savedata_archive, and title_metadata. 2018-01-21 15:39:23 -05:00
bunnei
7988f02489 archive_backend: Minor changes to match Switch IFileSystem. 2018-01-21 15:39:20 -05:00
bunnei
2f71a32363 file_sys: Repurpose 3DS IVFC code for Switch ROMFS. 2018-01-21 15:39:18 -05:00
bunnei
4c07dde472 Merge pull request #129 from Rozelette/master
gdbstub: Update registers and sizes for aarch64
2018-01-21 15:32:15 -05:00
bunnei
ee024eb0a2 Merge pull request #124 from akkatracker/patch-1
Fix minor spelling error in CMakeLists
2018-01-21 13:06:37 -05:00
bunnei
1bcc233245 Merge pull request #125 from MerryMage/bundled-unicorn
Unicorn build fixups
2018-01-21 13:05:32 -05:00
Rozlette
425a78ec1b gdbstub: Update registers and sizes for aarch64
This gets gdbstub working at least to the point where clients can
communicate with it.

What works:
- Reading/writing GPRegs
- Reading/writing memory
- Interrupting the emulated program and continuing

What does NOT work:
- Breakpoints. Sizes have been updated to u64, but support will need to be
  added in the interpreter for them to work.
- VRegs. Mostly because my gdb was having issues with 128-bit regs for
  some reason. However, the current u128 representation is a bit
  awkward to use and should probably be updated first.
2018-01-21 11:12:49 -06:00
Subv
749043c809 VI: Implement the Query transaction of IHOSBinderDriver, and stubbed some results. 2018-01-21 11:13:47 -05:00
MerryMage
add8d40f3f CMakeLists: Fix unicorn build for macOS developers with x86_64-only systems
Some of us do not have any i386 libraries required to build x86-32 universal libraries.
2018-01-21 13:59:43 +00:00
MerryMage
bba785d643 CMakeLists: Do not look for system Unicorn by default
Since we use a custom build of unicorn it doesn't make much sense to look for
the system version, unless the user explicitly wants to override this.
2018-01-21 13:58:39 +00:00
236 changed files with 33506 additions and 5129 deletions

2
.gitmodules vendored
View File

@@ -18,7 +18,7 @@
url = https://github.com/fmtlib/fmt.git
[submodule "lz4"]
path = externals/lz4
url = http://github.com/lz4/lz4.git
url = https://github.com/lz4/lz4.git
[submodule "unicorn"]
path = externals/unicorn
url = https://github.com/yuzu-emu/unicorn

View File

@@ -3,16 +3,11 @@ matrix:
include:
- os: linux
env: NAME="clang-format"
sudo: required
dist: trusty
addons:
apt:
sources:
- sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
- sourceline: 'deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main'
packages:
- clang-format-6.0
script: "./.travis/clang-format/script.sh"
services: docker
install: "./.travis/clang-format/deps.sh"
script: "./.travis/clang-format/build.sh"
- os: linux
env: NAME="linux build"
sudo: required
@@ -42,3 +37,8 @@ deploy:
skip_cleanup: true
on:
tags: true
notifications:
webhooks:
urls:
- https://api.yuzu-emu.org/code/travis/notify

3
.travis/clang-format/build.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash -ex
docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash -ex /yuzu/.travis/clang-format/docker.sh

3
.travis/clang-format/deps.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh -ex
docker pull ubuntu:18.04

8
.travis/clang-format/docker.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash -ex
apt-get update
apt-get install -y clang-format-6.0
# Run clang-format
cd /yuzu
./.travis/clang-format/script.sh

View File

@@ -2,13 +2,13 @@
set -o pipefail
export MACOSX_DEPLOYMENT_TARGET=10.9
export MACOSX_DEPLOYMENT_TARGET=10.12
export Qt5_DIR=$(brew --prefix)/opt/qt5
export UNICORNDIR=$(pwd)/externals/unicorn
mkdir build && cd build
cmake --version
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h" -DCMAKE_BUILD_TYPE=Release
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
make -j4
ctest -VV -C Release

View File

@@ -13,7 +13,9 @@ option(ENABLE_SDL2 "Enable the SDL2 frontend" ON)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON "ENABLE_SDL2;MSVC" OFF)
option(ENABLE_QT "Enable the Qt frontend" ON)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_SDL2;MSVC" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit)
message(STATUS "Copying pre-commit hook")
@@ -209,8 +211,7 @@ else()
endif()
# If unicorn isn't found, msvc -> download bundled unicorn; everyone else -> build external
find_package(Unicorn QUIET)
if (NOT UNICORN_FOUND)
if (YUZU_USE_BUNDLED_UNICORN)
if (MSVC)
message(STATUS "unicorn not found, falling back to bundled")
# Detect toolchain and platform
@@ -249,7 +250,7 @@ if (NOT UNICORN_FOUND)
find_package(PythonInterp 2.7 REQUIRED)
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no
WORKING_DIRECTORY ${UNICORN_PREFIX}
)
# ALL makes this custom target build every time
@@ -259,6 +260,8 @@ if (NOT UNICORN_FOUND)
)
unset(UNICORN_LIB_NAME)
endif()
else()
find_package(Unicorn REQUIRED)
endif()
if (UNICORN_FOUND)
@@ -420,7 +423,7 @@ if(ENABLE_QT AND UNIX AND NOT APPLE)
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.desktop"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.svg"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/pixmaps")
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.xml"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
endif()

View File

@@ -1,5 +1,5 @@
These files were generated by the [glad](https://github.com/Dav1dde/glad) OpenGL loader generator and have been checked in as-is. You can re-generate them using glad with the following command:
```
python -m glad --profile core --out-path glad/ --api gl=3.3,gles=3.0
python -m glad --profile core --out-path glad/ --api gl=3.3 --generator=c
```

View File

@@ -26,7 +26,7 @@
/* Khronos platform-specific types and definitions.
*
* $Revision: 23298 $ on $Date: 2013-09-30 17:07:13 -0700 (Mon, 30 Sep 2013) $
* $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
@@ -101,6 +101,8 @@
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
@@ -223,7 +225,7 @@ typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -32,12 +32,24 @@ namespace Log {
CLS(Kernel) \
SUB(Kernel, SVC) \
CLS(Service) \
SUB(Service, SM) \
SUB(Service, ACC) \
SUB(Service, Audio) \
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, Fatal) \
SUB(Service, Friend) \
SUB(Service, FS) \
SUB(Service, GSP) \
SUB(Service, CFG) \
SUB(Service, DSP) \
SUB(Service, HID) \
SUB(Service, LM) \
SUB(Service, NIFM) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, PCTL) \
SUB(Service, SET) \
SUB(Service, SM) \
SUB(Service, Time) \
SUB(Service, VI) \
CLS(HW) \
SUB(HW, Memory) \
SUB(HW, LCD) \

View File

@@ -49,12 +49,24 @@ enum class Class : ClassType {
Kernel_SVC, ///< Kernel system calls
Service, ///< HLE implementation of system services. Each major service
/// should have its own subclass.
Service_SM, ///< The SRV (Service Directory) implementation
Service_FS, ///< The FS (Filesystem) service implementation
Service_GSP, ///< The GSP (GPU control) service
Service_CFG, ///< The CFG (Configuration) service
Service_DSP, ///< The DSP (DSP control) service
Service_ACC, ///< The ACC (Accounts) service
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service
Service_APM, ///< The APM (Performance) service
Service_Audio, ///< The Audio (Audio control) service
Service_Fatal, ///< The Fatal service
Service_Friend, ///< The friend service
Service_FS, ///< The FS (Filesystem) service
Service_HID, ///< The HID (Human interface device) service
Service_LM, ///< The LM (Logger) service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_PCTL, ///< The PCTL (Parental control) service
Service_SET, ///< The SET (Settings) service
Service_SM, ///< The SM (Service manager) service
Service_Time, ///< The time service
Service_VI, ///< The VI (Video interface) service
HW, ///< Low-level hardware emulation
HW_Memory, ///< Memory-map and address translation
HW_LCD, ///< LCD register emulation

View File

@@ -6,21 +6,23 @@ add_library(core STATIC
core.h
core_timing.cpp
core_timing.h
file_sys/archive_backend.cpp
file_sys/archive_backend.h
file_sys/directory_backend.h
file_sys/disk_archive.cpp
file_sys/disk_archive.h
file_sys/directory.h
file_sys/disk_filesystem.cpp
file_sys/disk_filesystem.h
file_sys/errors.h
file_sys/file_backend.h
file_sys/ivfc_archive.cpp
file_sys/ivfc_archive.h
file_sys/filesystem.cpp
file_sys/filesystem.h
file_sys/path_parser.cpp
file_sys/path_parser.h
file_sys/savedata_archive.cpp
file_sys/savedata_archive.h
file_sys/title_metadata.cpp
file_sys/title_metadata.h
file_sys/program_metadata.cpp
file_sys/program_metadata.h
file_sys/romfs_factory.cpp
file_sys/romfs_factory.h
file_sys/romfs_filesystem.cpp
file_sys/romfs_filesystem.h
file_sys/savedata_factory.cpp
file_sys/savedata_factory.h
file_sys/storage.h
frontend/emu_window.cpp
frontend/emu_window.h
frontend/framebuffer_layout.cpp
@@ -32,16 +34,12 @@ add_library(core STATIC
hle/config_mem.h
hle/ipc.h
hle/ipc_helpers.h
hle/kernel/address_arbiter.cpp
hle/kernel/address_arbiter.h
hle/kernel/client_port.cpp
hle/kernel/client_port.h
hle/kernel/client_session.cpp
hle/kernel/client_session.h
hle/kernel/condition_variable.cpp
hle/kernel/condition_variable.h
hle/kernel/domain.cpp
hle/kernel/domain.h
hle/kernel/errors.h
hle/kernel/event.cpp
hle/kernel/event.h
@@ -61,6 +59,8 @@ add_library(core STATIC
hle/kernel/process.h
hle/kernel/resource_limit.cpp
hle/kernel/resource_limit.h
hle/kernel/scheduler.cpp
hle/kernel/scheduler.h
hle/kernel/server_port.cpp
hle/kernel/server_port.h
hle/kernel/server_session.cpp
@@ -71,7 +71,6 @@ add_library(core STATIC
hle/kernel/svc.cpp
hle/kernel/svc.h
hle/kernel/svc_wrap.h
hle/kernel/sync_object.h
hle/kernel/thread.cpp
hle/kernel/thread.h
hle/kernel/timer.cpp
@@ -91,31 +90,83 @@ add_library(core STATIC
hle/service/acc/acc_u0.h
hle/service/am/am.cpp
hle/service/am/am.h
hle/service/am/applet_ae.cpp
hle/service/am/applet_ae.h
hle/service/am/applet_oe.cpp
hle/service/am/applet_oe.h
hle/service/aoc/aoc_u.cpp
hle/service/aoc/aoc_u.h
hle/service/apm/apm.cpp
hle/service/apm/apm.h
hle/service/apm/interface.cpp
hle/service/apm/interface.h
hle/service/audio/audio.cpp
hle/service/audio/audio.h
hle/service/audio/audin_u.cpp
hle/service/audio/audin_u.h
hle/service/audio/audout_u.cpp
hle/service/audio/audout_u.h
hle/service/audio/audrec_u.cpp
hle/service/audio/audrec_u.h
hle/service/audio/audren_u.cpp
hle/service/audio/audren_u.h
hle/service/audio/audren_u.cpp
hle/service/audio/audren_u.h
hle/service/audio/codecctl.cpp
hle/service/audio/codecctl.h
hle/service/fatal/fatal.cpp
hle/service/fatal/fatal.h
hle/service/fatal/fatal_p.cpp
hle/service/fatal/fatal_p.h
hle/service/fatal/fatal_u.cpp
hle/service/fatal/fatal_u.h
hle/service/filesystem/filesystem.cpp
hle/service/filesystem/filesystem.h
hle/service/filesystem/fsp_srv.cpp
hle/service/filesystem/fsp_srv.h
hle/service/friend/friend.cpp
hle/service/friend/friend.h
hle/service/friend/friend_a.cpp
hle/service/friend/friend_a.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
hle/service/lm/lm.cpp
hle/service/lm/lm.h
hle/service/nifm/nifm.cpp
hle/service/nifm/nifm.h
hle/service/nifm/nifm_a.cpp
hle/service/nifm/nifm_a.h
hle/service/nifm/nifm_s.cpp
hle/service/nifm/nifm_s.h
hle/service/nifm/nifm_u.cpp
hle/service/nifm/nifm_u.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/pl_u.cpp
hle/service/ns/pl_u.h
hle/service/nvdrv/devices/nvdevice.h
hle/service/nvdrv/devices/nvdisp_disp0.cpp
hle/service/nvdrv/devices/nvdisp_disp0.h
hle/service/nvdrv/devices/nvhost_as_gpu.cpp
hle/service/nvdrv/devices/nvhost_as_gpu.h
hle/service/nvdrv/devices/nvhost_ctrl.cpp
hle/service/nvdrv/devices/nvhost_ctrl.h
hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
hle/service/nvdrv/devices/nvhost_gpu.cpp
hle/service/nvdrv/devices/nvhost_gpu.h
hle/service/nvdrv/devices/nvmap.cpp
hle/service/nvdrv/devices/nvmap.h
hle/service/nvdrv/interface.cpp
hle/service/nvdrv/interface.h
hle/service/nvdrv/nvdrv.cpp
hle/service/nvdrv/nvdrv.h
hle/service/nvdrv/nvmemp.cpp
hle/service/nvdrv/nvmemp.h
hle/service/nvflinger/buffer_queue.cpp
hle/service/nvflinger/buffer_queue.h
hle/service/nvflinger/nvflinger.cpp
hle/service/nvflinger/nvflinger.h
hle/service/pctl/pctl.cpp
hle/service/pctl/pctl.h
hle/service/pctl/pctl_a.cpp
@@ -124,12 +175,21 @@ add_library(core STATIC
hle/service/service.h
hle/service/set/set.cpp
hle/service/set/set.h
hle/service/set/set_cal.cpp
hle/service/set/set_cal.h
hle/service/set/set_fd.cpp
hle/service/set/set_fd.h
hle/service/set/set_sys.cpp
hle/service/set/set_sys.h
hle/service/set/settings.cpp
hle/service/set/settings.h
hle/service/sm/controller.cpp
hle/service/sm/controller.h
hle/service/sm/sm.cpp
hle/service/sm/sm.h
hle/service/sockets/bsd_u.cpp
hle/service/sockets/bsd_u.h
hle/service/sockets/sfdnsres.cpp
hle/service/sockets/sfdnsres.h
hle/service/sockets/sockets.cpp
hle/service/sockets/sockets.h
@@ -143,6 +203,10 @@ add_library(core STATIC
hle/service/vi/vi.h
hle/service/vi/vi_m.cpp
hle/service/vi/vi_m.h
hle/service/vi/vi_s.cpp
hle/service/vi/vi_s.h
hle/service/vi/vi_u.cpp
hle/service/vi/vi_u.h
hle/shared_page.cpp
hle/shared_page.h
hw/hw.cpp
@@ -163,8 +227,8 @@ add_library(core STATIC
loader/nso.h
memory.cpp
memory.h
memory_hook.h
memory_setup.h
mmio.h
perf_stats.cpp
perf_stats.h
settings.cpp

View File

@@ -25,22 +25,18 @@ public:
VAddr tls_address;
};
/**
* Runs the CPU for the given number of instructions
* @param num_instructions Number of instructions to run
*/
void Run(int num_instructions) {
ExecuteInstructions(num_instructions);
this->num_instructions += num_instructions;
}
/// Runs the CPU until an event happens
virtual void Run() = 0;
/// Step CPU by one instruction
void Step() {
Run(1);
}
virtual void Step() = 0;
/// Maps a backing memory region for the CPU
virtual void MapBackingMemory(VAddr address, size_t size, u8* memory,
Kernel::VMAPermission perms) {}
Kernel::VMAPermission perms) = 0;
/// Unmaps a region of memory that was previously mapped using MapBackingMemory
virtual void UnmapMemory(VAddr address, size_t size) = 0;
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
@@ -122,19 +118,4 @@ public:
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
/// Getter for num_instructions
u64 GetNumInstructions() const {
return num_instructions;
}
protected:
/**
* Executes the given number of instructions
* @param num_instructions Number of instructions to executes
*/
virtual void ExecuteInstructions(int num_instructions) = 0;
private:
u64 num_instructions = 0; ///< Number of instructions executed
};

View File

@@ -6,11 +6,16 @@
#include <memory>
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/config.h>
#include "common/logging/log.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
using Vector = Dynarmic::A64::Vector;
class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
public:
explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
@@ -28,6 +33,9 @@ public:
u64 MemoryRead64(u64 vaddr) override {
return Memory::Read64(vaddr);
}
Vector MemoryRead128(u64 vaddr) override {
return {Memory::Read64(vaddr), Memory::Read64(vaddr + 8)};
}
void MemoryWrite8(u64 vaddr, u8 value) override {
Memory::Write8(vaddr, value);
@@ -41,8 +49,15 @@ public:
void MemoryWrite64(u64 vaddr, u64 value) override {
Memory::Write64(vaddr, value);
}
void MemoryWrite128(u64 vaddr, Vector value) override {
Memory::Write64(vaddr, value[0]);
Memory::Write64(vaddr + 8, value[1]);
}
void InterpreterFallback(u64 pc, size_t num_instructions) override {
LOG_INFO(Core_ARM, "Unicorn fallback @ 0x%" PRIx64 " for %zu instructions (instr = %08x)",
pc, num_instructions, MemoryReadCode(pc));
ARM_Interface::ThreadContext ctx;
parent.SaveContext(ctx);
parent.inner_unicorn.LoadContext(ctx);
@@ -52,12 +67,21 @@ public:
num_interpreted_instructions += num_instructions;
}
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception /*exception*/) override {
ASSERT_MSG(false, "ExceptionRaised(%" PRIx64 ")", pc);
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
switch (exception) {
case Dynarmic::A64::Exception::WaitForInterrupt:
case Dynarmic::A64::Exception::WaitForEvent:
case Dynarmic::A64::Exception::SendEvent:
case Dynarmic::A64::Exception::SendEventLocal:
case Dynarmic::A64::Exception::Yield:
return;
default:
ASSERT_MSG(false, "ExceptionRaised(exception = %zu, pc = %" PRIx64 ")",
static_cast<size_t>(exception), pc);
}
}
void CallSVC(u32 swi) override {
printf("svc %x\n", swi);
Kernel::CallSVC(swi);
}
@@ -71,19 +95,49 @@ public:
u64 GetTicksRemaining() override {
return ticks_remaining;
}
u64 GetCNTPCT() override {
return CoreTiming::GetTicks();
}
ARM_Dynarmic& parent;
size_t ticks_remaining = 0;
size_t num_interpreted_instructions = 0;
u64 tpidrr0_el0 = 0;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
};
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_Callbacks>& cb) {
const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
Dynarmic::A64::UserConfig config;
config.callbacks = cb.get();
config.tpidrro_el0 = &cb->tpidrro_el0;
config.tpidr_el0 = &cb->tpidr_el0;
config.dczid_el0 = 4;
config.ctr_el0 = 0x8444c004;
config.page_table = reinterpret_cast<void**>(page_table);
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
config.silently_mirror_page_table = false;
return std::make_unique<Dynarmic::A64::Jit>(config);
}
void ARM_Dynarmic::Run() {
ASSERT(Memory::GetCurrentPageTable() == current_page_table);
jit->Run();
}
void ARM_Dynarmic::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
ARM_Dynarmic::ARM_Dynarmic()
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)),
jit(Dynarmic::A64::UserConfig{cb.get()}) {
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), jit(MakeJit(cb)) {
ARM_Interface::ThreadContext ctx;
inner_unicorn.SaveContext(ctx);
LoadContext(ctx);
PageTableChanged();
}
ARM_Dynarmic::~ARM_Dynarmic() = default;
@@ -93,28 +147,32 @@ void ARM_Dynarmic::MapBackingMemory(u64 address, size_t size, u8* memory,
inner_unicorn.MapBackingMemory(address, size, memory, perms);
}
void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) {
inner_unicorn.UnmapMemory(address, size);
}
void ARM_Dynarmic::SetPC(u64 pc) {
jit.SetPC(pc);
jit->SetPC(pc);
}
u64 ARM_Dynarmic::GetPC() const {
return jit.GetPC();
return jit->GetPC();
}
u64 ARM_Dynarmic::GetReg(int index) const {
return jit.GetRegister(index);
return jit->GetRegister(index);
}
void ARM_Dynarmic::SetReg(int index, u64 value) {
jit.SetRegister(index, value);
jit->SetRegister(index, value);
}
u128 ARM_Dynarmic::GetExtReg(int index) const {
return jit.GetVector(index);
return jit->GetVector(index);
}
void ARM_Dynarmic::SetExtReg(int index, u128 value) {
jit.SetVector(index, value);
jit->SetVector(index, value);
}
u32 ARM_Dynarmic::GetVFPReg(int /*index*/) const {
@@ -127,58 +185,52 @@ void ARM_Dynarmic::SetVFPReg(int /*index*/, u32 /*value*/) {
}
u32 ARM_Dynarmic::GetCPSR() const {
return jit.GetPstate();
return jit->GetPstate();
}
void ARM_Dynarmic::SetCPSR(u32 cpsr) {
jit.SetPstate(cpsr);
jit->SetPstate(cpsr);
}
u64 ARM_Dynarmic::GetTlsAddress() const {
return cb->tpidrr0_el0;
return cb->tpidrro_el0;
}
void ARM_Dynarmic::SetTlsAddress(u64 address) {
cb->tpidrr0_el0 = address;
}
void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
cb->ticks_remaining = num_instructions;
jit.Run();
CoreTiming::AddTicks(num_instructions - cb->num_interpreted_instructions);
cb->num_interpreted_instructions = 0;
cb->tpidrro_el0 = address;
}
void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
ctx.cpu_registers = jit.GetRegisters();
ctx.sp = jit.GetSP();
ctx.pc = jit.GetPC();
ctx.cpsr = jit.GetPstate();
ctx.fpu_registers = jit.GetVectors();
ctx.fpscr = jit.GetFpcr();
ctx.tls_address = cb->tpidrr0_el0;
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
ctx.pc = jit->GetPC();
ctx.cpsr = jit->GetPstate();
ctx.fpu_registers = jit->GetVectors();
ctx.fpscr = jit->GetFpcr();
ctx.tls_address = cb->tpidrro_el0;
}
void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
jit.SetRegisters(ctx.cpu_registers);
jit.SetSP(ctx.sp);
jit.SetPC(ctx.pc);
jit.SetPstate(static_cast<u32>(ctx.cpsr));
jit.SetVectors(ctx.fpu_registers);
jit.SetFpcr(static_cast<u32>(ctx.fpscr));
cb->tpidrr0_el0 = ctx.tls_address;
jit->SetRegisters(ctx.cpu_registers);
jit->SetSP(ctx.sp);
jit->SetPC(ctx.pc);
jit->SetPstate(static_cast<u32>(ctx.cpsr));
jit->SetVectors(ctx.fpu_registers);
jit->SetFpcr(static_cast<u32>(ctx.fpscr));
cb->tpidrro_el0 = ctx.tls_address;
}
void ARM_Dynarmic::PrepareReschedule() {
if (jit.IsExecuting()) {
jit.HaltExecution();
if (jit->IsExecuting()) {
jit->HaltExecution();
}
}
void ARM_Dynarmic::ClearInstructionCache() {
jit.ClearCache();
jit->ClearCache();
}
void ARM_Dynarmic::PageTableChanged() {
UNIMPLEMENTED();
jit = MakeJit(cb);
current_page_table = Memory::GetCurrentPageTable();
}

View File

@@ -19,7 +19,7 @@ public:
void MapBackingMemory(VAddr address, size_t size, u8* memory,
Kernel::VMAPermission perms) override;
void UnmapMemory(u64 address, size_t size) override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetReg(int index) const override;
@@ -29,6 +29,8 @@ public:
u32 GetVFPReg(int index) const override;
void SetVFPReg(int index, u32 value) override;
u32 GetCPSR() const override;
void Run() override;
void Step() override;
void SetCPSR(u32 cpsr) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
@@ -37,7 +39,6 @@ public:
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
void ExecuteInstructions(int num_instructions) override;
void ClearInstructionCache() override;
void PageTableChanged() override;
@@ -45,6 +46,8 @@ public:
private:
friend class ARM_Dynarmic_Callbacks;
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
Dynarmic::A64::Jit jit;
std::unique_ptr<Dynarmic::A64::Jit> jit;
ARM_Unicorn inner_unicorn;
Memory::PageTable* current_page_table = nullptr;
};

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <unicorn/arm64.h>
#include "common/assert.h"
#include "common/microprofile.h"
@@ -52,7 +53,8 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
void* user_data) {
ARM_Interface::ThreadContext ctx{};
Core::CPU().SaveContext(ctx);
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%llx", addr);
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%lx, pc=0x%lx, lr=0x%lx", addr,
ctx.pc, ctx.cpu_registers[30]);
return {};
}
@@ -76,6 +78,10 @@ void ARM_Unicorn::MapBackingMemory(VAddr address, size_t size, u8* memory,
CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory));
}
void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) {
CHECKED(uc_mem_unmap(uc, address, size));
}
void ARM_Unicorn::SetPC(u64 pc) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
}
@@ -148,6 +154,14 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
}
void ARM_Unicorn::Run() {
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
}
void ARM_Unicorn::Step() {
ExecuteInstructions(1);
}
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {

View File

@@ -14,6 +14,7 @@ public:
~ARM_Unicorn();
void MapBackingMemory(VAddr address, size_t size, u8* memory,
Kernel::VMAPermission perms) override;
void UnmapMemory(VAddr address, size_t size) override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetReg(int index) const override;
@@ -29,7 +30,9 @@ public:
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
void ExecuteInstructions(int num_instructions) override;
void ExecuteInstructions(int num_instructions);
void Run() override;
void Step() override;
void ClearInstructionCache() override;
void PageTableChanged() override{};

View File

@@ -26,7 +26,7 @@ namespace Core {
/*static*/ System System::s_instance;
System::ResultStatus System::RunLoop(int tight_loop) {
System::ResultStatus System::RunLoop(bool tight_loop) {
status = ResultStatus::Success;
if (!cpu_core) {
return ResultStatus::ErrorNotInitialized;
@@ -40,7 +40,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
GDBStub::SetCpuStepFlag(false);
tight_loop = 1;
tight_loop = false;
} else {
return ResultStatus::Success;
}
@@ -56,7 +56,11 @@ System::ResultStatus System::RunLoop(int tight_loop) {
PrepareReschedule();
} else {
CoreTiming::Advance();
cpu_core->Run(tight_loop);
if (tight_loop) {
cpu_core->Run();
} else {
cpu_core->Step();
}
}
HW::Update();
@@ -66,7 +70,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
}
System::ResultStatus System::SingleStep() {
return RunLoop(1);
return RunLoop(false);
}
System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) {
@@ -95,14 +99,15 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
ResultStatus init_result{Init(emu_window, system_mode.first.get())};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result);
LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!",
static_cast<int>(init_result));
System::Shutdown();
return init_result;
}
const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)};
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
if (Loader::ResultStatus::Success != load_result) {
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result);
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", static_cast<int>(load_result));
System::Shutdown();
switch (load_result) {
@@ -133,32 +138,38 @@ void System::Reschedule() {
}
reschedule_pending = false;
Kernel::Reschedule();
Core::System::GetInstance().Scheduler().Reschedule();
}
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
current_process = Kernel::Process::Create("main");
switch (Settings::values.cpu_core) {
case Settings::CpuCore::Unicorn:
cpu_core = std::make_unique<ARM_Unicorn>();
cpu_core = std::make_shared<ARM_Unicorn>();
break;
case Settings::CpuCore::Dynarmic:
default:
#ifdef ARCHITECTURE_x86_64
cpu_core = std::make_unique<ARM_Dynarmic>();
cpu_core = std::make_shared<ARM_Dynarmic>();
#else
cpu_core = std::make_unique<ARM_Unicorn>();
cpu_core = std::make_shared<ARM_Unicorn>();
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
break;
}
gpu_core = std::make_unique<Tegra::GPU>();
telemetry_session = std::make_unique<Core::TelemetrySession>();
CoreTiming::Init();
HW::Init();
Kernel::Init(system_mode);
scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get());
Service::Init();
GDBStub::Init();
@@ -186,15 +197,18 @@ void System::Shutdown() {
perf_results.frametime * 1000.0);
// Shutdown emulation session
GDBStub::Shutdown();
VideoCore::Shutdown();
GDBStub::Shutdown();
Service::Shutdown();
scheduler = nullptr;
Kernel::Shutdown();
HW::Shutdown();
CoreTiming::Shutdown();
cpu_core = nullptr;
app_loader = nullptr;
telemetry_session = nullptr;
gpu_core = nullptr;
cpu_core = nullptr;
CoreTiming::Shutdown();
app_loader = nullptr;
LOG_DEBUG(Core, "Shutdown OK");
}

View File

@@ -7,10 +7,13 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.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 "video_core/gpu.h"
class EmuWindow;
class ARM_Interface;
@@ -50,14 +53,14 @@ public:
* is not required to do a full dispatch with each instruction. NOTE: the number of instructions
* requested is not guaranteed to run, as this will be interrupted preemptively if a hardware
* update is requested (e.g. on a thread switch).
* @param tight_loop Number of instructions to execute.
* @return Result status, indicating whethor or not the operation succeeded.
* @param tight_loop If false, the CPU single-steps.
* @return Result status, indicating whether or not the operation succeeded.
*/
ResultStatus RunLoop(int tight_loop = 100000);
ResultStatus RunLoop(bool tight_loop = true);
/**
* Step the CPU one instruction
* @return Result status, indicating whethor or not the operation succeeded.
* @return Result status, indicating whether or not the operation succeeded.
*/
ResultStatus SingleStep();
@@ -102,6 +105,18 @@ public:
return *cpu_core;
}
Tegra::GPU& GPU() {
return *gpu_core;
}
Kernel::Scheduler& Scheduler() {
return *scheduler;
}
Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
return current_process;
}
PerfStats perf_stats;
FrameLimiter frame_limiter;
@@ -135,8 +150,11 @@ private:
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
///< ARM11 CPU core
std::unique_ptr<ARM_Interface> cpu_core;
std::shared_ptr<ARM_Interface> cpu_core;
std::unique_ptr<Kernel::Scheduler> scheduler;
std::unique_ptr<Tegra::GPU> gpu_core;
Kernel::SharedPtr<Kernel::Process> current_process;
/// When true, signals that a reschedule should happen
bool reschedule_pending{};
@@ -158,4 +176,8 @@ inline TelemetrySession& Telemetry() {
return System::GetInstance().TelemetrySession();
}
inline Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
return System::GetInstance().CurrentProcess();
}
} // namespace Core

View File

@@ -1,4 +1,4 @@
// Copyright 2014 Citra Emulator Project
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

View File

@@ -1,99 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstdio>
#include <memory>
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/disk_archive.h"
#include "core/file_sys/errors.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
if (!mode.read_flag)
return ERROR_INVALID_OPEN_FLAGS;
file->Seek(offset, SEEK_SET);
return MakeResult<size_t>(file->ReadBytes(buffer, length));
}
ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush,
const u8* buffer) const {
if (!mode.write_flag)
return ERROR_INVALID_OPEN_FLAGS;
file->Seek(offset, SEEK_SET);
size_t written = file->WriteBytes(buffer, length);
if (flush)
file->Flush();
return MakeResult<size_t>(written);
}
u64 DiskFile::GetSize() const {
return file->GetSize();
}
bool DiskFile::SetSize(const u64 size) const {
file->Resize(size);
file->Flush();
return true;
}
bool DiskFile::Close() const {
return file->Close();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
DiskDirectory::DiskDirectory(const std::string& path) : directory() {
unsigned size = FileUtil::ScanDirectoryTree(path, directory);
directory.size = size;
directory.isDirectory = true;
children_iterator = directory.children.begin();
}
u32 DiskDirectory::Read(const u32 count, Entry* entries) {
u32 entries_read = 0;
while (entries_read < count && children_iterator != directory.children.cend()) {
const FileUtil::FSTEntry& file = *children_iterator;
const std::string& filename = file.virtualName;
Entry& entry = entries[entries_read];
LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size,
file.isDirectory);
// TODO(Link Mauve): use a proper conversion to UTF-16.
for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
entry.filename[j] = filename[j];
if (!filename[j])
break;
}
FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
entry.is_directory = file.isDirectory;
entry.is_hidden = (filename[0] == '.');
entry.is_read_only = 0;
entry.file_size = file.size;
// We emulate a SD card where the archive bit has never been cleared, as it would be on
// most user SD cards.
// Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
// file bit.
entry.is_archive = !file.isDirectory;
++entries_read;
++children_iterator;
}
return entries_read;
}
} // namespace FileSys

View File

@@ -1,68 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <memory>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/archive_backend.h"
#include "core/file_sys/directory_backend.h"
#include "core/file_sys/file_backend.h"
#include "core/hle/result.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
class DiskFile : public FileBackend {
public:
DiskFile(FileUtil::IOFile&& file_, const Mode& mode_)
: file(new FileUtil::IOFile(std::move(file_))) {
mode.hex = mode_.hex;
}
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
u64 GetSize() const override;
bool SetSize(u64 size) const override;
bool Close() const override;
void Flush() const override {
file->Flush();
}
protected:
Mode mode;
std::unique_ptr<FileUtil::IOFile> file;
};
class DiskDirectory : public DirectoryBackend {
public:
DiskDirectory(const std::string& path);
~DiskDirectory() override {
Close();
}
u32 Read(const u32 count, Entry* entries) override;
bool Close() const override {
return true;
}
protected:
u32 total_entries_in_directory;
FileUtil::FSTEntry directory;
// We need to remember the last entry we returned, so a subsequent call to Read will continue
// from the next one. This iterator will always point to the next unread entry.
std::vector<FileUtil::FSTEntry>::iterator children_iterator;
};
} // namespace FileSys

View File

@@ -0,0 +1,146 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <memory>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/file_sys/disk_filesystem.h"
#include "core/file_sys/errors.h"
namespace FileSys {
std::string Disk_FileSystem::GetName() const {
return "Disk";
}
ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path,
Mode mode) const {
ASSERT_MSG(mode == Mode::Read || mode == Mode::Write, "Other file modes are not supported");
std::string full_path = base_directory + path;
auto file = std::make_shared<FileUtil::IOFile>(full_path, mode == Mode::Read ? "rb" : "wb");
if (!file->IsOpen()) {
return ERROR_PATH_NOT_FOUND;
}
return MakeResult<std::unique_ptr<StorageBackend>>(
std::make_unique<Disk_Storage>(std::move(file)));
}
ResultCode Disk_FileSystem::DeleteFile(const Path& path) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultCode Disk_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
std::string full_path = base_directory + path;
if (size == 0) {
FileUtil::CreateEmptyFile(full_path);
return RESULT_SUCCESS;
}
FileUtil::IOFile file(full_path, "wb");
// Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
// We do this by seeking to the right size, then writing a single null byte.
if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
return RESULT_SUCCESS;
}
LOG_ERROR(Service_FS, "Too large file");
// TODO(Subv): Find out the correct error code
return ResultCode(-1);
}
ResultCode Disk_FileSystem::CreateDirectory(const Path& path) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory(
const Path& path) const {
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<Disk_Directory>());
}
u64 Disk_FileSystem::GetFreeSpaceSize() const {
LOG_WARNING(Service_FS, "(STUBBED) called");
return 0;
}
ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& path) const {
std::string full_path = base_directory + path;
if (!FileUtil::Exists(full_path)) {
return ERROR_PATH_NOT_FOUND;
}
// TODO(Subv): Find out the EntryType values
UNIMPLEMENTED_MSG("Unimplemented GetEntryType");
}
ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
file->Seek(offset, SEEK_SET);
return MakeResult<size_t>(file->ReadBytes(buffer, length));
}
ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush,
const u8* buffer) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
file->Seek(offset, SEEK_SET);
size_t written = file->WriteBytes(buffer, length);
if (flush) {
file->Flush();
}
return MakeResult<size_t>(written);
}
u64 Disk_Storage::GetSize() const {
return file->GetSize();
}
bool Disk_Storage::SetSize(const u64 size) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
return false;
}
u32 Disk_Directory::Read(const u32 count, Entry* entries) {
LOG_WARNING(Service_FS, "(STUBBED) called");
return 0;
}
bool Disk_Directory::Close() const {
LOG_WARNING(Service_FS, "(STUBBED) called");
return true;
}
} // namespace FileSys

View File

@@ -0,0 +1,66 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <memory>
#include <string>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/storage.h"
#include "core/hle/result.h"
namespace FileSys {
class Disk_FileSystem : public FileSystemBackend {
public:
explicit Disk_FileSystem(std::string base_directory)
: base_directory(std::move(base_directory)) {}
std::string GetName() const override;
ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
Mode mode) const override;
ResultCode DeleteFile(const Path& path) const override;
ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
ResultCode DeleteDirectory(const Path& path) const override;
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
ResultCode CreateFile(const std::string& path, u64 size) const override;
ResultCode CreateDirectory(const Path& path) const override;
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
u64 GetFreeSpaceSize() const override;
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
protected:
std::string base_directory;
};
class Disk_Storage : public StorageBackend {
public:
Disk_Storage(std::shared_ptr<FileUtil::IOFile> file) : file(std::move(file)) {}
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
u64 GetSize() const override;
bool SetSize(u64 size) const override;
bool Close() const override {
return false;
}
void Flush() const override {}
private:
std::shared_ptr<FileUtil::IOFile> file;
};
class Disk_Directory : public DirectoryBackend {
public:
u32 Read(const u32 count, Entry* entries) override;
bool Close() const override;
};
} // namespace FileSys

View File

@@ -10,36 +10,17 @@ namespace FileSys {
namespace ErrCodes {
enum {
RomFSNotFound = 100,
ArchiveNotMounted = 101,
FileNotFound = 112,
PathNotFound = 113,
GameCardNotInserted = 141,
NotFound = 120,
FileAlreadyExists = 180,
DirectoryAlreadyExists = 185,
AlreadyExists = 190,
InvalidOpenFlags = 230,
DirectoryNotEmpty = 240,
NotAFile = 250,
NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
ExeFSSectionNotFound = 567,
CommandNotAllowed = 630,
InvalidReadFlag = 700,
InvalidPath = 702,
WriteBeyondEnd = 705,
UnsupportedOpenFlags = 760,
IncorrectExeFSReadSize = 761,
UnexpectedFileOrDirectory = 770,
NotFound = 1,
};
}
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
// TODO(bunnei): Replace these with correct errors for Switch OS
constexpr ResultCode ERROR_INVALID_PATH(ResultCode(-1));
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ResultCode(-1));
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ResultCode(-1));
constexpr ResultCode ERROR_FILE_NOT_FOUND(ResultCode(-1));
constexpr ResultCode ERROR_PATH_NOT_FOUND(ResultCode(-1));
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ResultCode(-1));
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ResultCode(-1));
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ResultCode(-1));

View File

@@ -1,4 +1,4 @@
// Copyright 2015 Citra Emulator Project
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -7,7 +7,7 @@
#include <sstream>
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/archive_backend.h"
#include "core/file_sys/filesystem.h"
#include "core/memory.h"
namespace FileSys {

View File

@@ -1,4 +1,4 @@
// Copyright 2014 Citra Emulator Project
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -15,7 +15,7 @@
namespace FileSys {
class FileBackend;
class StorageBackend;
class DirectoryBackend;
// Path string type
@@ -27,11 +27,14 @@ enum LowPathType : u32 {
Wchar = 4,
};
union Mode {
u32 hex;
BitField<0, 1, u32> read_flag;
BitField<1, 1, u32> write_flag;
BitField<2, 1, u32> create_flag;
enum EntryType : u32 {
Directory = 0,
File = 1,
};
enum class Mode : u32 {
Read = 1,
Write = 2,
};
class Path {
@@ -71,9 +74,9 @@ struct ArchiveFormatInfo {
};
static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD");
class ArchiveBackend : NonCopyable {
class FileSystemBackend : NonCopyable {
public:
virtual ~ArchiveBackend() {}
virtual ~FileSystemBackend() {}
/**
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
@@ -81,13 +84,12 @@ public:
virtual std::string GetName() const = 0;
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or error code
* Create a file specified by its path
* @param path Path relative to the Archive
* @param size The size of the new file, filled with zeroes
* @return Result of the operation
*/
virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
const Mode& mode) const = 0;
virtual ResultCode CreateFile(const std::string& path, u64 size) const = 0;
/**
* Delete a file specified by its path
@@ -97,12 +99,11 @@ public:
virtual ResultCode DeleteFile(const Path& path) const = 0;
/**
* Rename a File specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0;
virtual ResultCode CreateDirectory(const Path& path) const = 0;
/**
* Delete a directory specified by its path
@@ -119,19 +120,12 @@ public:
virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0;
/**
* Create a file specified by its path
* @param path Path relative to the Archive
* @param size The size of the new file, filled with zeroes
* Rename a File specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* @return Result of the operation
*/
virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
/**
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
virtual ResultCode CreateDirectory(const Path& path) const = 0;
virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0;
/**
* Rename a Directory specified by its path
@@ -141,6 +135,15 @@ public:
*/
virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0;
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or error code
*/
virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
Mode mode) const = 0;
/**
* Open a directory specified by its path
* @param path Path relative to the archive
@@ -152,12 +155,18 @@ public:
* Get the free space
* @return The number of free bytes in the archive
*/
virtual u64 GetFreeBytes() const = 0;
virtual u64 GetFreeSpaceSize() const = 0;
/**
* Get the type of the specified path
* @return The type of the specified path or error code
*/
virtual ResultVal<EntryType> GetEntryType(const std::string& path) const = 0;
};
class ArchiveFactory : NonCopyable {
class FileSystemFactory : NonCopyable {
public:
virtual ~ArchiveFactory() {}
virtual ~FileSystemFactory() {}
/**
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
@@ -169,15 +178,14 @@ public:
* @param path Path to the archive
* @return An ArchiveBackend corresponding operating specified archive path.
*/
virtual ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) = 0;
virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0;
/**
* Deletes the archive contents and then re-creates the base folder
* @param path Path to the archive
* @param format_info Format information for the new archive
* @return ResultCode of the operation, 0 on success
*/
virtual ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) = 0;
virtual ResultCode Format(const Path& path) = 0;
/**
* Retrieves the format info about the archive with the specified path

View File

@@ -1,110 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <memory>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/file_sys/ivfc_archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
std::string IVFCArchive::GetName() const {
return "IVFC";
}
ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path,
const Mode& mode) const {
return MakeResult<std::unique_ptr<FileBackend>>(
std::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
}
ResultCode IVFCArchive::DeleteFile(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).",
GetName().c_str());
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
GetName().c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode IVFCArchive::DeleteDirectory(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
GetName().c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
GetName().c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).",
GetName().c_str());
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultCode IVFCArchive::CreateDirectory(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).",
GetName().c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
GetName().c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const {
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>());
}
u64 IVFCArchive::GetFreeBytes() const {
LOG_WARNING(Service_FS, "Attempted to get the free space in an IVFC archive");
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const {
LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
romfs_file->Seek(data_offset + offset, SEEK_SET);
size_t read_length = (size_t)std::min((u64)length, data_size - offset);
return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length));
}
ResultVal<size_t> IVFCFile::Write(const u64 offset, const size_t length, const bool flush,
const u8* buffer) const {
LOG_ERROR(Service_FS, "Attempted to write to IVFC file");
// TODO(Subv): Find error code
return MakeResult<size_t>(0);
}
u64 IVFCFile::GetSize() const {
return data_size;
}
bool IVFCFile::SetSize(const u64 size) const {
LOG_ERROR(Service_FS, "Attempted to set the size of an IVFC file");
return false;
}
} // namespace FileSys

View File

@@ -6,7 +6,7 @@
#include <string>
#include <vector>
#include "core/file_sys/archive_backend.h"
#include "core/file_sys/filesystem.h"
namespace FileSys {

View File

@@ -0,0 +1,114 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/program_metadata.h"
#include "core/loader/loader.h"
namespace FileSys {
Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) {
FileUtil::IOFile file(file_path, "rb");
if (!file.IsOpen())
return Loader::ResultStatus::Error;
std::vector<u8> file_data(file.GetSize());
if (!file.ReadBytes(file_data.data(), file_data.size()))
return Loader::ResultStatus::Error;
Loader::ResultStatus result = Load(file_data);
if (result != Loader::ResultStatus::Success)
LOG_ERROR(Service_FS, "Failed to load NPDM from file %s!", file_path.c_str());
return result;
}
Loader::ResultStatus ProgramMetadata::Load(const std::vector<u8> file_data, size_t offset) {
size_t total_size = static_cast<size_t>(file_data.size() - offset);
if (total_size < sizeof(Header))
return Loader::ResultStatus::Error;
size_t header_offset = offset;
memcpy(&npdm_header, &file_data[offset], sizeof(Header));
size_t aci_offset = header_offset + npdm_header.aci_offset;
size_t acid_offset = header_offset + npdm_header.acid_offset;
memcpy(&aci_header, &file_data[aci_offset], sizeof(AciHeader));
memcpy(&acid_header, &file_data[acid_offset], sizeof(AcidHeader));
size_t fac_offset = acid_offset + acid_header.fac_offset;
size_t fah_offset = aci_offset + aci_header.fah_offset;
memcpy(&acid_file_access, &file_data[fac_offset], sizeof(FileAccessControl));
memcpy(&aci_file_access, &file_data[fah_offset], sizeof(FileAccessHeader));
return Loader::ResultStatus::Success;
}
bool ProgramMetadata::Is64BitProgram() const {
return npdm_header.has_64_bit_instructions;
}
ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const {
return npdm_header.address_space_type;
}
u8 ProgramMetadata::GetMainThreadPriority() const {
return npdm_header.main_thread_priority;
}
u8 ProgramMetadata::GetMainThreadCore() const {
return npdm_header.main_thread_cpu;
}
u32 ProgramMetadata::GetMainThreadStackSize() const {
return npdm_header.main_stack_size;
}
u64 ProgramMetadata::GetTitleID() const {
return aci_header.title_id;
}
u64 ProgramMetadata::GetFilesystemPermissions() const {
return aci_file_access.permissions;
}
void ProgramMetadata::Print() const {
LOG_DEBUG(Service_FS, "Magic: %.4s", npdm_header.magic.data());
LOG_DEBUG(Service_FS, "Main thread priority: 0x%02x", npdm_header.main_thread_priority);
LOG_DEBUG(Service_FS, "Main thread core: %u", npdm_header.main_thread_cpu);
LOG_DEBUG(Service_FS, "Main thread stack size: 0x%x bytes", npdm_header.main_stack_size);
LOG_DEBUG(Service_FS, "Process category: %u", npdm_header.process_category);
LOG_DEBUG(Service_FS, "Flags: %02x", npdm_header.flags);
LOG_DEBUG(Service_FS, " > 64-bit instructions: %s",
npdm_header.has_64_bit_instructions ? "YES" : "NO");
auto address_space = "Unknown";
switch (npdm_header.address_space_type) {
case ProgramAddressSpaceType::Is64Bit:
address_space = "64-bit";
break;
case ProgramAddressSpaceType::Is32Bit:
address_space = "32-bit";
break;
}
LOG_DEBUG(Service_FS, " > Address space: %s\n", address_space);
// Begin ACID printing (potential perms, signed)
LOG_DEBUG(Service_FS, "Magic: %.4s", acid_header.magic.data());
LOG_DEBUG(Service_FS, "Flags: %02x", acid_header.flags);
LOG_DEBUG(Service_FS, " > Is Retail: %s", acid_header.is_retail ? "YES" : "NO");
LOG_DEBUG(Service_FS, "Title ID Min: %016" PRIX64, acid_header.title_id_min);
LOG_DEBUG(Service_FS, "Title ID Max: %016" PRIX64, acid_header.title_id_max);
LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", acid_file_access.permissions);
// Begin ACI0 printing (actual perms, unsigned)
LOG_DEBUG(Service_FS, "Magic: %.4s", aci_header.magic.data());
LOG_DEBUG(Service_FS, "Title ID: %016" PRIX64, aci_header.title_id);
LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", aci_file_access.permissions);
}
} // namespace FileSys

View File

@@ -0,0 +1,154 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
namespace Loader {
enum class ResultStatus;
}
namespace FileSys {
enum class ProgramAddressSpaceType : u8 {
Is64Bit = 1,
Is32Bit = 2,
};
enum class ProgramFilePermission : u64 {
MountContent = 1ULL << 0,
SaveDataBackup = 1ULL << 5,
SdCard = 1ULL << 21,
Calibration = 1ULL << 34,
Bit62 = 1ULL << 62,
Everything = 1ULL << 63,
};
/**
* Helper which implements an interface to parse Program Description Metadata (NPDM)
* Data can either be loaded from a file path or with data and an offset into it.
*/
class ProgramMetadata {
public:
Loader::ResultStatus Load(const std::string& file_path);
Loader::ResultStatus Load(const std::vector<u8> file_data, size_t offset = 0);
bool Is64BitProgram() const;
ProgramAddressSpaceType GetAddressSpaceType() const;
u8 GetMainThreadPriority() const;
u8 GetMainThreadCore() const;
u32 GetMainThreadStackSize() const;
u64 GetTitleID() const;
u64 GetFilesystemPermissions() const;
void Print() const;
private:
struct Header {
std::array<char, 4> magic;
std::array<u8, 8> reserved;
union {
u8 flags;
BitField<0, 1, u8> has_64_bit_instructions;
BitField<1, 3, ProgramAddressSpaceType> address_space_type;
BitField<4, 4, u8> reserved_2;
};
u8 reserved_3;
u8 main_thread_priority;
u8 main_thread_cpu;
std::array<u8, 8> reserved_4;
u32_le process_category;
u32_le main_stack_size;
std::array<u8, 0x10> application_name;
std::array<u8, 0x40> reserved_5;
u32_le aci_offset;
u32_le aci_size;
u32_le acid_offset;
u32_le acid_size;
};
static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
struct AcidHeader {
std::array<u8, 0x100> signature;
std::array<u8, 0x100> nca_modulus;
std::array<char, 4> magic;
u32_le nca_size;
std::array<u8, 0x4> reserved;
union {
u32 flags;
BitField<0, 1, u32> is_retail;
BitField<1, 31, u32> flags_unk;
};
u64_le title_id_min;
u64_le title_id_max;
u32_le fac_offset;
u32_le fac_size;
u32_le sac_offset;
u32_le sac_size;
u32_le kac_offset;
u32_le kac_size;
INSERT_PADDING_BYTES(0x8);
};
static_assert(sizeof(AcidHeader) == 0x240, "ACID header structure size is wrong");
struct AciHeader {
std::array<char, 4> magic;
std::array<u8, 0xC> reserved;
u64_le title_id;
INSERT_PADDING_BYTES(0x8);
u32_le fah_offset;
u32_le fah_size;
u32_le sac_offset;
u32_le sac_size;
u32_le kac_offset;
u32_le kac_size;
INSERT_PADDING_BYTES(0x8);
};
static_assert(sizeof(AciHeader) == 0x40, "ACI0 header structure size is wrong");
#pragma pack(push, 1)
struct FileAccessControl {
u8 version;
INSERT_PADDING_BYTES(3);
u64_le permissions;
std::array<u8, 0x20> unknown;
};
static_assert(sizeof(FileAccessControl) == 0x2C, "FS access control structure size is wrong");
struct FileAccessHeader {
u8 version;
INSERT_PADDING_BYTES(3);
u64_le permissions;
u32_le unk_offset;
u32_le unk_size;
u32_le unk_offset_2;
u32_le unk_size_2;
};
static_assert(sizeof(FileAccessHeader) == 0x1C, "FS access header structure size is wrong");
#pragma pack(pop)
Header npdm_header;
AciHeader aci_header;
AcidHeader acid_header;
FileAccessControl acid_file_access;
FileAccessHeader aci_file_access;
};
} // namespace FileSys

View File

@@ -0,0 +1,38 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <memory>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/romfs_filesystem.h"
namespace FileSys {
RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) {
// Load the RomFS from the app
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) {
LOG_ERROR(Service_FS, "Unable to read RomFS!");
}
}
ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) {
auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size);
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
}
ResultCode RomFS_Factory::Format(const Path& path) {
LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
}
ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const {
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
}
} // namespace FileSys

View File

@@ -0,0 +1,35 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/filesystem.h"
#include "core/hle/result.h"
#include "core/loader/loader.h"
namespace FileSys {
/// File system interface to the RomFS archive
class RomFS_Factory final : public FileSystemFactory {
public:
explicit RomFS_Factory(Loader::AppLoader& app_loader);
std::string GetName() const override {
return "ArchiveFactory_RomFS";
}
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::shared_ptr<FileUtil::IOFile> romfs_file;
u64 data_offset;
u64 data_size;
};
} // namespace FileSys

View File

@@ -0,0 +1,112 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <memory>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/file_sys/romfs_filesystem.h"
namespace FileSys {
std::string RomFS_FileSystem::GetName() const {
return "RomFS";
}
ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std::string& path,
Mode mode) const {
return MakeResult<std::unique_ptr<StorageBackend>>(
std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size));
}
ResultCode RomFS_FileSystem::DeleteFile(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive (%s).",
GetName().c_str());
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).",
GetName().c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).",
GetName().c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).",
GetName().c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const {
LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive (%s).",
GetName().c_str());
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::CreateDirectory(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).",
GetName().c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).",
GetName().c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory(
const Path& path) const {
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>());
}
u64 RomFS_FileSystem::GetFreeSpaceSize() const {
LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive");
return 0;
}
ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const {
LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path %s).", path.c_str());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
romfs_file->Seek(data_offset + offset, SEEK_SET);
size_t read_length = (size_t)std::min((u64)length, data_size - offset);
return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length));
}
ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush,
const u8* buffer) const {
LOG_ERROR(Service_FS, "Attempted to write to ROMFS file");
// TODO(Subv): Find error code
return MakeResult<size_t>(0);
}
u64 RomFS_Storage::GetSize() const {
return data_size;
}
bool RomFS_Storage::SetSize(const u64 size) const {
LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file");
return false;
}
} // namespace FileSys

View File

@@ -1,4 +1,4 @@
// Copyright 2014 Citra Emulator Project
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -10,39 +10,37 @@
#include <vector>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/archive_backend.h"
#include "core/file_sys/directory_backend.h"
#include "core/file_sys/file_backend.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/storage.h"
#include "core/hle/result.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/**
* Helper which implements an interface to deal with IVFC images used in some archives
* This should be subclassed by concrete archive types, which will provide the
* input data (load the raw IVFC archive) and override any required methods
* Helper which implements an interface to deal with Switch .istorage ROMFS images used in some
* archives This should be subclassed by concrete archive types, which will provide the input data
* (load the raw ROMFS archive) and override any required methods
*/
class IVFCArchive : public ArchiveBackend {
class RomFS_FileSystem : public FileSystemBackend {
public:
IVFCArchive(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
RomFS_FileSystem(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
: romfs_file(file), data_offset(offset), data_size(size) {}
std::string GetName() const override;
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
const Mode& mode) const override;
ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
Mode mode) const override;
ResultCode DeleteFile(const Path& path) const override;
ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
ResultCode DeleteDirectory(const Path& path) const override;
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
ResultCode CreateFile(const Path& path, u64 size) const override;
ResultCode CreateFile(const std::string& path, u64 size) const override;
ResultCode CreateDirectory(const Path& path) const override;
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
u64 GetFreeBytes() const override;
u64 GetFreeSpaceSize() const override;
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
protected:
std::shared_ptr<FileUtil::IOFile> romfs_file;
@@ -50,9 +48,9 @@ protected:
u64 data_size;
};
class IVFCFile : public FileBackend {
class RomFS_Storage : public StorageBackend {
public:
IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
RomFS_Storage(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
: romfs_file(file), data_offset(offset), data_size(size) {}
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
@@ -70,7 +68,7 @@ private:
u64 data_size;
};
class IVFCDirectory : public DirectoryBackend {
class ROMFSDirectory : public DirectoryBackend {
public:
u32 Read(const u32 count, Entry* entries) override {
return 0;

View File

@@ -1,330 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/file_util.h"
#include "core/file_sys/disk_archive.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/path_parser.h"
#include "core/file_sys/savedata_archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path,
const Mode& mode) const {
LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
if (mode.hex == 0) {
LOG_ERROR(Service_FS, "Empty open mode");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
if (mode.create_flag && !mode.write_flag) {
LOG_ERROR(Service_FS, "Create flag set but write flag not set");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_FILE_NOT_FOUND;
case PathParser::PathNotFound:
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
return ERROR_PATH_NOT_FOUND;
case PathParser::FileInPath:
case PathParser::DirectoryFound:
LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
case PathParser::NotFound:
if (!mode.create_flag) {
LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
full_path.c_str());
return ERROR_FILE_NOT_FOUND;
} else {
// Create the file
FileUtil::CreateEmptyFile(full_path);
}
break;
case PathParser::FileFound:
break; // Expected 'success' case
}
FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
if (!file.IsOpen()) {
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
return ERROR_FILE_NOT_FOUND;
}
auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
}
ResultCode SaveDataArchive::DeleteFile(const Path& path) const {
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_FILE_NOT_FOUND;
case PathParser::PathNotFound:
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
return ERROR_PATH_NOT_FOUND;
case PathParser::FileInPath:
case PathParser::DirectoryFound:
case PathParser::NotFound:
LOG_ERROR(Service_FS, "File not found %s", full_path.c_str());
return ERROR_FILE_NOT_FOUND;
case PathParser::FileFound:
break; // Expected 'success' case
}
if (FileUtil::Delete(full_path)) {
return RESULT_SUCCESS;
}
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
return ERROR_FILE_NOT_FOUND;
}
ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
const PathParser path_parser_src(src_path);
// TODO: Verify these return codes with HW
if (!path_parser_src.IsValid()) {
LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const PathParser path_parser_dest(dest_path);
if (!path_parser_dest.IsValid()) {
LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
if (FileUtil::Rename(src_path_full, dest_path_full)) {
return RESULT_SUCCESS;
}
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
template <typename T>
static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point,
T deleter) {
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
if (path_parser.IsRootDirectory())
return ERROR_DIRECTORY_NOT_EMPTY;
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_PATH_NOT_FOUND;
case PathParser::PathNotFound:
case PathParser::NotFound:
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
return ERROR_PATH_NOT_FOUND;
case PathParser::FileInPath:
case PathParser::FileFound:
LOG_ERROR(Service_FS, "Unexpected file or directory %s", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
case PathParser::DirectoryFound:
break; // Expected 'success' case
}
if (deleter(full_path)) {
return RESULT_SUCCESS;
}
LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
return ERROR_DIRECTORY_NOT_EMPTY;
}
ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const {
return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
}
ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const {
return DeleteDirectoryHelper(
path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
}
ResultCode SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const {
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_FILE_NOT_FOUND;
case PathParser::PathNotFound:
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
return ERROR_PATH_NOT_FOUND;
case PathParser::FileInPath:
LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
case PathParser::DirectoryFound:
case PathParser::FileFound:
LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
return ERROR_FILE_ALREADY_EXISTS;
case PathParser::NotFound:
break; // Expected 'success' case
}
if (size == 0) {
FileUtil::CreateEmptyFile(full_path);
return RESULT_SUCCESS;
}
FileUtil::IOFile file(full_path, "wb");
// Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
// We do this by seeking to the right size, then writing a single null byte.
if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
return RESULT_SUCCESS;
}
LOG_ERROR(Service_FS, "Too large file");
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultCode SaveDataArchive::CreateDirectory(const Path& path) const {
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_FILE_NOT_FOUND;
case PathParser::PathNotFound:
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
return ERROR_PATH_NOT_FOUND;
case PathParser::FileInPath:
LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
case PathParser::DirectoryFound:
case PathParser::FileFound:
LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
return ERROR_DIRECTORY_ALREADY_EXISTS;
case PathParser::NotFound:
break; // Expected 'success' case
}
if (FileUtil::CreateDir(mount_point + path.AsString())) {
return RESULT_SUCCESS;
}
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str());
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
const PathParser path_parser_src(src_path);
// TODO: Verify these return codes with HW
if (!path_parser_src.IsValid()) {
LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const PathParser path_parser_dest(dest_path);
if (!path_parser_dest.IsValid()) {
LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
if (FileUtil::Rename(src_path_full, dest_path_full)) {
return RESULT_SUCCESS;
}
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory(
const Path& path) const {
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_FILE_NOT_FOUND;
case PathParser::PathNotFound:
case PathParser::NotFound:
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
return ERROR_PATH_NOT_FOUND;
case PathParser::FileInPath:
case PathParser::FileFound:
LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
case PathParser::DirectoryFound:
break; // Expected 'success' case
}
auto directory = std::make_unique<DiskDirectory>(full_path);
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
}
u64 SaveDataArchive::GetFreeBytes() const {
// TODO: Stubbed to return 1GiB
return 1024 * 1024 * 1024;
}
} // namespace FileSys

View File

@@ -1,43 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "core/file_sys/archive_backend.h"
#include "core/file_sys/directory_backend.h"
#include "core/file_sys/file_backend.h"
#include "core/hle/result.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/// Archive backend for general save data archive type (SaveData and SystemSaveData)
class SaveDataArchive : public ArchiveBackend {
public:
explicit SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
std::string GetName() const override {
return "SaveDataArchive: " + mount_point;
}
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
const Mode& mode) const override;
ResultCode DeleteFile(const Path& path) const override;
ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
ResultCode DeleteDirectory(const Path& path) const override;
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
ResultCode CreateFile(const Path& path, u64 size) const override;
ResultCode CreateDirectory(const Path& path) const override;
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
u64 GetFreeBytes() const override;
protected:
std::string mount_point;
};
} // namespace FileSys

View File

@@ -0,0 +1,57 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <memory>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/disk_filesystem.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/kernel/process.h"
namespace FileSys {
SaveData_Factory::SaveData_Factory(std::string nand_directory)
: nand_directory(std::move(nand_directory)) {}
ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) {
std::string save_directory = GetFullPath();
// Return an error if the save data doesn't actually exist.
if (!FileUtil::IsDirectory(save_directory)) {
// TODO(Subv): Find out correct error code.
return ResultCode(-1);
}
auto archive = std::make_unique<Disk_FileSystem>(save_directory);
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
}
ResultCode SaveData_Factory::Format(const Path& path) {
LOG_WARNING(Service_FS, "Format archive %s", GetName().c_str());
// Create the save data directory.
if (!FileUtil::CreateFullPath(GetFullPath())) {
// TODO(Subv): Find the correct error code.
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const {
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
}
std::string SaveData_Factory::GetFullPath() const {
u64 title_id = Core::CurrentProcess()->program_id;
// TODO(Subv): Somehow obtain this value.
u32 user = 0;
return Common::StringFromFormat("%ssave/%016" PRIX64 "/%08X/", nand_directory.c_str(), title_id,
user);
}
} // namespace FileSys

View File

@@ -0,0 +1,33 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/filesystem.h"
#include "core/hle/result.h"
namespace FileSys {
/// File system interface to the SaveData archive
class SaveData_Factory final : public FileSystemFactory {
public:
explicit SaveData_Factory(std::string nand_directory);
std::string GetName() const override {
return "SaveData_Factory";
}
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::string nand_directory;
std::string GetFullPath() const;
};
} // namespace FileSys

View File

@@ -1,4 +1,4 @@
// Copyright 2014 Citra Emulator Project
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -8,15 +8,12 @@
#include "common/common_types.h"
#include "core/hle/result.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
class FileBackend : NonCopyable {
class StorageBackend : NonCopyable {
public:
FileBackend() {}
virtual ~FileBackend() {}
StorageBackend() {}
virtual ~StorageBackend() {}
/**
* Read data from the file
@@ -39,10 +36,9 @@ public:
const u8* buffer) const = 0;
/**
* Get the size of the file in bytes
* @return Size of the file in bytes
* Flushes the file
*/
virtual u64 GetSize() const = 0;
virtual void Flush() const = 0;
/**
* Set the size of the file in bytes
@@ -51,16 +47,17 @@ public:
*/
virtual bool SetSize(u64 size) const = 0;
/**
* Get the size of the file in bytes
* @return Size of the file in bytes
*/
virtual u64 GetSize() const = 0;
/**
* Close the file
* @return true if the file closed correctly
*/
virtual bool Close() const = 0;
/**
* Flushes the file
*/
virtual void Flush() const = 0;
};
} // namespace FileSys

View File

@@ -1,163 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "common/alignment.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/title_metadata.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
static u32 GetSignatureSize(u32 signature_type) {
switch (signature_type) {
case Rsa4096Sha1:
case Rsa4096Sha256:
return 0x200;
case Rsa2048Sha1:
case Rsa2048Sha256:
return 0x100;
case EllipticSha1:
case EcdsaSha256:
return 0x3C;
}
}
Loader::ResultStatus TitleMetadata::Load() {
FileUtil::IOFile file(filepath, "rb");
if (!file.IsOpen())
return Loader::ResultStatus::Error;
if (!file.ReadBytes(&signature_type, sizeof(u32_be)))
return Loader::ResultStatus::Error;
// Signature lengths are variable, and the body follows the signature
u32 signature_size = GetSignatureSize(signature_type);
tmd_signature.resize(signature_size);
if (!file.ReadBytes(&tmd_signature[0], signature_size))
return Loader::ResultStatus::Error;
// The TMD body start position is rounded to the nearest 0x40 after the signature
size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
file.Seek(body_start, SEEK_SET);
// Read our TMD body, then load the amount of ContentChunks specified
if (file.ReadBytes(&tmd_body, sizeof(TitleMetadata::Body)) != sizeof(TitleMetadata::Body))
return Loader::ResultStatus::Error;
for (u16 i = 0; i < tmd_body.content_count; i++) {
ContentChunk chunk;
if (file.ReadBytes(&chunk, sizeof(ContentChunk)) == sizeof(ContentChunk)) {
tmd_chunks.push_back(chunk);
} else {
LOG_ERROR(Service_FS, "Malformed TMD %s, failed to load content chunk index %u!",
filepath.c_str(), i);
return Loader::ResultStatus::ErrorInvalidFormat;
}
}
return Loader::ResultStatus::Success;
}
Loader::ResultStatus TitleMetadata::Save() {
UNIMPLEMENTED();
return Loader::ResultStatus::Success;
}
u64 TitleMetadata::GetTitleID() const {
return tmd_body.title_id;
}
u32 TitleMetadata::GetTitleType() const {
return tmd_body.title_type;
}
u16 TitleMetadata::GetTitleVersion() const {
return tmd_body.title_version;
}
u64 TitleMetadata::GetSystemVersion() const {
return tmd_body.system_version;
}
size_t TitleMetadata::GetContentCount() const {
return tmd_chunks.size();
}
u32 TitleMetadata::GetBootContentID() const {
return tmd_chunks[TMDContentIndex::Main].id;
}
u32 TitleMetadata::GetManualContentID() const {
return tmd_chunks[TMDContentIndex::Manual].id;
}
u32 TitleMetadata::GetDLPContentID() const {
return tmd_chunks[TMDContentIndex::DLP].id;
}
void TitleMetadata::SetTitleID(u64 title_id) {
tmd_body.title_id = title_id;
}
void TitleMetadata::SetTitleType(u32 type) {
tmd_body.title_type = type;
}
void TitleMetadata::SetTitleVersion(u16 version) {
tmd_body.title_version = version;
}
void TitleMetadata::SetSystemVersion(u64 version) {
tmd_body.system_version = version;
}
void TitleMetadata::AddContentChunk(const ContentChunk& chunk) {
tmd_chunks.push_back(chunk);
}
void TitleMetadata::Print() const {
LOG_DEBUG(Service_FS, "%s - %u chunks", filepath.c_str(),
static_cast<u32>(tmd_body.content_count));
// Content info describes ranges of content chunks
LOG_DEBUG(Service_FS, "Content info:");
for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) {
if (tmd_body.contentinfo[i].command_count == 0)
break;
LOG_DEBUG(Service_FS, " Index %04X, Command Count %04X",
static_cast<u32>(tmd_body.contentinfo[i].index),
static_cast<u32>(tmd_body.contentinfo[i].command_count));
}
// For each content info, print their content chunk range
for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) {
u16 index = static_cast<u16>(tmd_body.contentinfo[i].index);
u16 count = static_cast<u16>(tmd_body.contentinfo[i].command_count);
if (count == 0)
continue;
LOG_DEBUG(Service_FS, "Content chunks for content info index %zu:", i);
for (u16 j = index; j < index + count; j++) {
// Don't attempt to print content we don't have
if (j > tmd_body.content_count)
break;
const ContentChunk& chunk = tmd_chunks[j];
LOG_DEBUG(Service_FS, " ID %08X, Index %04X, Type %04x, Size %016" PRIX64,
static_cast<u32>(chunk.id), static_cast<u32>(chunk.index),
static_cast<u32>(chunk.type), static_cast<u64>(chunk.size));
}
}
}
} // namespace FileSys

View File

@@ -1,126 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
namespace Loader {
enum class ResultStatus;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
enum TMDSignatureType : u32 {
Rsa4096Sha1 = 0x10000,
Rsa2048Sha1 = 0x10001,
EllipticSha1 = 0x10002,
Rsa4096Sha256 = 0x10003,
Rsa2048Sha256 = 0x10004,
EcdsaSha256 = 0x10005
};
enum TMDContentTypeFlag : u16 {
Encrypted = 1 << 1,
Disc = 1 << 2,
CFM = 1 << 3,
Optional = 1 << 14,
Shared = 1 << 15
};
/**
* Helper which implements an interface to read and write Title Metadata (TMD) files.
* If a file path is provided and the file exists, it can be parsed and used, otherwise
* it must be created. The TMD file can then be interpreted, modified and/or saved.
*/
class TitleMetadata {
public:
struct ContentChunk {
u32_be id;
u16_be index;
u16_be type;
u64_be size;
std::array<u8, 0x20> hash;
};
static_assert(sizeof(ContentChunk) == 0x30, "TMD ContentChunk structure size is wrong");
struct ContentInfo {
u16_be index;
u16_be command_count;
std::array<u8, 0x20> hash;
};
static_assert(sizeof(ContentInfo) == 0x24, "TMD ContentInfo structure size is wrong");
#pragma pack(push, 1)
struct Body {
std::array<u8, 0x40> issuer;
u8 version;
u8 ca_crl_version;
u8 signer_crl_version;
u8 reserved;
u64_be system_version;
u64_be title_id;
u32_be title_type;
u16_be group_id;
u32_be savedata_size;
u32_be srl_private_savedata_size;
std::array<u8, 4> reserved_2;
u8 srl_flag;
std::array<u8, 0x31> reserved_3;
u32_be access_rights;
u16_be title_version;
u16_be content_count;
u16_be boot_content;
std::array<u8, 2> reserved_4;
std::array<u8, 0x20> contentinfo_hash;
std::array<ContentInfo, 64> contentinfo;
};
static_assert(sizeof(Body) == 0x9C4, "TMD body structure size is wrong");
#pragma pack(pop)
explicit TitleMetadata(std::string& path) : filepath(std::move(path)) {}
Loader::ResultStatus Load();
Loader::ResultStatus Save();
u64 GetTitleID() const;
u32 GetTitleType() const;
u16 GetTitleVersion() const;
u64 GetSystemVersion() const;
size_t GetContentCount() const;
u32 GetBootContentID() const;
u32 GetManualContentID() const;
u32 GetDLPContentID() const;
void SetTitleID(u64 title_id);
void SetTitleType(u32 type);
void SetTitleVersion(u16 version);
void SetSystemVersion(u64 version);
void AddContentChunk(const ContentChunk& chunk);
void Print() const;
private:
enum TMDContentIndex { Main = 0, Manual = 1, DLP = 2 };
Body tmd_body;
u32_be signature_type;
std::vector<u8> tmd_signature;
std::vector<ContentChunk> tmd_chunks;
std::string filepath;
};
} // namespace FileSys

View File

@@ -6,6 +6,7 @@
#include <algorithm>
#include <atomic>
#include <cinttypes>
#include <climits>
#include <csignal>
#include <cstdarg>
@@ -57,9 +58,10 @@ const u32 SIGTERM = 15;
const u32 MSG_WAITALL = 8;
#endif
const u32 R15_REGISTER = 15;
const u32 CPSR_REGISTER = 25;
const u32 FPSCR_REGISTER = 58;
const u32 X30_REGISTER = 30;
const u32 SP_REGISTER = 31;
const u32 PC_REGISTER = 32;
const u32 CPSR_REGISTER = 33;
// For sample XML files see the GDB source /gdb/features
// GDB also wants the l character at the start
@@ -68,48 +70,62 @@ static const char* target_xml =
R"(l<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
<feature name="org.gnu.gdb.arm.core">
<reg name="r0" bitsize="32"/>
<reg name="r1" bitsize="32"/>
<reg name="r2" bitsize="32"/>
<reg name="r3" bitsize="32"/>
<reg name="r4" bitsize="32"/>
<reg name="r5" bitsize="32"/>
<reg name="r6" bitsize="32"/>
<reg name="r7" bitsize="32"/>
<reg name="r8" bitsize="32"/>
<reg name="r9" bitsize="32"/>
<reg name="r10" bitsize="32"/>
<reg name="r11" bitsize="32"/>
<reg name="r12" bitsize="32"/>
<reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<feature name="org.gnu.gdb.aarch64.core">
<reg name="x0" bitsize="64"/>
<reg name="x1" bitsize="64"/>
<reg name="x2" bitsize="64"/>
<reg name="x3" bitsize="64"/>
<reg name="x4" bitsize="64"/>
<reg name="x5" bitsize="64"/>
<reg name="x6" bitsize="64"/>
<reg name="x7" bitsize="64"/>
<reg name="x8" bitsize="64"/>
<reg name="x9" bitsize="64"/>
<reg name="x10" bitsize="64"/>
<reg name="x11" bitsize="64"/>
<reg name="x12" bitsize="64"/>
<reg name="x13" bitsize="64"/>
<reg name="x14" bitsize="64"/>
<reg name="x15" bitsize="64"/>
<reg name="x16" bitsize="64"/>
<reg name="x17" bitsize="64"/>
<reg name="x18" bitsize="64"/>
<reg name="x19" bitsize="64"/>
<reg name="x20" bitsize="64"/>
<reg name="x21" bitsize="64"/>
<reg name="x22" bitsize="64"/>
<reg name="x23" bitsize="64"/>
<reg name="x24" bitsize="64"/>
<reg name="x25" bitsize="64"/>
<reg name="x26" bitsize="64"/>
<reg name="x27" bitsize="64"/>
<reg name="x28" bitsize="64"/>
<reg name="x29" bitsize="64"/>
<reg name="x30" bitsize="64"/>
<reg name="sp" bitsize="64" type="data_ptr"/>
<!-- The CPSR is register 25, rather than register 16, because
the FPA registers historically were placed between the PC
and the CPSR in the "g" packet. -->
<reg name="pc" bitsize="64" type="code_ptr"/>
<reg name="cpsr" bitsize="32" regnum="25"/>
</feature>
<feature name="org.gnu.gdb.arm.vfp">
<reg name="d0" bitsize="64" type="float"/>
<reg name="d1" bitsize="64" type="float"/>
<reg name="d2" bitsize="64" type="float"/>
<reg name="d3" bitsize="64" type="float"/>
<reg name="d4" bitsize="64" type="float"/>
<reg name="d5" bitsize="64" type="float"/>
<reg name="d6" bitsize="64" type="float"/>
<reg name="d7" bitsize="64" type="float"/>
<reg name="d8" bitsize="64" type="float"/>
<reg name="d9" bitsize="64" type="float"/>
<reg name="d10" bitsize="64" type="float"/>
<reg name="d11" bitsize="64" type="float"/>
<reg name="d12" bitsize="64" type="float"/>
<reg name="d13" bitsize="64" type="float"/>
<reg name="d14" bitsize="64" type="float"/>
<reg name="d15" bitsize="64" type="float"/>
<reg name="fpscr" bitsize="32" type="int" group="float"/>
<flags id="cpsr_flags" size="4">
<field name="SP" start="0" end="0"/>
<field name="" start="1" end="1"/>
<field name="EL" start="2" end="3"/>
<field name="nRW" start="4" end="4"/>
<field name="" start="5" end="5"/>
<field name="F" start="6" end="6"/>
<field name="I" start="7" end="7"/>
<field name="A" start="8" end="8"/>
<field name="D" start="9" end="9"/>
<field name="IL" start="20" end="20"/>
<field name="SS" start="21" end="21"/>
<field name="V" start="28" end="28"/>
<field name="C" start="29" end="29"/>
<field name="Z" start="30" end="30"/>
<field name="N" start="31" end="31"/>
</flags>
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
</feature>
</target>
)";
@@ -143,12 +159,12 @@ WSADATA InitData;
struct Breakpoint {
bool active;
PAddr addr;
u32 len;
u64 len;
};
static std::map<u32, Breakpoint> breakpoints_execute;
static std::map<u32, Breakpoint> breakpoints_read;
static std::map<u32, Breakpoint> breakpoints_write;
static std::map<u64, Breakpoint> breakpoints_execute;
static std::map<u64, Breakpoint> breakpoints_read;
static std::map<u64, Breakpoint> breakpoints_write;
/**
* Turns hex string character into the equivalent byte.
@@ -197,6 +213,21 @@ static u32 HexToInt(const u8* src, size_t len) {
return output;
}
/**
* Converts input hex string characters into an array of equivalent of u8 bytes.
*
* @param src Pointer to array of output hex string characters.
* @param len Length of src array.
*/
static u64 HexToLong(const u8* src, size_t len) {
u64 output = 0;
while (len-- > 0) {
output = (output << 4) | HexCharToValue(src[0]);
src++;
}
return output;
}
/**
* Converts input array of u8 bytes into their equivalent hex string characters.
*
@@ -234,8 +265,21 @@ static void GdbHexToMem(u8* dest, const u8* src, size_t len) {
*/
static void IntToGdbHex(u8* dest, u32 v) {
for (int i = 0; i < 8; i += 2) {
dest[i + 1] = NibbleToHex(v >> (4 * i));
dest[i] = NibbleToHex(v >> (4 * (i + 1)));
dest[i + 1] = NibbleToHex(static_cast<u8>(v >> (4 * i)));
dest[i] = NibbleToHex(static_cast<u8>(v >> (4 * (i + 1))));
}
}
/**
* Convert a u64 into a gdb-formatted hex string.
*
* @param dest Pointer to buffer to store output hex string characters.
* @param v Value to convert.
*/
static void LongToGdbHex(u8* dest, u64 v) {
for (int i = 0; i < 16; i += 2) {
dest[i + 1] = NibbleToHex(static_cast<u8>(v >> (4 * i)));
dest[i] = NibbleToHex(static_cast<u8>(v >> (4 * (i + 1))));
}
}
@@ -255,6 +299,22 @@ static u32 GdbHexToInt(const u8* src) {
return output;
}
/**
* Convert a gdb-formatted hex string into a u64.
*
* @param src Pointer to hex string.
*/
static u64 GdbHexToLong(const u8* src) {
u64 output = 0;
for (int i = 0; i < 16; i += 2) {
output = (output << 4) | HexCharToValue(src[15 - i - 1]);
output = (output << 4) | HexCharToValue(src[15 - i]);
}
return output;
}
/// Read a byte from the gdb client.
static u8 ReadByte() {
u8 c;
@@ -277,7 +337,7 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) {
*
* @param type Type of breakpoint list.
*/
static std::map<u32, Breakpoint>& GetBreakpointList(BreakpointType type) {
static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) {
switch (type) {
case BreakpointType::Execute:
return breakpoints_execute;
@@ -297,19 +357,20 @@ static std::map<u32, Breakpoint>& GetBreakpointList(BreakpointType type) {
* @param addr Address of breakpoint.
*/
static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
std::map<u32, Breakpoint>& p = GetBreakpointList(type);
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
auto bp = p.find(static_cast<u32>(addr));
auto bp = p.find(static_cast<u64>(addr));
if (bp != p.end()) {
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n",
bp->second.len, bp->second.addr, type);
p.erase(static_cast<u32>(addr));
LOG_DEBUG(Debug_GDBStub,
"gdb: removed a breakpoint: %016" PRIx64 " bytes at %016" PRIx64 " of type %d\n",
bp->second.len, bp->second.addr, static_cast<int>(type));
p.erase(static_cast<u64>(addr));
}
}
BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) {
std::map<u32, Breakpoint>& p = GetBreakpointList(type);
auto next_breakpoint = p.lower_bound(static_cast<u32>(addr));
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
auto next_breakpoint = p.lower_bound(static_cast<u64>(addr));
BreakpointAddress breakpoint;
if (next_breakpoint != p.end()) {
@@ -328,11 +389,11 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) {
return false;
}
std::map<u32, Breakpoint>& p = GetBreakpointList(type);
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
auto bp = p.find(static_cast<u32>(addr));
auto bp = p.find(static_cast<u64>(addr));
if (bp != p.end()) {
u32 len = bp->second.len;
u64 len = bp->second.len;
// IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
// no matter if it's a 4-byte or 2-byte instruction. When you execute a
@@ -348,8 +409,9 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) {
if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
LOG_DEBUG(Debug_GDBStub,
"Found breakpoint type %d @ %08x, range: %08x - %08x (%d bytes)\n", type,
addr, bp->second.addr, bp->second.addr + len, len);
"Found breakpoint type %d @ %016" PRIx64 ", range: %016" PRIx64
" - %016" PRIx64 " (%" PRIx64 " bytes)\n",
static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
return true;
}
}
@@ -419,7 +481,7 @@ static void HandleQuery() {
SendReply("T0");
} else if (strncmp(query, "Supported", strlen("Supported")) == 0) {
// PacketSize needs to be large enough for target xml
SendReply("PacketSize=800;qXfer:features:read+");
SendReply("PacketSize=2000;qXfer:features:read+");
} else if (strncmp(query, "Xfer:features:read:target.xml:",
strlen("Xfer:features:read:target.xml:")) == 0) {
SendReply(target_xml);
@@ -450,10 +512,7 @@ static void SendSignal(u32 signal) {
latest_signal = signal;
std::string buffer =
Common::StringFromFormat("T%02x%02x:%08x;%02x:%08x;", latest_signal, 15,
htonl(static_cast<u_long>(Core::CPU().GetPC())), 13,
htonl(static_cast<u_long>(Core::CPU().GetReg(13))));
std::string buffer = Common::StringFromFormat("T%02x", latest_signal);
LOG_DEBUG(Debug_GDBStub, "Response: %s", buffer.c_str());
SendReply(buffer.c_str());
}
@@ -539,16 +598,12 @@ static void ReadRegister() {
id |= HexCharToValue(command_buffer[2]);
}
if (id <= R15_REGISTER) {
IntToGdbHex(reply, static_cast<u32>(Core::CPU().GetReg(static_cast<u64>(id))));
if (id <= SP_REGISTER) {
LongToGdbHex(reply, Core::CPU().GetReg(static_cast<int>(id)));
} else if (id == PC_REGISTER) {
LongToGdbHex(reply, Core::CPU().GetPC());
} else if (id == CPSR_REGISTER) {
IntToGdbHex(reply, Core::CPU().GetCPSR());
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
IntToGdbHex(reply, Core::CPU().GetVFPReg(
id - CPSR_REGISTER -
1)); // VFP registers should start at 26, so one after CSPR_REGISTER
} else if (id == FPSCR_REGISTER) {
UNIMPLEMENTED();
} else {
return SendReply("E01");
}
@@ -563,21 +618,19 @@ static void ReadRegisters() {
u8* bufptr = buffer;
for (int reg = 0; reg <= R15_REGISTER; reg++) {
IntToGdbHex(bufptr + reg * CHAR_BIT, static_cast<u32>(Core::CPU().GetReg(reg)));
for (int reg = 0; reg <= SP_REGISTER; reg++) {
LongToGdbHex(bufptr + reg * 16, Core::CPU().GetReg(reg));
}
bufptr += (16 * CHAR_BIT);
bufptr += (32 * 16);
LongToGdbHex(bufptr, Core::CPU().GetPC());
bufptr += 16;
IntToGdbHex(bufptr, Core::CPU().GetCPSR());
bufptr += CHAR_BIT;
for (int reg = 0; reg <= 31; reg++) {
IntToGdbHex(bufptr + reg * CHAR_BIT, Core::CPU().GetVFPReg(reg));
}
bufptr += (32 * CHAR_BIT);
bufptr += 8;
SendReply(reinterpret_cast<char*>(buffer));
}
@@ -593,14 +646,12 @@ static void WriteRegister() {
id |= HexCharToValue(command_buffer[2]);
}
if (id <= R15_REGISTER) {
Core::CPU().SetReg(id, GdbHexToInt(buffer_ptr));
if (id <= SP_REGISTER) {
Core::CPU().SetReg(id, GdbHexToLong(buffer_ptr));
} else if (id == PC_REGISTER) {
Core::CPU().SetPC(GdbHexToLong(buffer_ptr));
} else if (id == CPSR_REGISTER) {
Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr));
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
Core::CPU().SetVFPReg(id - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr));
} else if (id == FPSCR_REGISTER) {
UNIMPLEMENTED();
} else {
return SendReply("E01");
}
@@ -615,20 +666,14 @@ static void WriteRegisters() {
if (command_buffer[0] != 'G')
return SendReply("E01");
for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) {
if (reg <= R15_REGISTER) {
Core::CPU().SetReg(reg, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) {
if (reg <= SP_REGISTER) {
Core::CPU().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16));
} else if (reg == PC_REGISTER) {
Core::CPU().SetPC(GdbHexToLong(buffer_ptr + i * 16));
} else if (reg == CPSR_REGISTER) {
Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr + i * CHAR_BIT));
} else if (reg == CPSR_REGISTER - 1) {
// Dummy FPA register, ignore
} else if (reg < CPSR_REGISTER) {
// Dummy FPA registers, ignore
i += 2;
} else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) {
Core::CPU().SetVFPReg(reg - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
i++; // Skip padding
} else if (reg == FPSCR_REGISTER) {
Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr + i * 16));
} else {
UNIMPLEMENTED();
}
}
@@ -642,13 +687,13 @@ static void ReadMemory() {
auto start_offset = command_buffer + 1;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
VAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
start_offset = addr_pos + 1;
u32 len =
HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
u64 len =
HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %08x len: %08x\n", addr, len);
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016lx len: %016lx\n", addr, len);
if (len * 2 > sizeof(reply)) {
SendReply("E01");
@@ -670,11 +715,11 @@ static void ReadMemory() {
static void WriteMemory() {
auto start_offset = command_buffer + 1;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
VAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
start_offset = addr_pos + 1;
auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
u32 len = HexToInt(start_offset, static_cast<u32>(len_pos - start_offset));
u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset));
if (!Memory::IsValidVirtualAddress(addr)) {
return SendReply("E00");
@@ -727,8 +772,8 @@ static void Continue() {
* @param addr Address of breakpoint.
* @param len Length of breakpoint.
*/
static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
std::map<u32, Breakpoint>& p = GetBreakpointList(type);
static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) {
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
Breakpoint breakpoint;
breakpoint.active = true;
@@ -736,8 +781,8 @@ static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
breakpoint.len = len;
p.insert({addr, breakpoint});
LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, breakpoint.len,
breakpoint.addr);
LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %016" PRIx64 " bytes at %016" PRIx64 "\n",
static_cast<int>(type), breakpoint.len, breakpoint.addr);
return true;
}
@@ -767,11 +812,11 @@ static void AddBreakpoint() {
auto start_offset = command_buffer + 3;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
start_offset = addr_pos + 1;
u32 len =
HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
u64 len =
HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
if (type == BreakpointType::Access) {
// Access is made up of Read and Write types, so add both breakpoints
@@ -816,7 +861,7 @@ static void RemoveBreakpoint() {
auto start_offset = command_buffer + 3;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
if (type == BreakpointType::Access) {
// Access is made up of Read and Write types, so add both breakpoints

View File

@@ -91,6 +91,10 @@ struct BufferDescriptorX {
address |= static_cast<VAddr>(address_bits_36_38) << 36;
return address;
}
u64 Size() const {
return static_cast<u64>(size);
}
};
static_assert(sizeof(BufferDescriptorX) == 8, "BufferDescriptorX size is incorrect");

View File

@@ -11,7 +11,6 @@
#include "core/hle/ipc.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/domain.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
@@ -31,11 +30,6 @@ public:
RequestHelperBase(Kernel::HLERequestContext& context)
: context(&context), cmdbuf(context.CommandBuffer()) {}
void ValidateHeader() {
// DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)",
// header.raw);
}
void Skip(unsigned size_in_words, bool set_to_null) {
if (set_to_null)
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
@@ -60,14 +54,30 @@ public:
}
};
class RequestBuilder : public RequestHelperBase {
class ResponseBuilder : public RequestHelperBase {
public:
RequestBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
u32 normal_params_size{};
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
std::ptrdiff_t datapayload_index{};
/// Flags used for customizing the behavior of ResponseBuilder
enum class Flags : u32 {
None = 0,
/// Uses move handles to move objects in the response, even when in a domain. This is
/// required when PushMoveObjects is used.
AlwaysMoveHandles = 1,
};
ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
Flags flags = Flags::None)
: RequestHelperBase(context), normal_params_size(normal_params_size),
num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) {
RequestBuilder(Kernel::HLERequestContext& context, unsigned normal_params_size,
u32 num_handles_to_copy = 0, u32 num_handles_to_move = 0,
u32 num_domain_objects = 0)
: RequestHelperBase(context) {
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
context.ClearIncomingObjects();
@@ -77,12 +87,19 @@ public:
// The entire size of the raw data section in u32 units, including the 16 bytes of mandatory
// padding.
u32 raw_data_size = sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size;
if (context.IsDomain()) {
raw_data_size += sizeof(DomainMessageHeader) / 4 + num_domain_objects;
u32 num_handles_to_move{};
u32 num_domain_objects{};
const bool always_move_handles{
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
if (!context.Session()->IsDomain() || always_move_handles) {
num_handles_to_move = num_objects_to_move;
} else {
// If we're not in a domain, turn the domain object parameters into move handles.
num_handles_to_move += num_domain_objects;
num_domain_objects = 0;
num_domain_objects = num_objects_to_move;
}
if (context.Session()->IsDomain()) {
raw_data_size += sizeof(DomainMessageHeader) / 4 + num_domain_objects;
}
header.data_size.Assign(raw_data_size);
@@ -101,7 +118,8 @@ public:
AlignWithPadding();
if (context.IsDomain()) {
const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr};
if (context.Session()->IsDomain() && request_has_domain_header) {
IPC::DomainMessageHeader domain_header{};
domain_header.num_objects = num_domain_objects;
PushRaw(domain_header);
@@ -110,23 +128,43 @@ public:
IPC::DataPayloadHeader data_payload_header{};
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
PushRaw(data_payload_header);
datapayload_index = index;
}
template <class T>
void PushIpcInterface(std::shared_ptr<T> iface) {
if (context->Session()->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
auto sessions = Kernel::ServerSession::CreateSessionPair(iface->GetServiceName());
auto server = std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions);
auto client = std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions);
iface->ClientConnected(server);
context->AddMoveObject(std::move(client));
}
}
template <class T, class... Args>
void PushIpcInterface(Args&&... args) {
auto iface = std::make_shared<T>(std::forward<Args>(args)...);
if (context->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
auto port = iface->CreatePort();
auto session = port->Connect();
ASSERT(session.Succeeded());
context->AddMoveObject(std::move(session).Unwrap());
}
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
}
void ValidateHeader() {
const size_t num_domain_objects = context->NumDomainObjects();
const size_t num_move_objects = context->NumMoveObjects();
ASSERT_MSG(!num_domain_objects || !num_move_objects,
"cannot move normal handles and domain objects");
ASSERT_MSG((index - datapayload_index) == normal_params_size,
"normal_params_size value is incorrect");
ASSERT_MSG((num_domain_objects + num_move_objects) == num_objects_to_move,
"num_objects_to_move value is incorrect");
ASSERT_MSG(context->NumCopyObjects() == num_handles_to_copy,
"num_handles_to_copy value is incorrect");
}
// Validate on destruction, as there shouldn't be any case where we don't want it
~RequestBuilder() {
~ResponseBuilder() {
ValidateHeader();
}
@@ -154,52 +192,52 @@ public:
/// Push ///
template <>
inline void RequestBuilder::Push(u32 value) {
inline void ResponseBuilder::Push(u32 value) {
cmdbuf[index++] = value;
}
template <typename T>
void RequestBuilder::PushRaw(const T& value) {
void ResponseBuilder::PushRaw(const T& value) {
std::memcpy(cmdbuf + index, &value, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
template <>
inline void RequestBuilder::Push(ResultCode value) {
inline void ResponseBuilder::Push(ResultCode value) {
// Result codes are actually 64-bit in the IPC buffer, but only the high part is discarded.
Push(value.raw);
Push<u32>(0);
}
template <>
inline void RequestBuilder::Push(u8 value) {
inline void ResponseBuilder::Push(u8 value) {
PushRaw(value);
}
template <>
inline void RequestBuilder::Push(u16 value) {
inline void ResponseBuilder::Push(u16 value) {
PushRaw(value);
}
template <>
inline void RequestBuilder::Push(u64 value) {
inline void ResponseBuilder::Push(u64 value) {
Push(static_cast<u32>(value));
Push(static_cast<u32>(value >> 32));
}
template <>
inline void RequestBuilder::Push(bool value) {
inline void ResponseBuilder::Push(bool value) {
Push(static_cast<u8>(value));
}
template <typename First, typename... Other>
void RequestBuilder::Push(const First& first_value, const Other&... other_values) {
void ResponseBuilder::Push(const First& first_value, const Other&... other_values) {
Push(first_value);
Push(other_values...);
}
template <typename... O>
inline void RequestBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) {
inline void ResponseBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) {
auto objects = {pointers...};
for (auto& object : objects) {
context->AddCopyObject(std::move(object));
@@ -207,7 +245,7 @@ inline void RequestBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) {
}
template <typename... O>
inline void RequestBuilder::PushMoveObjects(Kernel::SharedPtr<O>... pointers) {
inline void ResponseBuilder::PushMoveObjects(Kernel::SharedPtr<O>... pointers) {
auto objects = {pointers...};
for (auto& object : objects) {
context->AddMoveObject(std::move(object));
@@ -226,15 +264,10 @@ public:
Skip(CommandIdSize, false);
}
RequestBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy,
u32 num_handles_to_move, u32 num_domain_objects,
bool validate_header = true) {
if (validate_header) {
ValidateHeader();
}
return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move,
num_domain_objects};
ResponseBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy,
u32 num_handles_to_move,
ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) {
return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move, flags};
}
template <typename T>
@@ -304,6 +337,11 @@ inline u64 RequestParser::Pop() {
return msw << 32 | lsw;
}
template <>
inline s64 RequestParser::Pop() {
return static_cast<s64>(Pop<u64>());
}
template <>
inline bool RequestParser::Pop() {
return Pop<u8>() != 0;

View File

@@ -1,91 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/thread.h"
#include "core/memory.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
namespace Kernel {
AddressArbiter::AddressArbiter() {}
AddressArbiter::~AddressArbiter() {}
SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) {
SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter);
address_arbiter->name = std::move(name);
return address_arbiter;
}
ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value,
u64 nanoseconds) {
switch (type) {
// Signal thread(s) waiting for arbitrate address...
case ArbitrationType::Signal:
// Negative value means resume all threads
if (value < 0) {
ArbitrateAllThreads(address);
} else {
// Resume first N threads
for (int i = 0; i < value; i++)
ArbitrateHighestPriorityThread(address);
}
break;
// Wait current thread (acquire the arbiter)...
case ArbitrationType::WaitIfLessThan:
if ((s32)Memory::Read32(address) < value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
}
break;
case ArbitrationType::WaitIfLessThanWithTimeout:
if ((s32)Memory::Read32(address) < value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
GetCurrentThread()->WakeAfterDelay(nanoseconds);
}
break;
case ArbitrationType::DecrementAndWaitIfLessThan: {
s32 memory_value = Memory::Read32(address);
if (memory_value < value) {
// Only change the memory value if the thread should wait
Memory::Write32(address, (s32)memory_value - 1);
Kernel::WaitCurrentThread_ArbitrateAddress(address);
}
break;
}
case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: {
s32 memory_value = Memory::Read32(address);
if (memory_value < value) {
// Only change the memory value if the thread should wait
Memory::Write32(address, (s32)memory_value - 1);
Kernel::WaitCurrentThread_ArbitrateAddress(address);
GetCurrentThread()->WakeAfterDelay(nanoseconds);
}
break;
}
default:
LOG_ERROR(Kernel, "unknown type=%d", type);
return ERR_INVALID_ENUM_VALUE_FND;
}
// The calls that use a timeout seem to always return a Timeout error even if they did not put
// the thread to sleep
if (type == ArbitrationType::WaitIfLessThanWithTimeout ||
type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) {
return RESULT_TIMEOUT;
}
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

@@ -1,60 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
// Address arbiters are an underlying kernel synchronization object that can be created/used via
// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
// semphores.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
namespace Kernel {
enum class ArbitrationType : u32 {
Signal,
WaitIfLessThan,
DecrementAndWaitIfLessThan,
WaitIfLessThanWithTimeout,
DecrementAndWaitIfLessThanWithTimeout,
};
class AddressArbiter final : public Object {
public:
/**
* Creates an address arbiter.
*
* @param name Optional name used for debugging.
* @returns The created AddressArbiter.
*/
static SharedPtr<AddressArbiter> Create(std::string name = "Unknown");
std::string GetTypeName() const override {
return "Arbiter";
}
std::string GetName() const override {
return name;
}
static const HandleType HANDLE_TYPE = HandleType::AddressArbiter;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
std::string name; ///< Name of address arbiter object (optional)
ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds);
private:
AddressArbiter();
~AddressArbiter() override;
};
} // namespace Kernel

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/sync_object.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
namespace Kernel {
@@ -16,7 +16,7 @@ class ServerSession;
class Session;
class Thread;
class ClientSession final : public SyncObject {
class ClientSession final : public Object {
public:
friend class ServerSession;
@@ -33,7 +33,7 @@ public:
return HANDLE_TYPE;
}
ResultCode SendSyncRequest(SharedPtr<Thread> thread) override;
ResultCode SendSyncRequest(SharedPtr<Thread> thread);
std::string name; ///< Name of client port (optional)

View File

@@ -15,13 +15,12 @@ ConditionVariable::ConditionVariable() {}
ConditionVariable::~ConditionVariable() {}
ResultVal<SharedPtr<ConditionVariable>> ConditionVariable::Create(VAddr guest_addr,
VAddr mutex_addr,
std::string name) {
SharedPtr<ConditionVariable> condition_variable(new ConditionVariable);
condition_variable->name = std::move(name);
condition_variable->guest_addr = guest_addr;
condition_variable->mutex_addr = mutex_addr;
condition_variable->mutex_addr = 0;
// Condition variables are referenced by guest address, so track this in the kernel
g_object_address_table.Insert(guest_addr, condition_variable);

View File

@@ -19,12 +19,10 @@ public:
* Creates a condition variable.
* @param guest_addr Address of the object tracking the condition variable in guest memory. If
* specified, this condition variable will update the guest object when its state changes.
* @param mutex_addr Optional address of a guest mutex associated with this condition variable,
* used by the OS for implementing events.
* @param name Optional name of condition variable.
* @return The created condition variable.
*/
static ResultVal<SharedPtr<ConditionVariable>> Create(VAddr guest_addr, VAddr mutex_addr = 0,
static ResultVal<SharedPtr<ConditionVariable>> Create(VAddr guest_addr,
std::string name = "Unknown");
std::string GetTypeName() const override {

View File

@@ -1,63 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/domain.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
ResultVal<SharedPtr<Domain>> Domain::Create(std::string name) {
SharedPtr<Domain> domain(new Domain);
domain->name = std::move(name);
return MakeResult(std::move(domain));
}
ResultVal<SharedPtr<Domain>> Domain::CreateFromSession(const Session& session) {
auto res = Create(session.port->GetName() + "_Domain");
auto& domain = res.Unwrap();
domain->request_handlers.push_back(std::move(session.server->hle_handler));
Kernel::g_handle_table.ConvertSessionToDomain(session, domain);
return res;
}
ResultCode Domain::SendSyncRequest(SharedPtr<Thread> thread) {
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
Kernel::g_handle_table);
auto& domain_message_header = context.GetDomainMessageHeader();
if (domain_message_header) {
// If there is a DomainMessageHeader, then this is CommandType "Request"
const u32 object_id{context.GetDomainMessageHeader()->object_id};
switch (domain_message_header->command) {
case IPC::DomainMessageHeader::CommandType::SendMessage:
return request_handlers[object_id - 1]->HandleSyncRequest(context);
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id);
request_handlers[object_id - 1] = nullptr;
IPC::RequestBuilder rb{context, 2};
rb.Push(RESULT_SUCCESS);
return RESULT_SUCCESS;
}
}
LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
UNIMPLEMENTED();
}
return request_handlers.front()->HandleSyncRequest(context);
}
} // namespace Kernel

View File

@@ -1,45 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "core/hle/kernel/sync_object.h"
#include "core/hle/result.h"
namespace Kernel {
class Session;
class SessionRequestHandler;
class Domain final : public SyncObject {
public:
std::string GetTypeName() const override {
return "Domain";
}
static const HandleType HANDLE_TYPE = HandleType::Domain;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
static ResultVal<SharedPtr<Domain>> CreateFromSession(const Session& server);
ResultCode SendSyncRequest(SharedPtr<Thread> thread) override;
/// The name of this domain (optional)
std::string name;
std::vector<std::shared_ptr<SessionRequestHandler>> request_handlers;
private:
Domain() = default;
~Domain() override = default;
static ResultVal<SharedPtr<Domain>> Create(std::string name = "Unknown");
};
} // namespace Kernel

View File

@@ -5,12 +5,11 @@
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/kernel/client_session.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
@@ -55,14 +54,6 @@ ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
return Create(std::move(object));
}
void HandleTable::ConvertSessionToDomain(const Session& session, SharedPtr<Object> domain) {
for (auto& object : objects) {
if (DynamicObjectCast<ClientSession>(object) == session.client) {
object = domain;
}
}
}
ResultCode HandleTable::Close(Handle handle) {
if (!IsValid(handle))
return ERR_INVALID_HANDLE;
@@ -87,7 +78,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
if (handle == CurrentThread) {
return GetCurrentThread();
} else if (handle == CurrentProcess) {
return g_current_process;
return Core::CurrentProcess();
}
if (!IsValid(handle)) {

View File

@@ -17,8 +17,6 @@ enum KernelHandle : Handle {
CurrentProcess = 0xFFFF8001,
};
class Session;
/**
* This class allows the creation of Handles, which are references to objects that can be tested
* for validity and looked up. Here they are used to pass references to kernel objects to/from the
@@ -61,11 +59,6 @@ public:
*/
ResultVal<Handle> Duplicate(Handle handle);
/**
* Convert all handles of the specified Session to the specified Domain.
*/
void ConvertSessionToDomain(const Session& session, SharedPtr<Object> domain);
/**
* Closes a handle, removing it from the table and decreasing the object's ref-count.
* @return `RESULT_SUCCESS` or one of the following errors:

View File

@@ -7,12 +7,13 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/domain.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/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/memory.h"
namespace Kernel {
@@ -26,8 +27,30 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
boost::range::remove_erase(connected_sessions, server_session);
}
HLERequestContext::HLERequestContext(SharedPtr<Kernel::Domain> domain) : domain(std::move(domain)) {
cmd_buf[0] = 0;
SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
const std::string& reason, u64 timeout,
WakeupCallback&& callback) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->wakeup_callback =
[context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, size_t index) mutable -> bool {
ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT);
callback(thread, context, reason);
context.WriteToOutgoingCommandBuffer(*thread);
return true;
};
auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
thread->status = THREADSTATUS_WAIT_HLE_EVENT;
thread->wait_objects = {event};
event->AddWaitingThread(thread);
if (timeout > 0) {
thread->WakeAfterDelay(timeout);
}
return event;
}
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
@@ -39,7 +62,7 @@ HLERequestContext::~HLERequestContext() = default;
void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
IPC::RequestParser rp(src_cmdbuf);
command_header = std::make_unique<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
if (command_header->type == IPC::CommandType::Close) {
// Close does not populate the rest of the IPC header
@@ -49,7 +72,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
// If handle descriptor is present, add size of it
if (command_header->enable_handle_descriptor) {
handle_descriptor_header =
std::make_unique<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
if (handle_descriptor_header->send_current_pid) {
rp.Skip(2, false);
}
@@ -87,15 +110,20 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
// Padding to align to 16 bytes
rp.AlignWithPadding();
if (IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) {
if (Session()->IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) {
// If this is an incoming message, only CommandType "Request" has a domain header
// All outgoing domain messages have the domain header
domain_message_header =
std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
// All outgoing domain messages have the domain header, if only incoming has it
if (incoming || domain_message_header) {
domain_message_header =
std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
} else {
if (Session()->IsDomain())
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
}
}
data_payload_header =
std::make_unique<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
data_payload_offset = rp.GetCurrentOffset();
@@ -158,8 +186,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
return RESULT_SUCCESS;
}
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
HandleTable& dst_table) {
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));
// The header was already built in the internal command buffer. Attempt to parse it to verify
// the integrity and then copy it over to the target command buffer.
ParseCommandBuffer(cmd_buf.data(), false);
@@ -170,7 +201,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
if (domain_message_header)
size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
std::copy_n(cmd_buf.begin(), size, dst_cmdbuf);
std::copy_n(cmd_buf.begin(), size, dst_cmdbuf.data());
if (command_header->enable_handle_descriptor) {
ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(),
@@ -200,19 +231,118 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
// TODO(Subv): Translate the X/A/B/W buffers.
if (IsDomain()) {
if (Session()->IsDomain() && domain_message_header) {
ASSERT(domain_message_header->num_objects == domain_objects.size());
// Write the domain objects to the command buffer, these go after the raw untranslated data.
// TODO(Subv): This completely ignores C buffers.
size_t domain_offset = size - domain_message_header->num_objects;
auto& request_handlers = domain->request_handlers;
auto& request_handlers = server_session->domain_request_handlers;
for (auto& object : domain_objects) {
request_handlers.emplace_back(object);
dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size());
}
}
// Copy the translated command buffer back into the thread's command buffer area.
Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));
return RESULT_SUCCESS;
}
std::vector<u8> HLERequestContext::ReadBuffer() const {
std::vector<u8> buffer;
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()};
if (is_buffer_a) {
buffer.resize(BufferDescriptorA()[0].Size());
Memory::ReadBlock(BufferDescriptorA()[0].Address(), buffer.data(), buffer.size());
} else {
buffer.resize(BufferDescriptorX()[0].Size());
Memory::ReadBlock(BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
}
return buffer;
}
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const {
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
ASSERT_MSG(size <= GetWriteBufferSize(), "Size %lx is too big", size);
if (is_buffer_b) {
Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size);
} else {
Memory::WriteBlock(BufferDescriptorC()[0].Address(), buffer, size);
}
return size;
}
size_t HLERequestContext::WriteBuffer(const std::vector<u8>& buffer) const {
return WriteBuffer(buffer.data(), buffer.size());
}
size_t HLERequestContext::GetReadBufferSize() const {
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()};
return is_buffer_a ? BufferDescriptorA()[0].Size() : BufferDescriptorX()[0].Size();
}
size_t HLERequestContext::GetWriteBufferSize() const {
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
return is_buffer_b ? BufferDescriptorB()[0].Size() : BufferDescriptorC()[0].Size();
}
std::string HLERequestContext::Description() const {
if (!command_header) {
return "No command header available";
}
std::ostringstream s;
s << "IPC::CommandHeader: Type:" << static_cast<u32>(command_header->type.Value());
s << ", X(Pointer):" << command_header->num_buf_x_descriptors;
if (command_header->num_buf_x_descriptors) {
s << '[';
for (u64 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
s << "0x" << std::hex << BufferDescriptorX()[i].Size();
if (i < command_header->num_buf_x_descriptors - 1)
s << ", ";
}
s << ']';
}
s << ", A(Send):" << command_header->num_buf_a_descriptors;
if (command_header->num_buf_a_descriptors) {
s << '[';
for (u64 i = 0; i < command_header->num_buf_a_descriptors; ++i) {
s << "0x" << std::hex << BufferDescriptorA()[i].Size();
if (i < command_header->num_buf_a_descriptors - 1)
s << ", ";
}
s << ']';
}
s << ", B(Receive):" << command_header->num_buf_b_descriptors;
if (command_header->num_buf_b_descriptors) {
s << '[';
for (u64 i = 0; i < command_header->num_buf_b_descriptors; ++i) {
s << "0x" << std::hex << BufferDescriptorB()[i].Size();
if (i < command_header->num_buf_b_descriptors - 1)
s << ", ";
}
s << ']';
}
s << ", C(ReceiveList):" << BufferDescriptorC().size();
if (!BufferDescriptorC().empty()) {
s << '[';
for (u64 i = 0; i < BufferDescriptorC().size(); ++i) {
s << "0x" << std::hex << BufferDescriptorC()[i].Size();
if (i < BufferDescriptorC().size() - 1)
s << ", ";
}
s << ']';
}
s << ", data_size:" << command_header->data_size.Value();
return s.str();
}
} // namespace Kernel

View File

@@ -6,6 +6,7 @@
#include <array>
#include <memory>
#include <string>
#include <vector>
#include <boost/container/small_vector.hpp>
#include "common/common_types.h"
@@ -13,6 +14,7 @@
#include "core/hle/ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
namespace Service {
class ServiceFrameworkBase;
@@ -24,6 +26,7 @@ class Domain;
class HandleTable;
class HLERequestContext;
class Process;
class Event;
/**
* Interface implemented by HLE Session handlers.
@@ -86,7 +89,6 @@ protected:
*/
class HLERequestContext {
public:
HLERequestContext(SharedPtr<Kernel::Domain> domain);
HLERequestContext(SharedPtr<Kernel::ServerSession> session);
~HLERequestContext();
@@ -95,29 +97,39 @@ public:
return cmd_buf.data();
}
/**
* Returns the domain through which this request was made.
*/
const SharedPtr<Kernel::Domain>& Domain() const {
return domain;
}
/**
* Returns the session through which this request was made. This can be used as a map key to
* access per-client data on services.
*/
const SharedPtr<Kernel::ServerSession>& ServerSession() const {
const SharedPtr<Kernel::ServerSession>& Session() const {
return server_session;
}
using WakeupCallback = std::function<void(SharedPtr<Thread> thread, HLERequestContext& context,
ThreadWakeupReason reason)>;
/**
* Puts the specified guest thread to sleep until the returned event is signaled or until the
* specified timeout expires.
* @param thread Thread to be put to sleep.
* @param reason Reason for pausing the thread, to be used for debugging purposes.
* @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
* invoked with a Timeout reason.
* @param callback Callback to be invoked when the thread is resumed. This callback must write
* the entire command response once again, regardless of the state of it before this function
* was called.
* @returns Event that when signaled will resume the thread and call the callback function.
*/
SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
u64 timeout, WakeupCallback&& callback);
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
/// Populates this context with data from the requesting process/thread.
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
HandleTable& src_table);
/// Writes data from this context back to the requesting process/thread.
ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
HandleTable& dst_table);
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
u32_le GetCommand() const {
return command;
@@ -147,13 +159,24 @@ public:
return buffer_c_desciptors;
}
const std::unique_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
return domain_message_header;
}
bool IsDomain() const {
return domain != nullptr;
}
/// Helper function to read a buffer using the appropriate buffer descriptor
std::vector<u8> ReadBuffer() const;
/// Helper function to write a buffer using the appropriate buffer descriptor
size_t WriteBuffer(const void* buffer, size_t size) const;
/// Helper function to write a buffer using the appropriate buffer descriptor
size_t WriteBuffer(const std::vector<u8>& buffer) const;
/// Helper function to get the size of the input buffer
size_t GetReadBufferSize() const;
/// Helper function to get the size of the output buffer
size_t GetWriteBufferSize() const;
template <typename T>
SharedPtr<T> GetCopyObject(size_t index) {
@@ -187,19 +210,32 @@ public:
domain_objects.clear();
}
size_t NumMoveObjects() const {
return move_objects.size();
}
size_t NumCopyObjects() const {
return copy_objects.size();
}
size_t NumDomainObjects() const {
return domain_objects.size();
}
std::string Description() const;
private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
SharedPtr<Kernel::Domain> domain;
SharedPtr<Kernel::ServerSession> server_session;
// TODO(yuriks): Check common usage of this and optimize size accordingly
boost::container::small_vector<SharedPtr<Object>, 8> move_objects;
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
std::unique_ptr<IPC::CommandHeader> command_header;
std::unique_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
std::unique_ptr<IPC::DataPayloadHeader> data_payload_header;
std::unique_ptr<IPC::DomainMessageHeader> domain_message_header;
std::shared_ptr<IPC::CommandHeader> command_header;
std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
std::shared_ptr<IPC::DataPayloadHeader> data_payload_header;
std::shared_ptr<IPC::DomainMessageHeader> domain_message_header;
std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;

View File

@@ -41,7 +41,6 @@ void Shutdown() {
g_object_address_table.Clear();
Kernel::ThreadingShutdown();
g_current_process = nullptr;
Kernel::TimersShutdown();
Kernel::ResourceLimitsShutdown();

View File

@@ -31,11 +31,6 @@ enum class HandleType : u32 {
ServerPort,
ClientSession,
ServerSession,
Domain,
};
enum {
DEFAULT_STACK_SIZE = 0x10000,
};
enum class ResetType {
@@ -84,27 +79,12 @@ public:
case HandleType::CodeSet:
case HandleType::ClientPort:
case HandleType::ClientSession:
case HandleType::Domain:
return false;
}
UNREACHABLE();
}
/**
* Check if svcSendSyncRequest can be called on the object
* @return True svcSendSyncRequest can be called on the object, otherwise false
*/
bool IsSyncable() const {
switch (GetHandleType()) {
case HandleType::ClientSession:
case HandleType::Domain:
return true;
}
UNREACHABLE();
}
public:
static unsigned int next_object_id;

View File

@@ -70,6 +70,7 @@ ResultCode Mutex::Release(Thread* thread) {
holding_thread->held_mutexes.erase(this);
holding_thread->UpdatePriority();
SetHoldingThread(nullptr);
SetHasWaiters(!GetWaitingThreads().empty());
WakeupAllWaitingThreads();
Core::System::GetInstance().PrepareReschedule();

View File

@@ -10,12 +10,12 @@ namespace Kernel {
ObjectAddressTable g_object_address_table;
void ObjectAddressTable::Insert(VAddr addr, SharedPtr<Object> obj) {
ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x%llx", addr);
ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x%lx", addr);
objects[addr] = obj;
}
void ObjectAddressTable::Close(VAddr addr) {
ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x%llx", addr);
ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x%lx", addr);
objects.erase(addr);
}

View File

@@ -20,12 +20,9 @@ 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, u64 program_id) {
SharedPtr<CodeSet> CodeSet::Create(std::string name) {
SharedPtr<CodeSet> codeset(new CodeSet);
codeset->name = std::move(name);
codeset->program_id = program_id;
return codeset;
}
@@ -41,6 +38,7 @@ SharedPtr<Process> Process::Create(std::string&& name) {
process->flags.raw = 0;
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
process->status = ProcessStatus::Created;
process->program_id = 0;
process_list.push_back(process);
return process;
@@ -119,11 +117,12 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
}
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
// Allocate and map stack
// Allocate and map the main thread stack
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
// of the user address space.
vm_manager
.MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size,
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Heap)
.MapMemoryBlock(Memory::STACK_VADDR, std::make_shared<std::vector<u8>>(stack_size, 0), 0,
stack_size, MemoryState::Mapped)
.Unwrap();
misc_memory_used += stack_size;
memory_region->used += stack_size;
@@ -155,9 +154,9 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
};
// Map CodeSet segments
MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::Code);
MapSegment(module_->rodata, VMAPermission::Read, MemoryState::Static);
MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::Static);
MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable);
}
VAddr Process::GetLinearHeapAreaAddress() const {
@@ -184,6 +183,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
// Initialize heap
heap_memory = std::make_shared<std::vector<u8>>();
heap_start = heap_end = target;
} else {
vm_manager.UnmapRange(heap_start, heap_end - heap_start);
}
// If necessary, expand backing vector to cover new heap extents.
@@ -203,7 +204,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
size, MemoryState::Heap));
vm_manager.Reprotect(vma, perms);
heap_used += size;
heap_used = size;
memory_region->used += size;
return MakeResult<VAddr>(heap_end - size);
@@ -290,7 +291,7 @@ ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
CASCADE_RESULT(auto new_vma,
vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
vma->second.meminfo_state));
MemoryState::Mapped));
// Protect mirror with permissions from old region
vm_manager.Reprotect(new_vma, vma->second.permissions);
// Remove permissions from old region
@@ -321,5 +322,4 @@ SharedPtr<Process> GetProcessById(u32 process_id) {
return *itr;
}
SharedPtr<Process> g_current_process;
} // namespace Kernel

View File

@@ -56,7 +56,7 @@ class ResourceLimit;
struct MemoryRegionInfo;
struct CodeSet final : public Object {
static SharedPtr<CodeSet> Create(std::string name, u64 program_id);
static SharedPtr<CodeSet> Create(std::string name);
std::string GetTypeName() const override {
return "CodeSet";
@@ -72,8 +72,6 @@ struct CodeSet final : public Object {
/// Name of the process
std::string name;
/// Title ID corresponding to the process
u64 program_id;
std::shared_ptr<std::vector<u8>> memory;
@@ -113,6 +111,9 @@ public:
static u32 next_process_id;
/// Title ID corresponding to the process
u64 program_id;
/// Resource limit descriptor for this process
SharedPtr<ResourceLimit> resource_limit;
@@ -202,5 +203,4 @@ void ClearProcessList();
/// Retrieves a process from the current list of processes.
SharedPtr<Process> GetProcessById(u32 process_id);
extern SharedPtr<Process> g_current_process;
} // namespace Kernel

View File

@@ -0,0 +1,135 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
namespace Kernel {
Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
Scheduler::~Scheduler() {
for (auto& thread : thread_list) {
thread->Stop();
}
}
bool Scheduler::HaveReadyThreads() {
return ready_queue.get_first() != nullptr;
}
Thread* Scheduler::GetCurrentThread() const {
return current_thread.get();
}
Thread* Scheduler::PopNextReadyThread() {
Thread* next = nullptr;
Thread* thread = GetCurrentThread();
if (thread && thread->status == THREADSTATUS_RUNNING) {
// We have to do better than the current thread.
// This call returns null when that's not possible.
next = ready_queue.pop_first_better(thread->current_priority);
if (!next) {
// Otherwise just keep going with the current thread
next = thread;
}
} else {
next = ready_queue.pop_first();
}
return next;
}
void Scheduler::SwitchContext(Thread* new_thread) {
Thread* previous_thread = GetCurrentThread();
// Save context for previous thread
if (previous_thread) {
previous_thread->last_running_ticks = CoreTiming::GetTicks();
cpu_core->SaveContext(previous_thread->context);
if (previous_thread->status == THREADSTATUS_RUNNING) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
ready_queue.push_front(previous_thread->current_priority, previous_thread);
previous_thread->status = THREADSTATUS_READY;
}
}
// Load context of new thread
if (new_thread) {
ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
"Thread must be ready to become running.");
// Cancel any outstanding wakeup events for this thread
new_thread->CancelWakeupTimer();
auto previous_process = Core::CurrentProcess();
current_thread = new_thread;
ready_queue.remove(new_thread->current_priority, new_thread);
new_thread->status = THREADSTATUS_RUNNING;
if (previous_process != current_thread->owner_process) {
Core::CurrentProcess() = current_thread->owner_process;
SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
}
cpu_core->LoadContext(new_thread->context);
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
} else {
current_thread = nullptr;
// Note: We do not reset the current process and current page table when idling because
// technically we haven't changed processes, our threads are just paused.
}
}
void Scheduler::Reschedule() {
Thread* cur = GetCurrentThread();
Thread* next = PopNextReadyThread();
if (cur && next) {
LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
} else if (cur) {
LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId());
} else if (next) {
LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId());
}
SwitchContext(next);
}
void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
thread_list.push_back(thread);
ready_queue.prepare(priority);
}
void Scheduler::RemoveThread(Thread* thread) {
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
ASSERT(thread->status == THREADSTATUS_READY);
ready_queue.push_back(priority, thread);
}
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
ASSERT(thread->status == THREADSTATUS_READY);
ready_queue.remove(priority, thread);
}
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
// If thread was ready, adjust queues
if (thread->status == THREADSTATUS_READY)
ready_queue.move(thread, thread->current_priority, priority);
else
ready_queue.prepare(priority);
}
} // namespace Kernel

View File

@@ -0,0 +1,73 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
class Scheduler final {
public:
explicit Scheduler(ARM_Interface* cpu_core);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
bool HaveReadyThreads();
/// Reschedules to the next available thread (call after current thread is suspended)
void Reschedule();
/// Gets the current running thread
Thread* GetCurrentThread() const;
/// Adds a new thread to the scheduler
void AddThread(SharedPtr<Thread> thread, u32 priority);
/// Removes a thread from the scheduler
void RemoveThread(Thread* thread);
/// Schedules a thread that has become "ready"
void ScheduleThread(Thread* thread, u32 priority);
/// Unschedules a thread that was already scheduled
void UnscheduleThread(Thread* thread, u32 priority);
/// Sets the priority of a thread in the scheduler
void SetThreadPriority(Thread* thread, u32 priority);
/// Returns a list of all threads managed by the scheduler
const std::vector<SharedPtr<Thread>>& GetThreadList() const {
return thread_list;
}
private:
/**
* Pops and returns the next thread from the thread queue
* @return A pointer to the next ready thread
*/
Thread* PopNextReadyThread();
/**
* Switches the CPU's active thread context to that of the specified thread
* @param new_thread The thread to switch to
*/
void SwitchContext(Thread* new_thread);
/// Lists all thread ids that aren't deleted/etc.
std::vector<SharedPtr<Thread>> thread_list;
/// Lists only ready thread ids.
Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
SharedPtr<Thread> current_thread = nullptr;
ARM_Interface* cpu_core;
};
} // namespace Kernel

View File

@@ -4,6 +4,8 @@
#include <tuple>
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/handle_table.h"
@@ -56,34 +58,91 @@ void ServerSession::Acquire(Thread* thread) {
pending_requesting_threads.pop_back();
}
ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
auto& domain_message_header = context.GetDomainMessageHeader();
if (domain_message_header) {
// If there is a DomainMessageHeader, then this is CommandType "Request"
const u32 object_id{context.GetDomainMessageHeader()->object_id};
switch (domain_message_header->command) {
case IPC::DomainMessageHeader::CommandType::SendMessage:
return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id);
domain_request_handlers[object_id - 1] = nullptr;
IPC::ResponseBuilder rb{context, 2};
rb.Push(RESULT_SUCCESS);
return RESULT_SUCCESS;
}
}
LOG_CRITICAL(IPC, "Unknown domain command=%d",
static_cast<int>(domain_message_header->command.Value()));
ASSERT(false);
}
return RESULT_SUCCESS;
}
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.
// If this ServerSession has an associated HLE handler, forward the request to it.
ResultCode result{RESULT_SUCCESS};
if (hle_handler != nullptr) {
// Attempt to translate the incoming request's command buffer.
ResultCode translate_result = TranslateHLERequest(this);
if (translate_result.IsError())
return translate_result;
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
Kernel::g_handle_table);
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(),
Kernel::g_handle_table);
ResultCode result = RESULT_SUCCESS;
// If the session has been converted to a domain, handle the domain request
if (IsDomain() && context.GetDomainMessageHeader()) {
result = HandleDomainSyncRequest(context);
// If there is no domain header, the regular session handler is used
} else if (hle_handler != nullptr) {
// If this ServerSession has an associated HLE handler, forward the request to it.
result = hle_handler->HandleSyncRequest(context);
} else {
// Add the thread to the list of threads that have issued a sync request with this
// server.
pending_requesting_threads.push_back(std::move(thread));
}
if (thread->status == THREADSTATUS_RUNNING) {
// Put the thread to sleep until the server replies, it will be awoken in
// svcReplyAndReceive for LLE servers.
thread->status = THREADSTATUS_WAIT_IPC;
if (hle_handler != nullptr) {
// For HLE services, we put the request threads to sleep for a short duration to
// simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for
// other reasons like an async callback. The IPC overhead is needed to prevent
// starvation when a thread only does sync requests to HLE services while a
// lower-priority thread is waiting to run.
// This delay was approximated in a homebrew application by measuring the average time
// it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC
// request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have
// a high variance and vary between models.
static constexpr u64 IPCDelayNanoseconds = 39000;
thread->WakeAfterDelay(IPCDelayNanoseconds);
} else {
// Add the thread to the list of threads that have issued a sync request with this
// server.
pending_requesting_threads.push_back(std::move(thread));
}
}
// If this ServerSession does not have an HLE implementation, just wake up the threads waiting
// on it.
WakeupAllWaitingThreads();
// Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the
// end of the command such that only commands following this one are handled as domains
if (convert_to_domain) {
ASSERT_MSG(domain_request_handlers.empty(), "already a domain");
domain_request_handlers = {hle_handler};
convert_to_domain = false;
}
return result;
}
@@ -103,9 +162,4 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n
return std::make_tuple(std::move(server_session), std::move(client_session));
}
ResultCode TranslateHLERequest(ServerSession* server_session) {
// TODO(Subv): Implement this function once multiple concurrent processes are supported.
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

@@ -21,6 +21,7 @@ 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
@@ -79,7 +80,10 @@ public:
std::string name; ///< The name of this session (optional)
std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint.
std::shared_ptr<SessionRequestHandler>
hle_handler; ///< This session's HLE request handler (optional)
hle_handler; ///< This session's HLE request handler (applicable when not a domain)
/// This is the list of domain request handlers (after conversion to a domain)
std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
/// List of threads that are pending a response after a sync request. This list is processed in
/// a LIFO manner, thus, the last request will be dispatched first.
@@ -91,6 +95,16 @@ public:
/// TODO(Subv): Find a better name for this.
SharedPtr<Thread> currently_handling;
/// Returns true if the session has been converted to a domain, otherwise False
bool IsDomain() const {
return !domain_request_handlers.empty();
}
/// Converts the session to a domain at the end of the current command
void ConvertToDomain() {
convert_to_domain = true;
}
private:
ServerSession();
~ServerSession() override;
@@ -102,15 +116,13 @@ private:
* @return The created server session
*/
static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown");
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
/// object handle.
ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
/// When set to True, converts the session to a domain at the end of the command
bool convert_to_domain{};
};
/**
* Performs command buffer translation for an HLE IPC request.
* The command buffer from the ServerSession thread's TLS is copied into a
* buffer and all descriptors in the buffer are processed.
* TODO(Subv): Implement this function, currently we do not support multiple processes running at
* once, but once that is implemented we'll need to properly translate all descriptors
* in the command buffer.
*/
ResultCode TranslateHLERequest(ServerSession* server_session);
} // namespace Kernel

View File

@@ -4,6 +4,7 @@
#include <cstring>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/shared_memory.h"
@@ -14,7 +15,7 @@ namespace Kernel {
SharedMemory::SharedMemory() {}
SharedMemory::~SharedMemory() {}
SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u32 size,
SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u64 size,
MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address,
MemoryRegion region, std::string name) {
@@ -51,8 +52,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
}
// Refresh the address mappings for the current process.
if (Kernel::g_current_process != nullptr) {
Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
if (Core::CurrentProcess() != nullptr) {
Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
}
} else {
auto& vm_manager = shared_memory->owner_process->vm_manager;
@@ -106,14 +107,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
// Error out if the requested permissions don't match what the creator process allows.
if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match",
GetObjectId(), address, name.c_str());
return ERR_INVALID_COMBINATION;
}
// Heap-backed memory blocks can not be mapped with other_permissions = DontCare
if (base_address != 0 && other_permissions == MemoryPermission::DontCare) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match",
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, permissions don't match",
GetObjectId(), address, name.c_str());
return ERR_INVALID_COMBINATION;
}
@@ -121,23 +115,18 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
// Error out if the provided permissions are not compatible with what the creator process needs.
if (other_permissions != MemoryPermission::DontCare &&
static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match",
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, permissions don't match",
GetObjectId(), address, name.c_str());
return ERR_WRONG_PERMISSION;
}
// TODO(Subv): Check for the Shared Device Mem flag in the creator process.
/*if (was_created_with_shared_device_mem && address != 0) {
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}*/
// TODO(Subv): The same process that created a SharedMemory object
// can not map it in its own address space unless it was created with addr=0, result 0xD900182C.
if (address != 0) {
if (address < Memory::HEAP_VADDR) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, invalid address",
// TODO(shinyquagsire23): Check for virtual/mappable memory here too?
if (address >= Memory::HEAP_VADDR && address < Memory::HEAP_VADDR_END) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, invalid address",
GetObjectId(), address, name.c_str());
return ERR_INVALID_ADDRESS;
}
@@ -154,10 +143,9 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
auto result = target_process->vm_manager.MapMemoryBlock(
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
if (result.Failed()) {
LOG_ERROR(
Kernel,
"cannot map id=%u, target_address=0x%llx name=%s, error mapping to virtual memory",
GetObjectId(), target_address, name.c_str());
LOG_ERROR(Kernel,
"cannot map id=%u, target_address=0x%lx name=%s, error mapping to virtual memory",
GetObjectId(), target_address, name.c_str());
return result.Code();
}

View File

@@ -39,7 +39,7 @@ public:
* linear heap.
* @param name Optional object name, used for debugging purposes.
*/
static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u32 size,
static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u64 size,
MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address = 0,
MemoryRegion region = MemoryRegion::BASE,
@@ -116,7 +116,7 @@ public:
/// Offset into the backing block for this shared memory.
size_t backing_block_offset;
/// Size of the memory block. Page-aligned.
u32 size;
u64 size;
/// Permission restrictions applied to the process which created the block.
MemoryPermission permissions;
/// Permission restrictions applied to other processes mapping the block.

View File

@@ -3,10 +3,12 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
@@ -20,7 +22,6 @@
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_wrap.h"
#include "core/hle/kernel/sync_object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
@@ -31,14 +32,14 @@ namespace Kernel {
/// Set the process heap to a given Size. It can both extend and shrink the heap.
static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
LOG_TRACE(Kernel_SVC, "called, heap_size=0x%llx", heap_size);
auto& process = *g_current_process;
auto& process = *Core::CurrentProcess();
CASCADE_RESULT(*heap_addr,
process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite));
return RESULT_SUCCESS;
}
static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x%llx", addr);
LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x%lx", addr);
return RESULT_SUCCESS;
}
@@ -46,14 +47,14 @@ static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state
static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr,
src_addr, size);
return g_current_process->MirrorMemory(dst_addr, src_addr, size);
return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size);
}
/// Unmaps a region that was previously mapped with svcMapMemory
static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr,
src_addr, size);
return g_current_process->UnmapMemory(dst_addr, src_addr, size);
return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -87,7 +88,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
/// Makes a blocking IPC call to an OS service.
static ResultCode SendSyncRequest(Handle handle) {
SharedPtr<SyncObject> session = g_handle_table.Get<SyncObject>(handle);
SharedPtr<ClientSession> session = g_handle_table.Get<ClientSession>(handle);
if (!session) {
LOG_ERROR(Kernel_SVC, "called with invalid handle=0x%08X", handle);
return ERR_INVALID_HANDLE;
@@ -264,6 +265,7 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle);
ASSERT(requesting_thread);
ASSERT(requesting_thread == GetCurrentThread());
SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr);
if (!mutex) {
@@ -305,23 +307,23 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
LOG_TRACE(Kernel_SVC, "called info_id=0x%X, info_sub_id=0x%X, handle=0x%08X", info_id,
info_sub_id, handle);
auto& vm_manager = g_current_process->vm_manager;
auto& vm_manager = Core::CurrentProcess()->vm_manager;
switch (static_cast<GetInfoType>(info_id)) {
case GetInfoType::AllowedCpuIdBitmask:
*result = g_current_process->allowed_processor_mask;
*result = Core::CurrentProcess()->allowed_processor_mask;
break;
case GetInfoType::AllowedThreadPrioBitmask:
*result = g_current_process->allowed_thread_priority_mask;
*result = Core::CurrentProcess()->allowed_thread_priority_mask;
break;
case GetInfoType::MapRegionBaseAddr:
*result = vm_manager.GetMapRegionBaseAddr();
*result = Memory::MAP_REGION_VADDR;
break;
case GetInfoType::MapRegionSize:
*result = vm_manager.GetAddressSpaceSize();
*result = Memory::MAP_REGION_SIZE;
break;
case GetInfoType::HeapRegionBaseAddr:
*result = vm_manager.GetNewMapRegionBaseAddr() + vm_manager.GetNewMapRegionSize();
*result = Memory::HEAP_VADDR;
break;
case GetInfoType::HeapRegionSize:
*result = Memory::HEAP_SIZE;
@@ -332,6 +334,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
case GetInfoType::TotalHeapUsage:
*result = vm_manager.GetTotalHeapUsage();
break;
case GetInfoType::IsCurrentProcessBeingDebugged:
*result = 0;
break;
case GetInfoType::RandomEntropy:
*result = 0;
break;
@@ -342,13 +347,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*result = vm_manager.GetAddressSpaceSize();
break;
case GetInfoType::NewMapRegionBaseAddr:
*result = vm_manager.GetNewMapRegionBaseAddr();
*result = Memory::NEW_MAP_REGION_VADDR;
break;
case GetInfoType::NewMapRegionSize:
*result = vm_manager.GetNewMapRegionSize();
*result = Memory::NEW_MAP_REGION_SIZE;
break;
case GetInfoType::IsVirtualAddressMemoryEnabled:
*result = g_current_process->is_virtual_address_memory_enabled;
*result = Core::CurrentProcess()->is_virtual_address_memory_enabled;
break;
case GetInfoType::TitleId:
LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0");
@@ -388,7 +393,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
// Note: The kernel uses the current process's resource limit instead of
// the one from the thread owner's resource limit.
SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit;
SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
return ERR_NOT_AUTHORIZED;
}
@@ -416,8 +421,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
"called, shared_memory_handle=0x%08X, addr=0x%llx, size=0x%llx, permissions=0x%08X",
shared_memory_handle, addr, size, permissions);
SharedPtr<SharedMemory> shared_memory =
Kernel::g_handle_table.Get<SharedMemory>(shared_memory_handle);
SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
return ERR_INVALID_HANDLE;
}
@@ -432,7 +436,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
case MemoryPermission::WriteExecute:
case MemoryPermission::ReadWriteExecute:
case MemoryPermission::DontCare:
return shared_memory->Map(Kernel::g_current_process.get(), addr, permissions_type,
return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type,
MemoryPermission::DontCare);
default:
LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
@@ -441,6 +445,16 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
return RESULT_SUCCESS;
}
static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
LOG_WARNING(Kernel_SVC,
"called, shared_memory_handle=0x%08X, addr=0x%" PRIx64 ", size=0x%" PRIx64 "",
shared_memory_handle, addr, size);
SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle);
return shared_memory->Unmap(Core::CurrentProcess().get(), addr);
}
/// Query process memory
static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
Handle process_handle, u64 addr) {
@@ -450,11 +464,11 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i
}
auto vma = process->vm_manager.FindVMA(addr);
memory_info->attributes = 0;
if (vma == g_current_process->vm_manager.vma_map.end()) {
if (vma == Core::CurrentProcess()->vm_manager.vma_map.end()) {
memory_info->base_address = 0;
memory_info->permission = static_cast<u32>(VMAPermission::None);
memory_info->size = 0;
memory_info->type = static_cast<u32>(MemoryState::Free);
memory_info->type = static_cast<u32>(MemoryState::Unmapped);
} else {
memory_info->base_address = vma->second.base;
memory_info->permission = static_cast<u32>(vma->second.permissions);
@@ -474,16 +488,17 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
/// Exits the current process
static void ExitProcess() {
LOG_INFO(Kernel_SVC, "Process %u exiting", g_current_process->process_id);
LOG_INFO(Kernel_SVC, "Process %u exiting", Core::CurrentProcess()->process_id);
ASSERT_MSG(g_current_process->status == ProcessStatus::Running, "Process has already exited");
ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running,
"Process has already exited");
g_current_process->status = ProcessStatus::Exited;
Core::CurrentProcess()->status = ProcessStatus::Exited;
// Stop all the process threads that are currently waiting for objects.
auto& thread_list = GetThreadList();
auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
for (auto& thread : thread_list) {
if (thread->owner_process != g_current_process)
if (thread->owner_process != Core::CurrentProcess())
continue;
if (thread == GetCurrentThread())
@@ -512,14 +527,14 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
return ERR_OUT_OF_RANGE;
}
SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit;
SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
return ERR_NOT_AUTHORIZED;
}
if (processor_id == THREADPROCESSORID_DEFAULT) {
// Set the target CPU to the one specified in the process' exheader.
processor_id = g_current_process->ideal_processor;
processor_id = Core::CurrentProcess()->ideal_processor;
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
}
@@ -541,7 +556,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
CASCADE_RESULT(SharedPtr<Thread> thread,
Thread::Create(name, entry_point, priority, arg, processor_id, stack_top,
g_current_process));
Core::CurrentProcess()));
CASCADE_RESULT(thread->guest_handle, g_handle_table.Create(thread));
*out_handle = thread->guest_handle;
@@ -583,7 +598,7 @@ static void SleepThread(s64 nanoseconds) {
// Don't attempt to yield execution if there are no available threads to run,
// this way we avoid a useless reschedule to the idle thread.
if (nanoseconds == 0 && !HaveReadyThreads())
if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads())
return;
// Sleep current thread and check for next thread to schedule
@@ -613,20 +628,29 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr);
}
ASSERT(mutex->GetOwnerHandle() == thread_handle);
SharedPtr<ConditionVariable> condition_variable =
g_object_address_table.Get<ConditionVariable>(condition_variable_addr);
if (!condition_variable) {
// Create a new condition_variable for the specified address if one does not already exist
condition_variable =
ConditionVariable::Create(condition_variable_addr, mutex_addr).Unwrap();
condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap();
condition_variable->name =
Common::StringFromFormat("condition-variable-%llx", condition_variable_addr);
}
ASSERT(condition_variable->GetAvailableCount() == 0);
ASSERT(condition_variable->mutex_addr == mutex_addr);
if (condition_variable->mutex_addr) {
// Previously created the ConditionVariable using WaitProcessWideKeyAtomic, verify
// everything is correct
ASSERT(condition_variable->mutex_addr == mutex_addr);
} else {
// Previously created the ConditionVariable using SignalProcessWideKey, set the mutex
// associated with it
condition_variable->mutex_addr = mutex_addr;
}
if (mutex->GetOwnerHandle()) {
// Release the mutex if the current thread is holding it
mutex->Release(thread.get());
}
auto wakeup_callback = [mutex, nano_seconds](ThreadWakeupReason reason,
SharedPtr<Thread> thread,
@@ -668,8 +692,6 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
CASCADE_CODE(
WaitSynchronization1(condition_variable, thread.get(), nano_seconds, wakeup_callback));
mutex->Release(thread.get());
return RESULT_SUCCESS;
}
@@ -728,7 +750,7 @@ static ResultCode ResetSignal(Handle handle) {
/// Creates a TransferMemory object
static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%llx, size=0x%llx, perms=%08X", addr, size,
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%lx, size=0x%lx, perms=%08X", addr, size,
permissions);
*handle = 0;
return RESULT_SUCCESS;
@@ -739,18 +761,29 @@ static ResultCode SetThreadCoreMask(u64, u64, u64) {
return RESULT_SUCCESS;
}
static ResultCode CreateSharedMemory(Handle* handle, u64 sz, u32 local_permissions,
static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permissions,
u32 remote_permissions) {
LOG_TRACE(Kernel_SVC, "called, sz=0x%llx, localPerms=0x%08x, remotePerms=0x%08x", sz,
LOG_TRACE(Kernel_SVC, "called, size=0x%llx, localPerms=0x%08x, remotePerms=0x%08x", size,
local_permissions, remote_permissions);
auto sharedMemHandle = SharedMemory::Create(
g_handle_table.Get<Process>(KernelHandle::CurrentProcess), sz,
(Kernel::MemoryPermission)local_permissions, (Kernel::MemoryPermission)remote_permissions);
auto sharedMemHandle =
SharedMemory::Create(g_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));
return RESULT_SUCCESS;
}
static ResultCode ClearEvent(Handle handle) {
LOG_TRACE(Kernel_SVC, "called, event=0xX", handle);
SharedPtr<Event> evt = g_handle_table.Get<Event>(handle);
if (evt == nullptr)
return ERR_INVALID_HANDLE;
evt->Clear();
return RESULT_SUCCESS;
}
namespace {
struct FunctionDef {
using Func = void();
@@ -780,9 +813,9 @@ static const FunctionDef SVC_Table[] = {
{0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"},
{0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
{0x11, nullptr, "SignalEvent"},
{0x12, nullptr, "ClearEvent"},
{0x12, SvcWrap<ClearEvent>, "ClearEvent"},
{0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"},
{0x14, nullptr, "UnmapSharedMemory"},
{0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"},
{0x15, SvcWrap<CreateTransferMemory>, "CreateTransferMemory"},
{0x16, SvcWrap<CloseHandle>, "CloseHandle"},
{0x17, SvcWrap<ResetSignal>, "ResetSignal"},

View File

@@ -91,6 +91,11 @@ void SvcWrap() {
FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2), (u32)PARAM(3)).raw);
}
template <ResultCode func(u32, u64, u64)>
void SvcWrap() {
FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2)).raw);
}
template <ResultCode func(u32*, u64, u64, s64)>
void SvcWrap() {
u32 param_1 = 0;

View File

@@ -1,35 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
namespace Kernel {
class Thread;
/// Class that represents a Kernel object that svcSendSyncRequest can be called on
class SyncObject : public Object {
public:
/**
* Handle a sync request from the emulated application.
* @param thread Thread that initiated the request.
* @returns ResultCode from the operation.
*/
virtual ResultCode SendSyncRequest(SharedPtr<Thread> thread) = 0;
};
// Specialization of DynamicObjectCast for SyncObjects
template <>
inline SharedPtr<SyncObject> DynamicObjectCast<SyncObject>(SharedPtr<Object> object) {
if (object != nullptr && object->IsSyncable()) {
return boost::static_pointer_cast<SyncObject>(std::move(object));
}
return nullptr;
}
} // namespace Kernel

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include <list>
#include <vector>
#include "common/assert.h"
@@ -40,14 +41,6 @@ void Thread::Acquire(Thread* thread) {
// us to simply use a pool index or similar.
static Kernel::HandleTable wakeup_callback_handle_table;
// Lists all thread ids that aren't deleted/etc.
static std::vector<SharedPtr<Thread>> thread_list;
// Lists only ready thread ids.
static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
static SharedPtr<Thread> current_thread;
// The first available thread id at startup
static u32 next_thread_id;
@@ -62,20 +55,6 @@ inline static u32 const NewThreadId() {
Thread::Thread() {}
Thread::~Thread() {}
Thread* GetCurrentThread() {
return current_thread.get();
}
/**
* Check if the specified thread is waiting on the specified address to be arbitrated
* @param thread The thread to test
* @param wait_address The address to test against
* @return True if the thread is waiting, false otherwise
*/
static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address;
}
void Thread::Stop() {
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
@@ -85,7 +64,7 @@ void Thread::Stop() {
// Clean up thread from ready queue
// This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
if (status == THREADSTATUS_READY) {
ready_queue.remove(current_priority, this);
Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority);
}
status = THREADSTATUS_DEAD;
@@ -105,113 +84,7 @@ void Thread::Stop() {
u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
u64 tls_slot =
((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot);
}
Thread* ArbitrateHighestPriorityThread(u32 address) {
Thread* highest_priority_thread = nullptr;
u32 priority = THREADPRIO_LOWEST;
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
for (auto& thread : thread_list) {
if (!CheckWait_AddressArbiter(thread.get(), address))
continue;
if (thread == nullptr)
continue;
if (thread->current_priority <= priority) {
highest_priority_thread = thread.get();
priority = thread->current_priority;
}
}
// If a thread was arbitrated, resume it
if (nullptr != highest_priority_thread) {
highest_priority_thread->ResumeFromWait();
}
return highest_priority_thread;
}
void ArbitrateAllThreads(u32 address) {
// Resume all threads found to be waiting on the address
for (auto& thread : thread_list) {
if (CheckWait_AddressArbiter(thread.get(), address))
thread->ResumeFromWait();
}
}
/**
* Switches the CPU's active thread context to that of the specified thread
* @param new_thread The thread to switch to
*/
static void SwitchContext(Thread* new_thread) {
Thread* previous_thread = GetCurrentThread();
// Save context for previous thread
if (previous_thread) {
previous_thread->last_running_ticks = CoreTiming::GetTicks();
Core::CPU().SaveContext(previous_thread->context);
if (previous_thread->status == THREADSTATUS_RUNNING) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
ready_queue.push_front(previous_thread->current_priority, previous_thread);
previous_thread->status = THREADSTATUS_READY;
}
}
// Load context of new thread
if (new_thread) {
ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
"Thread must be ready to become running.");
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
auto previous_process = Kernel::g_current_process;
current_thread = new_thread;
ready_queue.remove(new_thread->current_priority, new_thread);
new_thread->status = THREADSTATUS_RUNNING;
if (previous_process != current_thread->owner_process) {
Kernel::g_current_process = current_thread->owner_process;
SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
}
Core::CPU().LoadContext(new_thread->context);
Core::CPU().SetTlsAddress(new_thread->GetTLSAddress());
} else {
current_thread = nullptr;
// Note: We do not reset the current process and current page table when idling because
// technically we haven't changed processes, our threads are just paused.
}
}
/**
* Pops and returns the next thread from the thread queue
* @return A pointer to the next ready thread
*/
static Thread* PopNextReadyThread() {
Thread* next;
Thread* thread = GetCurrentThread();
if (thread && thread->status == THREADSTATUS_RUNNING) {
// We have to do better than the current thread.
// This call returns null when that's not possible.
next = ready_queue.pop_first_better(thread->current_priority);
if (!next) {
// Otherwise just keep going with the current thread
next = thread;
}
} else {
next = ready_queue.pop_first();
}
return next;
Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
}
void WaitCurrentThread_Sleep() {
@@ -219,17 +92,10 @@ void WaitCurrentThread_Sleep() {
thread->status = THREADSTATUS_WAIT_SLEEP;
}
void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
Thread* thread = GetCurrentThread();
thread->wait_address = wait_address;
thread->status = THREADSTATUS_WAIT_ARB;
}
void ExitCurrentThread() {
Thread* thread = GetCurrentThread();
thread->Stop();
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
Core::System::GetInstance().Scheduler().RemoveThread(thread);
}
/**
@@ -247,7 +113,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
bool resume = true;
if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) {
thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
thread->status == THREADSTATUS_WAIT_HLE_EVENT) {
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects)
@@ -281,8 +148,9 @@ void Thread::ResumeFromWait() {
switch (status) {
case THREADSTATUS_WAIT_SYNCH_ALL:
case THREADSTATUS_WAIT_SYNCH_ANY:
case THREADSTATUS_WAIT_ARB:
case THREADSTATUS_WAIT_HLE_EVENT:
case THREADSTATUS_WAIT_SLEEP:
case THREADSTATUS_WAIT_IPC:
break;
case THREADSTATUS_READY:
@@ -306,31 +174,11 @@ void Thread::ResumeFromWait() {
wakeup_callback = nullptr;
ready_queue.push_back(current_priority, this);
status = THREADSTATUS_READY;
Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority);
Core::System::GetInstance().PrepareReschedule();
}
/**
* Prints the thread queue for debugging purposes
*/
static void DebugThreadQueue() {
Thread* thread = GetCurrentThread();
if (!thread) {
LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD");
} else {
LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority,
GetCurrentThread()->GetObjectId());
}
for (auto& t : thread_list) {
u32 priority = ready_queue.contains(t.get());
if (priority != -1) {
LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId());
}
}
}
/**
* Finds a free location for the TLS section of a thread.
* @param tls_slots The TLS page array of the thread's owner process.
@@ -379,7 +227,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
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: %d", priority);
LOG_ERROR(Kernel_SVC, "Invalid thread priority: %u", priority);
return ERR_OUT_OF_RANGE;
}
@@ -391,15 +239,14 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
// TODO(yuriks): Other checks, returning 0xD9001BEA
if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %016" PRIx64, name.c_str(), entry_point);
// TODO (bunnei): Find the correct error code to use here
return ResultCode(-1);
}
SharedPtr<Thread> thread(new Thread);
thread_list.push_back(thread);
ready_queue.prepare(priority);
Core::System::GetInstance().Scheduler().AddThread(thread, priority);
thread->thread_id = NewThreadId();
thread->status = THREADSTATUS_DORMANT;
@@ -452,7 +299,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
// TODO(Subv): Find the correct MemoryState for this region.
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
linheap_memory, offset, Memory::PAGE_SIZE,
MemoryState::ThreadLocalStorage);
MemoryState::ThreadLocal);
}
// Mark the slot as used
@@ -470,12 +317,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
void Thread::SetPriority(u32 priority) {
ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
"Invalid priority value.");
// If thread was ready, adjust queues
if (status == THREADSTATUS_READY)
ready_queue.move(this, current_priority, priority);
else
ready_queue.prepare(priority);
Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
nominal_priority = current_priority = priority;
}
@@ -489,22 +331,18 @@ void Thread::UpdatePriority() {
}
void Thread::BoostPriority(u32 priority) {
// If thread was ready, adjust queues
if (status == THREADSTATUS_READY)
ready_queue.move(this, current_priority, priority);
else
ready_queue.prepare(priority);
Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
current_priority = priority;
}
SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process) {
// Setup page table so we can write to memory
SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
// Initialize new "main" thread
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
Memory::HEAP_VADDR_END, owner_process);
Memory::STACK_VADDR_END, owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
@@ -519,25 +357,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
return thread;
}
bool HaveReadyThreads() {
return ready_queue.get_first() != nullptr;
}
void Reschedule() {
Thread* cur = GetCurrentThread();
Thread* next = PopNextReadyThread();
if (cur && next) {
LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
} else if (cur) {
LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId());
} else if (next) {
LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId());
}
SwitchContext(next);
}
void Thread::SetWaitSynchronizationResult(ResultCode result) {
context.cpu_registers[0] = result.raw;
}
@@ -560,25 +379,20 @@ VAddr Thread::GetCommandBufferAddress() const {
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the current thread
*/
Thread* GetCurrentThread() {
return Core::System::GetInstance().Scheduler().GetCurrentThread();
}
void ThreadingInit() {
ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
current_thread = nullptr;
next_thread_id = 1;
}
void ThreadingShutdown() {
current_thread = nullptr;
for (auto& t : thread_list) {
t->Stop();
}
thread_list.clear();
ready_queue.clear();
}
const std::vector<SharedPtr<Thread>>& GetThreadList() {
return thread_list;
Kernel::ClearProcessList();
}
} // namespace Kernel

View File

@@ -38,8 +38,9 @@ enum ThreadProcessorId : s32 {
enum ThreadStatus {
THREADSTATUS_RUNNING, ///< Currently running
THREADSTATUS_READY, ///< Ready to run
THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter
THREADSTATUS_WAIT_HLE_EVENT, ///< Waiting for hle event to finish
THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC
THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
THREADSTATUS_DORMANT, ///< Created but not yet made ready
@@ -248,28 +249,6 @@ private:
SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process);
/**
* Returns whether there are any threads that are ready to run.
*/
bool HaveReadyThreads();
/**
* Reschedules to the next available thread (call after current thread is suspended)
*/
void Reschedule();
/**
* Arbitrate the highest priority thread that is waiting
* @param address The address for which waiting threads should be arbitrated
*/
Thread* ArbitrateHighestPriorityThread(VAddr address);
/**
* Arbitrate all threads currently waiting.
* @param address The address for which waiting threads should be arbitrated
*/
void ArbitrateAllThreads(VAddr address);
/**
* Gets the current thread
*/
@@ -301,9 +280,4 @@ void ThreadingInit();
*/
void ThreadingShutdown();
/**
* Get a const reference to the thread list for debug use
*/
const std::vector<SharedPtr<Thread>>& GetThreadList();
} // namespace Kernel

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <iterator>
#include "common/assert.h"
#include "common/logging/log.h"
@@ -10,15 +11,33 @@
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
#include "core/memory_hook.h"
#include "core/memory_setup.h"
#include "core/mmio.h"
namespace Kernel {
static const char* GetMemoryStateName(MemoryState state) {
static const char* names[] = {
"Free", "Reserved", "IO", "Static", "Code", "Private",
"Shared", "Continuous", "Aliased", "Alias", "AliasCode", "Locked",
"Unmapped",
"Io",
"Normal",
"CodeStatic",
"CodeMutable",
"Heap",
"Shared",
"Unknown1"
"ModuleCodeStatic",
"ModuleCodeMutable",
"IpcBuffer0",
"Mapped",
"ThreadLocal",
"TransferMemoryIsolated",
"TransferMemory",
"ProcessMemory",
"Unknown2"
"IpcBuffer1",
"IpcBuffer3",
"KernelStack",
};
return names[(int)state];
@@ -60,8 +79,8 @@ void VMManager::Reset() {
vma_map.emplace(initial_vma.base, initial_vma);
page_table.pointers.fill(nullptr);
page_table.special_regions.clear();
page_table.attributes.fill(Memory::PageType::Unmapped);
page_table.cached_res_count.fill(0);
UpdatePageTableForVMA(initial_vma);
}
@@ -121,7 +140,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
MemoryState state,
Memory::MMIORegionPointer mmio_handler) {
Memory::MemoryHookPointer mmio_handler) {
// This is the appropriately sized VMA that will turn into our allocation.
CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
VirtualMemoryArea& final_vma = vma_handle->second;
@@ -141,7 +160,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
VirtualMemoryArea& vma = vma_handle->second;
vma.type = VMAType::Free;
vma.permissions = VMAPermission::None;
vma.meminfo_state = MemoryState::Free;
vma.meminfo_state = MemoryState::Unmapped;
vma.backing_block = nullptr;
vma.offset = 0;
@@ -165,6 +184,9 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
}
ASSERT(FindVMA(target)->second.size >= size);
Core::CPU().UnmapMemory(target, size);
return RESULT_SUCCESS;
}
@@ -206,7 +228,8 @@ void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
void VMManager::LogLayout(Log::Level log_level) const {
for (const auto& p : vma_map) {
const VirtualMemoryArea& vma = p.second;
LOG_GENERIC(Log::Class::Kernel, log_level, "%08X - %08X size: %8X %c%c%c %s", vma.base,
LOG_GENERIC(Log::Class::Kernel, log_level,
"%016" PRIx64 " - %016" PRIx64 " size: %16" PRIx64 " %c%c%c %s", vma.base,
vma.base + vma.size, vma.size,
(u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-',
(u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-',
@@ -222,8 +245,8 @@ VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle& iter) {
}
ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) {
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size);
ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", base);
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%16" PRIx64, size);
ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%016" PRIx64, base);
VMAIter vma_handle = StripIterConstness(FindVMA(base));
if (vma_handle == vma_map.end()) {
@@ -258,8 +281,8 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) {
}
ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) {
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size);
ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", target);
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%16" PRIx64, size);
ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%016" PRIx64, target);
VAddr target_end = target + size;
ASSERT(target_end >= target);
@@ -375,19 +398,4 @@ u64 VMManager::GetAddressSpaceSize() {
return MAX_ADDRESS;
}
VAddr VMManager::GetMapRegionBaseAddr() {
LOG_WARNING(Kernel, "(STUBBED) called");
return Memory::HEAP_VADDR;
}
VAddr VMManager::GetNewMapRegionBaseAddr() {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0x8000000;
}
u64 VMManager::GetNewMapRegionSize() {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0x8000000;
}
} // namespace Kernel

View File

@@ -10,7 +10,7 @@
#include "common/common_types.h"
#include "core/hle/result.h"
#include "core/memory.h"
#include "core/mmio.h"
#include "core/memory_hook.h"
namespace Kernel {
@@ -41,15 +41,24 @@ enum class VMAPermission : u8 {
/// Set of values returned in MemoryInfo.state by svcQueryMemory.
enum class MemoryState : u32 {
Free = 0,
IO = 1,
Normal = 2,
Code = 3,
Static = 4,
Heap = 5,
Shared = 6,
Mapped = 6,
ThreadLocalStorage = 12,
Unmapped = 0x0,
Io = 0x1,
Normal = 0x2,
CodeStatic = 0x3,
CodeMutable = 0x4,
Heap = 0x5,
Shared = 0x6,
ModuleCodeStatic = 0x8,
ModuleCodeMutable = 0x9,
IpcBuffer0 = 0xA,
Mapped = 0xB,
ThreadLocal = 0xC,
TransferMemoryIsolated = 0xD,
TransferMemory = 0xE,
ProcessMemory = 0xF,
IpcBuffer1 = 0x11,
IpcBuffer3 = 0x12,
KernelStack = 0x13,
};
/**
@@ -66,7 +75,7 @@ struct VirtualMemoryArea {
VMAType type = VMAType::Free;
VMAPermission permissions = VMAPermission::None;
/// Tag returned by svcQueryMemory. Not otherwise used.
MemoryState meminfo_state = MemoryState::Free;
MemoryState meminfo_state = MemoryState::Unmapped;
// Settings for type = AllocatedMemoryBlock
/// Memory block backing this VMA.
@@ -81,7 +90,7 @@ struct VirtualMemoryArea {
// Settings for type = MMIO
/// Physical address of the register area this VMA maps to.
PAddr paddr = 0;
Memory::MMIORegionPointer mmio_handler = nullptr;
Memory::MemoryHookPointer mmio_handler = nullptr;
/// Tests if this area can be merged to the right with `next`.
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
@@ -160,7 +169,7 @@ public:
* @param mmio_handler The handler that will implement read and write for this MMIO region.
*/
ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state,
Memory::MMIORegionPointer mmio_handler);
Memory::MemoryHookPointer mmio_handler);
/// Unmaps a range of addresses, splitting VMAs as necessary.
ResultCode UnmapRange(VAddr target, u64 size);
@@ -192,15 +201,6 @@ public:
/// Gets the total address space address size, used by svcGetInfo
u64 GetAddressSpaceSize();
/// Gets the map region base address, used by svcGetInfo
VAddr GetMapRegionBaseAddr();
/// Gets the base address for a new memory region, used by svcGetInfo
VAddr GetNewMapRegionBaseAddr();
/// Gets the size for a new memory region, used by svcGetInfo
u64 GetNewMapRegionSize();
/// Each VMManager has its own page table, which is set as the main one when the owning process
/// is scheduled.
Memory::PageTable page_table;

View File

@@ -39,7 +39,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
for (const auto& thread : waiting_threads) {
// The list of waiting threads must not contain threads that are not waiting to be awakened.
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
thread->status == THREADSTATUS_WAIT_HLE_EVENT,
"Inconsistent thread statuses in waiting_threads");
if (thread->current_priority >= candidate_priority)

View File

@@ -19,6 +19,8 @@
enum class ErrorDescription : u32 {
Success = 0,
RemoteProcessDead = 301,
InvalidOffset = 6061,
InvalidLength = 6062,
};
/**
@@ -106,11 +108,11 @@ union ResultCode {
}
constexpr bool IsSuccess() const {
return is_error.ExtractValue(raw) == 0;
return raw == 0;
}
constexpr bool IsError() const {
return is_error.ExtractValue(raw) == 1;
return raw != 0;
}
};

View File

@@ -9,6 +9,9 @@
namespace Service {
namespace Account {
using Uid = std::array<u64, 2>;
static constexpr Uid DEFAULT_USER_ID{0x10ull, 0x20ull};
class IProfile final : public ServiceFramework<IProfile> {
public:
IProfile() : ServiceFramework("IProfile") {
@@ -20,9 +23,9 @@ public:
private:
void GetBase(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
LOG_WARNING(Service_ACC, "(STUBBED) called");
ProfileBase profile_base{};
IPC::RequestBuilder rb{ctx, 16};
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
}
@@ -30,52 +33,86 @@ private:
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
public:
IManagerForApplication() : ServiceFramework("IProfile") {
IManagerForApplication() : ServiceFramework("IManagerForApplication") {
static const FunctionInfo functions[] = {
{0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
{1, &IManagerForApplication::GetAccountId, "GetAccountId"},
};
RegisterHandlers(functions);
}
private:
void CheckAvailability(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 3};
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(true); // TODO: Check when this is supposed to return true and when not
}
void GetAccountId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0x12345678ABCDEF);
}
};
void ACC_U0::GetUserExistence(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 3};
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(true); // TODO: Check when this is supposed to return true and when not
}
void ACC_U0::ListAllUsers(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID};
ctx.WriteBuffer(user_ids.data(), user_ids.size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ACC_U0::ListOpenUsers(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID};
ctx.WriteBuffer(user_ids.data(), user_ids.size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ACC_U0::GetProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IProfile>();
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_ACC, "called");
}
void ACC_U0::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2};
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ACC_U0::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IManagerForApplication>();
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_ACC, "called");
}
void ACC_U0::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(DEFAULT_USER_ID);
}
ACC_U0::ACC_U0() : ServiceFramework("acc:u0") {
static const FunctionInfo functions[] = {
{1, &ACC_U0::GetUserExistence, "GetUserExistence"},
{2, &ACC_U0::ListAllUsers, "ListAllUsers"},
{3, &ACC_U0::ListOpenUsers, "ListOpenUsers"},
{4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"},
{5, &ACC_U0::GetProfile, "GetProfile"},
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},

View File

@@ -28,6 +28,9 @@ public:
private:
void GetUserExistence(Kernel::HLERequestContext& ctx);
void ListAllUsers(Kernel::HLERequestContext& ctx);
void ListOpenUsers(Kernel::HLERequestContext& ctx);
void GetLastOpenedUser(Kernel::HLERequestContext& ctx);
void GetProfile(Kernel::HLERequestContext& ctx);
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);

View File

@@ -2,14 +2,489 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "core/file_sys/filesystem.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/nvflinger/nvflinger.h"
namespace Service {
namespace AM {
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<AppletOE>()->InstallAsService(service_manager);
IWindowController::IWindowController() : ServiceFramework("IWindowController") {
static const FunctionInfo functions[] = {
{1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
{10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
};
RegisterHandlers(functions);
}
void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
}
void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
IAudioController::IAudioController() : ServiceFramework("IAudioController") {
static const FunctionInfo functions[] = {
{0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
{1, &IAudioController::GetMainAppletExpectedMasterVolume,
"GetMainAppletExpectedMasterVolume"},
{2, &IAudioController::GetLibraryAppletExpectedMasterVolume,
"GetLibraryAppletExpectedMasterVolume"},
{3, nullptr, "ChangeMainAppletMasterVolume"},
{4, nullptr, "SetTransparentVolumeRate"},
};
RegisterHandlers(functions);
}
void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(volume);
}
void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(volume);
}
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {}
IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {}
ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
static const FunctionInfo functions[] = {
{1, &ISelfController::LockExit, "LockExit"},
{2, &ISelfController::UnlockExit, "UnlockExit"},
{9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
{10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
{11, &ISelfController::SetOperationModeChangedNotification,
"SetOperationModeChangedNotification"},
{12, &ISelfController::SetPerformanceModeChangedNotification,
"SetPerformanceModeChangedNotification"},
{13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
{14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
{16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"},
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
};
RegisterHandlers(functions);
launchable_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "ISelfController:LaunchableEvent");
}
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
// Takes 3 input u8s with each field located immediately after the previous u8, these are
// bool flags. No output.
IPC::RequestParser rp{ctx};
struct FocusHandlingModeParams {
u8 unknown0;
u8 unknown1;
u8 unknown2;
};
auto flags = rp.PopRaw<FocusHandlingModeParams>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
bool flag = rp.Pop<bool>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called flag=%u", static_cast<u32>(flag));
}
void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
bool flag = rp.Pop<bool>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called flag=%u", static_cast<u32>(flag));
}
void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
// Takes 3 input u8s with each field located immediately after the previous u8, these are
// bool flags. No output.
IPC::RequestParser rp{ctx};
bool enabled = rp.Pop<bool>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called enabled=%u", static_cast<u32>(enabled));
}
void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
launchable_event->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(launchable_event);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
// TODO(Subv): Find out how AM determines the display to use, for now just create the layer
// in the Default display.
u64 display_id = nvflinger->OpenDisplay("Default");
u64 layer_id = nvflinger->CreateLayer(display_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
static const FunctionInfo functions[] = {
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
{1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
{5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"},
{6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"},
{9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"},
};
RegisterHandlers(functions);
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "ICommonStateGetter:Event");
}
void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
event->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(15);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u8>(FocusState::InFocus));
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u8>(OperationMode::Handheld));
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(APM::PerformanceMode::Handheld));
LOG_WARNING(Service_AM, "(STUBBED) called");
}
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
public:
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
static const FunctionInfo functions[] = {
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
{1, nullptr, "IsCompleted"},
{10, nullptr, "Start"},
{20, nullptr, "RequestExit"},
{25, nullptr, "Terminate"},
{30, nullptr, "GetResult"},
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
{100, nullptr, "PushInData"},
{101, nullptr, "PopOutData"},
{102, nullptr, "PushExtraStorage"},
{103, nullptr, "PushInteractiveInData"},
{104, nullptr, "PopInteractiveOutData"},
{105, nullptr, "GetPopOutDataEvent"},
{106, nullptr, "GetPopInteractiveOutDataEvent"},
{120, nullptr, "NeedsToExitProcess"},
{120, nullptr, "GetLibraryAppletInfo"},
{150, nullptr, "RequestForAppletToGetForeground"},
{160, nullptr, "GetIndirectLayerConsumerHandle"},
};
RegisterHandlers(functions);
state_changed_event = Kernel::Event::Create(Kernel::ResetType::OneShot,
"ILibraryAppletAccessor:StateChangedEvent");
}
private:
void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
state_changed_event->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(state_changed_event);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
Kernel::SharedPtr<Kernel::Event> state_changed_event;
};
ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
static const FunctionInfo functions[] = {
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
{1, nullptr, "TerminateAllLibraryApplets"},
{2, nullptr, "AreAnyLibraryAppletsLeft"},
{10, nullptr, "CreateStorage"},
{11, nullptr, "CreateTransferMemoryStorage"},
{12, nullptr, "CreateHandleStorage"},
};
RegisterHandlers(functions);
}
void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::ILibraryAppletAccessor>();
LOG_DEBUG(Service_AM, "called");
}
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
public:
explicit IStorageAccessor(std::vector<u8> buffer)
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorageAccessor::GetSize, "GetSize"},
{11, &IStorageAccessor::Read, "Read"},
};
RegisterHandlers(functions);
}
private:
std::vector<u8> buffer;
void GetSize(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(buffer.size()));
LOG_DEBUG(Service_AM, "called");
}
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u64 offset = rp.Pop<u64>();
const size_t size{ctx.GetWriteBufferSize()};
ASSERT(offset + size <= buffer.size());
ctx.WriteBuffer(buffer.data() + offset, size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_AM, "called");
}
};
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(std::vector<u8> buffer)
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Open, "Open"},
};
RegisterHandlers(functions);
}
private:
std::vector<u8> buffer;
void Open(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
LOG_DEBUG(Service_AM, "called");
}
};
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
static const FunctionInfo functions[] = {
{1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
{20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
{21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
{66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"},
{67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
};
RegisterHandlers(functions);
}
void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
constexpr u8 data[0x88] = {
0xca, 0x97, 0x94, 0xc7, // Magic
1, 0, 0, 0, // IsAccountSelected (bool)
1, 0, 0, 0, // User Id (word 0)
0, 0, 0, 0, // User Id (word 1)
0, 0, 0, 0, // User Id (word 2)
0, 0, 0, 0 // User Id (word 3)
};
std::vector<u8> buffer(data, data + sizeof(data));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorage>(buffer);
LOG_DEBUG(Service_AM, "called");
}
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u128 uid = rp.PopRaw<u128>();
LOG_WARNING(Service, "(STUBBED) called uid = %016" PRIX64 "%016" PRIX64, uid[1], uid[0]);
IPC::ResponseBuilder rb{ctx, 4};
FileSys::Path unused;
auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused);
if (savedata.Failed()) {
// Create the save data and return an error indicating that the operation was performed.
FileSystem::FormatFileSystem(FileSystem::Type::SaveData);
// TODO(Subv): Find out the correct error code for this.
rb.Push(ResultCode(ErrorModule::FS, 40));
} else {
rb.Push(RESULT_SUCCESS);
}
rb.Push<u64>(0);
}
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
// Takes an input u32 Result, no output.
// For example, in some cases official apps use this with error 0x2A2 then uses svcBreak.
IPC::RequestParser rp{ctx};
u32 result = rp.Pop<u32>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called, result=0x%08X", result);
}
void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(SystemLanguage::English);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0); // Unknown, seems to be ignored by official processes
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager);
std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager);
}
} // namespace AM

View File

@@ -4,13 +4,126 @@
#pragma once
#include <memory>
#include "core/hle/service/service.h"
namespace Kernel {
class Event;
}
namespace Service {
namespace NVFlinger {
class NVFlinger;
}
namespace AM {
// TODO: Add more languages
enum SystemLanguage {
Japanese = 0,
English = 1,
};
class IWindowController final : public ServiceFramework<IWindowController> {
public:
IWindowController();
private:
void GetAppletResourceUserId(Kernel::HLERequestContext& ctx);
void AcquireForegroundRights(Kernel::HLERequestContext& ctx);
};
class IAudioController final : public ServiceFramework<IAudioController> {
public:
IAudioController();
private:
void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx);
void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
u32 volume{100};
};
class IDisplayController final : public ServiceFramework<IDisplayController> {
public:
IDisplayController();
};
class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
public:
IDebugFunctions();
};
class ISelfController final : public ServiceFramework<ISelfController> {
public:
ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
private:
void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);
void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx);
void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx);
void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx);
void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
void LockExit(Kernel::HLERequestContext& ctx);
void UnlockExit(Kernel::HLERequestContext& ctx);
void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx);
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::SharedPtr<Kernel::Event> launchable_event;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
public:
ICommonStateGetter();
private:
enum class FocusState : u8 {
InFocus = 1,
NotInFocus = 2,
};
enum class OperationMode : u8 {
Handheld = 0,
Docked = 1,
};
void GetEventHandle(Kernel::HLERequestContext& ctx);
void ReceiveMessage(Kernel::HLERequestContext& ctx);
void GetCurrentFocusState(Kernel::HLERequestContext& ctx);
void GetOperationMode(Kernel::HLERequestContext& ctx);
void GetPerformanceMode(Kernel::HLERequestContext& ctx);
Kernel::SharedPtr<Kernel::Event> event;
};
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
public:
ILibraryAppletCreator();
private:
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
public:
IApplicationFunctions();
private:
void PopLaunchParameter(Kernel::HLERequestContext& ctx);
void EnsureSaveData(Kernel::HLERequestContext& ctx);
void SetTerminateResult(Kernel::HLERequestContext& ctx);
void GetDesiredLanguage(Kernel::HLERequestContext& ctx);
void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx);
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
void NotifyRunning(Kernel::HLERequestContext& ctx);
};
/// Registers all AM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
} // namespace AM
} // namespace Service

View File

@@ -0,0 +1,112 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/nvflinger/nvflinger.h"
namespace Service {
namespace AM {
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
public:
ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) {
static const FunctionInfo functions[] = {
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
{2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"},
{3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"},
{4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"},
{11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
{20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"},
{1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
};
RegisterHandlers(functions);
}
private:
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ICommonStateGetter>();
LOG_DEBUG(Service_AM, "called");
}
void GetSelfController(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISelfController>(nvflinger);
LOG_DEBUG(Service_AM, "called");
}
void GetWindowController(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IWindowController>();
LOG_DEBUG(Service_AM, "called");
}
void GetAudioController(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IAudioController>();
LOG_DEBUG(Service_AM, "called");
}
void GetDisplayController(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDisplayController>();
LOG_DEBUG(Service_AM, "called");
}
void GetDebugFunctions(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDebugFunctions>();
LOG_DEBUG(Service_AM, "called");
}
void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILibraryAppletCreator>();
LOG_DEBUG(Service_AM, "called");
}
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationFunctions>();
LOG_DEBUG(Service_AM, "called");
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
};
void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
LOG_DEBUG(Service_AM, "called");
}
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
static const FunctionInfo functions[] = {
{100, nullptr, "OpenSystemAppletProxy"},
{200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"},
{201, nullptr, "OpenLibraryAppletProxy"},
{300, nullptr, "OpenOverlayAppletProxy"},
{350, nullptr, "OpenSystemApplicationProxy"},
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
};
RegisterHandlers(functions);
}
} // namespace AM
} // namespace Service

View File

@@ -0,0 +1,30 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/service.h"
namespace Service {
namespace NVFlinger {
class NVFlinger;
}
namespace AM {
class AppletAE final : public ServiceFramework<AppletAE> {
public:
AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
~AppletAE() = default;
private:
void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
};
} // namespace AM
} // namespace Service

View File

@@ -4,370 +4,17 @@
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/nvflinger/nvflinger.h"
namespace Service {
namespace AM {
class IWindowController final : public ServiceFramework<IWindowController> {
public:
IWindowController() : ServiceFramework("IWindowController") {
static const FunctionInfo functions[] = {
{1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
{10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
};
RegisterHandlers(functions);
}
private:
void GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
}
void AcquireForegroundRights(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
};
class IAudioController final : public ServiceFramework<IAudioController> {
public:
IAudioController() : ServiceFramework("IAudioController") {}
};
class IDisplayController final : public ServiceFramework<IDisplayController> {
public:
IDisplayController() : ServiceFramework("IDisplayController") {}
};
class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
public:
IDebugFunctions() : ServiceFramework("IDebugFunctions") {}
};
class ISelfController final : public ServiceFramework<ISelfController> {
public:
ISelfController() : ServiceFramework("ISelfController") {
static const FunctionInfo functions[] = {
{1, &ISelfController::LockExit, "LockExit"},
{2, &ISelfController::UnlockExit, "UnlockExit"},
{11, &ISelfController::SetOperationModeChangedNotification,
"SetOperationModeChangedNotification"},
{12, &ISelfController::SetPerformanceModeChangedNotification,
"SetPerformanceModeChangedNotification"},
{13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
{14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
{16, &ISelfController::SetOutOfFocusSuspendingEnabled,
"SetOutOfFocusSuspendingEnabled"},
};
RegisterHandlers(functions);
}
private:
void SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
// Takes 3 input u8s with each field located immediately after the previous u8, these are
// bool flags. No output.
IPC::RequestParser rp{ctx};
struct FocusHandlingModeParams {
u8 unknown0;
u8 unknown1;
u8 unknown2;
};
auto flags = rp.PopRaw<FocusHandlingModeParams>();
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
bool flag = rp.Pop<bool>();
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called flag=%u", static_cast<u32>(flag));
}
void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
bool flag = rp.Pop<bool>();
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called flag=%u", static_cast<u32>(flag));
}
void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
// Takes 3 input u8s with each field located immediately after the previous u8, these are
// bool flags. No output.
IPC::RequestParser rp{ctx};
bool enabled = rp.Pop<bool>();
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called enabled=%u", static_cast<u32>(enabled));
}
void LockExit(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void UnlockExit(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
public:
ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
static const FunctionInfo functions[] = {
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
{1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
{5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"},
{6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"},
{9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"},
};
RegisterHandlers(functions);
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "ICommonStateGetter:Event");
}
private:
enum class FocusState : u8 {
InFocus = 1,
NotInFocus = 2,
};
enum class OperationMode : u8 {
Handheld = 0,
Docked = 1,
};
void GetEventHandle(Kernel::HLERequestContext& ctx) {
event->Signal();
IPC::RequestBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event);
LOG_WARNING(Service, "(STUBBED) called");
}
void ReceiveMessage(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(15);
LOG_WARNING(Service, "(STUBBED) called");
}
void GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u8>(FocusState::InFocus));
LOG_WARNING(Service, "(STUBBED) called");
}
void GetOperationMode(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u8>(OperationMode::Handheld));
LOG_WARNING(Service, "(STUBBED) called");
}
void GetPerformanceMode(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(APM::PerformanceMode::Handheld));
LOG_WARNING(Service, "(STUBBED) called");
}
Kernel::SharedPtr<Kernel::Event> event;
};
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
public:
explicit IStorageAccessor(std::vector<u8> buffer)
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorageAccessor::GetSize, "GetSize"},
{11, &IStorageAccessor::Read, "Read"},
};
RegisterHandlers(functions);
}
private:
std::vector<u8> buffer;
void GetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(buffer.size()));
LOG_DEBUG(Service, "called");
}
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u64 offset = rp.Pop<u64>();
const auto& output_buffer = ctx.BufferDescriptorC()[0];
ASSERT(offset + output_buffer.Size() <= buffer.size());
Memory::WriteBlock(output_buffer.Address(), buffer.data() + offset, output_buffer.Size());
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service, "called");
}
};
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(std::vector<u8> buffer)
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Open, "Open"},
};
RegisterHandlers(functions);
}
private:
std::vector<u8> buffer;
void Open(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
LOG_DEBUG(Service, "called");
}
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
public:
IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
static const FunctionInfo functions[] = {
{1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
{21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
{66, &IApplicationFunctions::InitializeGamePlayRecording,
"InitializeGamePlayRecording"},
{67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
};
RegisterHandlers(functions);
}
private:
void PopLaunchParameter(Kernel::HLERequestContext& ctx) {
constexpr u8 data[0x88] = {
0xca, 0x97, 0x94, 0xc7, // Magic
1, 0, 0, 0, // IsAccountSelected (bool)
1, 0, 0, 0, // User Id (word 0)
0, 0, 0, 0, // User Id (word 1)
0, 0, 0, 0, // User Id (word 2)
0, 0, 0, 0 // User Id (word 3)
};
std::vector<u8> buffer(data, data + sizeof(data));
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorage>(buffer);
LOG_DEBUG(Service, "called");
}
void SetTerminateResult(Kernel::HLERequestContext& ctx) {
// Takes an input u32 Result, no output.
// For example, in some cases official apps use this with error 0x2A2 then uses svcBreak.
IPC::RequestParser rp{ctx};
u32 result = rp.Pop<u32>();
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called, result=0x%08X", result);
}
void GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(SystemLanguage::English);
LOG_WARNING(Service, "(STUBBED) called");
}
void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void NotifyRunning(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0); // Unknown, seems to be ignored by official processes
LOG_WARNING(Service, "(STUBBED) called");
}
};
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
public:
ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {}
};
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public:
IApplicationProxy() : ServiceFramework("IApplicationProxy") {
IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
static const FunctionInfo functions[] = {
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &IApplicationProxy::GetSelfController, "GetSelfController"},
@@ -383,70 +30,73 @@ public:
private:
void GetAudioController(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IAudioController>();
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_AM, "called");
}
void GetDisplayController(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDisplayController>();
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_AM, "called");
}
void GetDebugFunctions(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDebugFunctions>();
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_AM, "called");
}
void GetWindowController(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IWindowController>();
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_AM, "called");
}
void GetSelfController(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISelfController>();
LOG_DEBUG(Service, "called");
rb.PushIpcInterface<ISelfController>(nvflinger);
LOG_DEBUG(Service_AM, "called");
}
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ICommonStateGetter>();
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_AM, "called");
}
void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILibraryAppletCreator>();
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_AM, "called");
}
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationFunctions>();
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_AM, "called");
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
};
void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationProxy>();
LOG_DEBUG(Service, "called");
rb.PushIpcInterface<IApplicationProxy>(nvflinger);
LOG_DEBUG(Service_AM, "called");
}
AppletOE::AppletOE() : ServiceFramework("appletOE") {
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) {
static const FunctionInfo functions[] = {
{0x00000000, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
};

View File

@@ -4,25 +4,26 @@
#pragma once
#include <memory>
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/service.h"
namespace Service {
namespace AM {
namespace NVFlinger {
class NVFlinger;
}
// TODO: Add more languages
enum SystemLanguage {
Japanese = 0,
English = 1,
};
namespace AM {
class AppletOE final : public ServiceFramework<AppletOE> {
public:
AppletOE();
AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
~AppletOE() = default;
private:
void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
};
} // namespace AM

View File

@@ -2,16 +2,44 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/aoc/aoc_u.h"
namespace Service {
namespace AOC {
AOC_U::AOC_U() : ServiceFramework("aoc:u") {
static const FunctionInfo functions[] = {
{0, nullptr, "CountAddOnContentByApplicationId"},
{1, nullptr, "ListAddOnContentByApplicationId"},
{2, &AOC_U::CountAddOnContent, "CountAddOnContent"},
{3, &AOC_U::ListAddOnContent, "ListAddOnContent"},
{4, nullptr, "GetAddOnContentBaseIdByApplicationId"},
{5, nullptr, "GetAddOnContentBaseId"},
{6, nullptr, "PrepareAddOnContentByApplicationId"},
{7, nullptr, "PrepareAddOnContent"},
};
RegisterHandlers(functions);
}
void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
LOG_WARNING(Service_AOC, "(STUBBED) called");
}
void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
LOG_WARNING(Service_AOC, "(STUBBED) called");
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<AOC_U>()->InstallAsService(service_manager);
}
AOC_U::AOC_U() : ServiceFramework("aoc:u") {}
} // namespace AOC
} // namespace Service

View File

@@ -13,6 +13,10 @@ class AOC_U final : public ServiceFramework<AOC_U> {
public:
AOC_U();
~AOC_U() = default;
private:
void CountAddOnContent(Kernel::HLERequestContext& ctx);
void ListAddOnContent(Kernel::HLERequestContext& ctx);
};
/// Registers all AOC services with the specified service manager.

View File

@@ -5,62 +5,15 @@
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/apm/interface.h"
namespace Service {
namespace APM {
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<APM>()->InstallAsService(service_manager);
}
class ISession final : public ServiceFramework<ISession> {
public:
ISession() : ServiceFramework("ISession") {
static const FunctionInfo functions[] = {
{0, &ISession::SetPerformanceConfiguration, "SetPerformanceConfiguration"},
{1, &ISession::GetPerformanceConfiguration, "GetPerformanceConfiguration"},
};
RegisterHandlers(functions);
}
private:
void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto mode = static_cast<PerformanceMode>(rp.Pop<u32>());
u32 config = rp.Pop<u32>();
IPC::RequestBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called mode=%u config=%u", static_cast<u32>(mode), config);
}
void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto mode = static_cast<PerformanceMode>(rp.Pop<u32>());
IPC::RequestBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // Performance configuration
LOG_WARNING(Service, "(STUBBED) called mode=%u", static_cast<u32>(mode));
}
};
APM::APM() : ServiceFramework("apm") {
static const FunctionInfo functions[] = {
{0x00000000, &APM::OpenSession, "OpenSession"},
{0x00000001, nullptr, "GetPerformanceMode"},
};
RegisterHandlers(functions);
}
void APM::OpenSession(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISession>();
auto module_ = std::make_shared<Module>();
std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager);
std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager);
}
} // namespace APM

View File

@@ -14,13 +14,10 @@ enum class PerformanceMode : u8 {
Docked = 1,
};
class APM final : public ServiceFramework<APM> {
class Module final {
public:
APM();
~APM() = default;
private:
void OpenSession(Kernel::HLERequestContext& ctx);
Module() = default;
~Module() = default;
};
/// Registers all AM services with the specified service manager.

View File

@@ -0,0 +1,66 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/apm/interface.h"
namespace Service {
namespace APM {
class ISession final : public ServiceFramework<ISession> {
public:
ISession() : ServiceFramework("ISession") {
static const FunctionInfo functions[] = {
{0, &ISession::SetPerformanceConfiguration, "SetPerformanceConfiguration"},
{1, &ISession::GetPerformanceConfiguration, "GetPerformanceConfiguration"},
};
RegisterHandlers(functions);
}
private:
void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto mode = static_cast<PerformanceMode>(rp.Pop<u32>());
u32 config = rp.Pop<u32>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_APM, "(STUBBED) called mode=%u config=%u", static_cast<u32>(mode),
config);
}
void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto mode = static_cast<PerformanceMode>(rp.Pop<u32>());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // Performance configuration
LOG_WARNING(Service_APM, "(STUBBED) called mode=%u", static_cast<u32>(mode));
}
};
APM::APM(std::shared_ptr<Module> apm, const char* name)
: ServiceFramework(name), apm(std::move(apm)) {
static const FunctionInfo functions[] = {
{0, &APM::OpenSession, "OpenSession"},
{1, nullptr, "GetPerformanceMode"},
};
RegisterHandlers(functions);
}
void APM::OpenSession(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISession>();
}
} // namespace APM
} // namespace Service

View File

@@ -0,0 +1,27 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service {
namespace APM {
class APM final : public ServiceFramework<APM> {
public:
APM(std::shared_ptr<Module> apm, const char* name);
~APM() = default;
private:
void OpenSession(Kernel::HLERequestContext& ctx);
std::shared_ptr<Module> apm;
};
/// Registers all AM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace APM
} // namespace Service

View File

@@ -0,0 +1,41 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/audin_u.h"
namespace Service {
namespace Audio {
class IAudioIn final : public ServiceFramework<IAudioIn> {
public:
IAudioIn() : ServiceFramework("IAudioIn") {
static const FunctionInfo functions[] = {
{0x0, nullptr, "GetAudioInState"},
{0x1, nullptr, "StartAudioIn"},
{0x2, nullptr, "StopAudioIn"},
{0x3, nullptr, "AppendAudioInBuffer_1"},
{0x4, nullptr, "RegisterBufferEvent"},
{0x5, nullptr, "GetReleasedAudioInBuffer_1"},
{0x6, nullptr, "ContainsAudioInBuffer"},
{0x7, nullptr, "AppendAudioInBuffer_2"},
{0x8, nullptr, "GetReleasedAudioInBuffer_2"},
};
RegisterHandlers(functions);
}
~IAudioIn() = default;
};
AudInU::AudInU() : ServiceFramework("audin:u") {
static const FunctionInfo functions[] = {
{0x00000000, nullptr, "ListAudioIns"},
{0x00000001, nullptr, "OpenAudioIn"},
};
RegisterHandlers(functions);
}
} // namespace Audio
} // namespace Service

View File

@@ -0,0 +1,23 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service {
namespace Audio {
class AudInU final : public ServiceFramework<AudInU> {
public:
explicit AudInU();
~AudInU() = default;
};
} // namespace Audio
} // namespace Service

View File

@@ -2,14 +2,22 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/audio/audin_u.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/audio/audout_u.h"
#include "core/hle/service/audio/audrec_u.h"
#include "core/hle/service/audio/audren_u.h"
#include "core/hle/service/audio/codecctl.h"
namespace Service {
namespace Audio {
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<AudOutU>()->InstallAsService(service_manager);
std::make_shared<AudInU>()->InstallAsService(service_manager);
std::make_shared<AudRecU>()->InstallAsService(service_manager);
std::make_shared<AudRenU>()->InstallAsService(service_manager);
std::make_shared<CodecCtl>()->InstallAsService(service_manager);
}
} // namespace Audio

View File

@@ -2,23 +2,195 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <vector>
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/audout_u.h"
namespace Service {
namespace Audio {
/// Switch sample rate frequency
constexpr u32 sample_rate{48000};
/// TODO(st4rk): dynamic number of channels, as I think Switch has support
/// to more audio channels (probably when Docked I guess)
constexpr u32 audio_channels{2};
/// TODO(st4rk): find a proper value for the audio_ticks
constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 500)};
class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) {
static const FunctionInfo functions[] = {
{0x0, nullptr, "GetAudioOutState"},
{0x1, &IAudioOut::StartAudioOut, "StartAudioOut"},
{0x2, &IAudioOut::StopAudioOut, "StopAudioOut"},
{0x3, &IAudioOut::AppendAudioOutBuffer_1, "AppendAudioOutBuffer_1"},
{0x4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
{0x5, &IAudioOut::GetReleasedAudioOutBuffer_1, "GetReleasedAudioOutBuffer_1"},
{0x6, nullptr, "ContainsAudioOutBuffer"},
{0x7, nullptr, "AppendAudioOutBuffer_2"},
{0x8, nullptr, "GetReleasedAudioOutBuffer_2"},
};
RegisterHandlers(functions);
// This is the event handle used to check if the audio buffer was released
buffer_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
// Register event callback to update the Audio Buffer
audio_event = CoreTiming::RegisterEvent(
"IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) {
UpdateAudioBuffersCallback();
CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
});
// Start the audio event
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
}
~IAudioOut() {
CoreTiming::UnscheduleEvent(audio_event, 0);
}
private:
void StartAudioOut(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
// Start audio
audio_out_state = AudioState::Started;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void StopAudioOut(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
// Stop audio
audio_out_state = AudioState::Stopped;
queue_keys.clear();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(buffer_event);
}
void AppendAudioOutBuffer_1(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const u64 key{rp.Pop<u64>()};
queue_keys.insert(queue_keys.begin(), key);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetReleasedAudioOutBuffer_1(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
// TODO(st4rk): This is how libtransistor currently implements the
// GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address
// is used to know which buffer should be filled with data and send again to the service
// through AppendAudioOutBuffer. Check if this is the proper way to do it.
u64 key{0};
if (queue_keys.size()) {
key = queue_keys.back();
queue_keys.pop_back();
}
ctx.WriteBuffer(&key, sizeof(u64));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
// TODO(st4rk): This might be the total of released buffers, needs to be verified on
// hardware
rb.Push<u32>(static_cast<u32>(queue_keys.size()));
}
void UpdateAudioBuffersCallback() {
if (audio_out_state != AudioState::Started) {
return;
}
if (queue_keys.empty()) {
return;
}
buffer_event->Signal();
}
enum class AudioState : u32 {
Started,
Stopped,
};
/// This is used to trigger the audio event callback that is going to read the samples from the
/// audio_buffer list and enqueue the samples using the sink (audio_core).
CoreTiming::EventType* audio_event;
/// This is the evend handle used to check if the audio buffer was released
Kernel::SharedPtr<Kernel::Event> buffer_event;
/// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor
/// uses the key as an address in the App, so we need to return when the
/// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because
/// libtransistor uses the key returned as an pointer.
std::vector<u64> queue_keys;
AudioState audio_out_state;
};
void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2};
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const std::string audio_interface = "AudioInterface";
ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
rb.Push(RESULT_SUCCESS);
// TODO(st4rk): We're currently returning only one audio interface (stringlist size). However,
// it's highly possible to have more than one interface (despite that libtransistor requires
// only one).
rb.Push<u32>(1);
}
void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
if (!audio_out_interface) {
audio_out_interface = std::make_shared<IAudioOut>();
}
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(sample_rate);
rb.Push<u32>(audio_channels);
rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
rb.Push<u32>(0); // This field is unknown
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
}
AudOutU::AudOutU() : ServiceFramework("audout:u") {
static const FunctionInfo functions[] = {
{0x00000000, &AudOutU::ListAudioOuts, "ListAudioOuts"},
};
static const FunctionInfo functions[] = {{0x00000000, &AudOutU::ListAudioOuts, "ListAudioOuts"},
{0x00000001, &AudOutU::OpenAudioOut, "OpenAudioOut"},
{0x00000002, nullptr, "Unknown2"},
{0x00000003, nullptr, "Unknown3"}};
RegisterHandlers(functions);
}

View File

@@ -4,19 +4,37 @@
#pragma once
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service {
namespace Audio {
class IAudioOut;
class AudOutU final : public ServiceFramework<AudOutU> {
public:
AudOutU();
~AudOutU() = default;
private:
std::shared_ptr<IAudioOut> audio_out_interface;
void ListAudioOuts(Kernel::HLERequestContext& ctx);
void OpenAudioOut(Kernel::HLERequestContext& ctx);
enum class PcmFormat : u32 {
Invalid = 0,
Int8 = 1,
Int16 = 2,
Int24 = 3,
Int32 = 4,
PcmFloat = 5,
Adpcm = 6,
};
};
} // namespace Audio

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