Compare commits

...

273 Commits

Author SHA1 Message Date
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
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
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
mailwl
eaa9f968a6 [WIP] sfdnsres: stub (#146)
sfdnsres: Add several stubs
2018-01-29 22:29:47 -08:00
196 changed files with 30881 additions and 2824 deletions

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

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,7 +2,7 @@
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

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,14 +32,23 @@ namespace Log {
CLS(Kernel) \
SUB(Kernel, SVC) \
CLS(Service) \
SUB(Service, SM) \
SUB(Service, FS) \
SUB(Service, GSP) \
SUB(Service, CFG) \
SUB(Service, DSP) \
SUB(Service, HID) \
SUB(Service, NVDRV) \
SUB(Service, ACC) \
SUB(Service, Audio) \
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, Friend) \
SUB(Service, FS) \
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,14 +49,23 @@ 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_HID, ///< The HID (Human interface device) service
Service_NVDRV, ///< The NVDRV (Nvidia driver) 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_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

@@ -7,15 +7,21 @@ add_library(core STATIC
core_timing.cpp
core_timing.h
file_sys/directory.h
file_sys/disk_filesystem.cpp
file_sys/disk_filesystem.h
file_sys/errors.h
file_sys/filesystem.cpp
file_sys/filesystem.h
file_sys/path_parser.cpp
file_sys/path_parser.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
@@ -28,8 +34,6 @@ 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
@@ -55,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
@@ -84,12 +90,16 @@ 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
@@ -108,10 +118,26 @@ add_library(core STATIC
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
@@ -119,6 +145,10 @@ add_library(core STATIC
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
@@ -139,12 +169,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
@@ -158,6 +197,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

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,10 +53,10 @@ 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.
* @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
@@ -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

@@ -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

@@ -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 {
@@ -86,7 +89,7 @@ public:
* @param size The size of the new file, filled with zeroes
* @return Result of the operation
*/
virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
virtual ResultCode CreateFile(const std::string& path, u64 size) const = 0;
/**
* Delete a file specified by its path
@@ -138,8 +141,8 @@ public:
* @param mode Mode to open the file with
* @return Opened file, or error code
*/
virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path,
const Mode& mode) const = 0;
virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
Mode mode) const = 0;
/**
* Open a directory specified by its path
@@ -153,6 +156,12 @@ public:
* @return The number of free bytes in the archive
*/
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 FileSystemFactory : NonCopyable {
@@ -174,10 +183,9 @@ public:
/**
* 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

@@ -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

@@ -23,7 +23,7 @@ ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& pa
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
}
ResultCode RomFS_Factory::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
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);

View File

@@ -23,7 +23,7 @@ public:
return "ArchiveFactory_RomFS";
}
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
ResultCode Format(const Path& path) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:

View File

@@ -14,8 +14,8 @@ std::string RomFS_FileSystem::GetName() const {
return "RomFS";
}
ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const Path& path,
const Mode& mode) const {
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));
}
@@ -48,7 +48,7 @@ ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::CreateFile(const Path& path, u64 size) const {
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
@@ -79,6 +79,12 @@ u64 RomFS_FileSystem::GetFreeSpaceSize() const {
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);

View File

@@ -29,17 +29,18 @@ public:
std::string GetName() const override;
ResultVal<std::unique_ptr<StorageBackend>> 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 GetFreeSpaceSize() const override;
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
protected:
std::shared_ptr<FileUtil::IOFile> romfs_file;

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

@@ -6,6 +6,7 @@
#include <algorithm>
#include <atomic>
#include <cinttypes>
#include <climits>
#include <csignal>
#include <cstdarg>
@@ -360,8 +361,9 @@ static void RemoveBreakpoint(BreakpointType type, PAddr 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);
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));
}
}
@@ -407,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;
}
}
@@ -690,7 +693,7 @@ static void ReadMemory() {
u64 len =
HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016llx len: %016llx\n", addr, len);
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016lx len: %016lx\n", addr, len);
if (len * 2 > sizeof(reply)) {
SendReply("E01");
@@ -778,8 +781,8 @@ static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 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;
}

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

@@ -118,7 +118,8 @@ public:
AlignWithPadding();
if (context.Session()->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);

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

@@ -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

@@ -5,6 +5,7 @@
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
@@ -77,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

@@ -7,11 +7,13 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/memory.h"
namespace Kernel {
@@ -25,6 +27,32 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
boost::range::remove_erase(connected_sessions, server_session);
}
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)
: server_session(std::move(server_session)) {
cmd_buf[0] = 0;
@@ -34,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
@@ -44,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);
}
@@ -84,13 +112,18 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool 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();
@@ -153,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);
@@ -165,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(),
@@ -195,7 +231,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
// TODO(Subv): Translate the X/A/B/W buffers.
if (Session()->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.
@@ -207,7 +243,106 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
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.
@@ -102,14 +105,31 @@ public:
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;
@@ -139,10 +159,25 @@ 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;
}
/// 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) {
ASSERT(index < copy_objects.size());
@@ -187,6 +222,8 @@ public:
return domain_objects.size();
}
std::string Description() const;
private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
SharedPtr<Kernel::ServerSession> server_session;
@@ -195,10 +232,10 @@ private:
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

@@ -33,10 +33,6 @@ enum class HandleType : u32 {
ServerSession,
};
enum {
DEFAULT_STACK_SIZE = 0x10000,
};
enum class ResetType {
OneShot,
Sticky,

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,7 @@
#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"
@@ -57,6 +58,34 @@ 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
@@ -64,49 +93,42 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(),
Kernel::g_handle_table);
// If the session has been converted to a domain, handle the doomain request
if (IsDomain()) {
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", domain_message_header->command.Value());
ASSERT(false);
}
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);
}
// 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;
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;
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 (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
@@ -140,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
@@ -116,17 +117,12 @@ private:
*/
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,24 +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) {
// 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%llx name=%s, invalid address",
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, invalid address",
GetObjectId(), address, name.c_str());
return ERR_INVALID_ADDRESS;
}
@@ -155,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"
@@ -30,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;
}
@@ -45,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
@@ -263,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) {
@@ -304,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;
@@ -331,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;
@@ -341,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");
@@ -387,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;
}
@@ -415,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;
}
@@ -431,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);
@@ -440,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) {
@@ -449,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);
@@ -473,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())
@@ -511,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);
}
@@ -540,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;
@@ -582,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
@@ -612,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,
@@ -667,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;
}
@@ -727,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;
@@ -738,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();
@@ -779,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

@@ -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"
@@ -17,8 +18,26 @@ 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];
@@ -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

@@ -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.
@@ -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

@@ -108,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,7 +23,7 @@ public:
private:
void GetBase(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
LOG_WARNING(Service_ACC, "(STUBBED) called");
ProfileBase profile_base{};
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
@@ -30,38 +33,62 @@ 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");
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");
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::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");
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -70,12 +97,22 @@ void ACC_U0::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx)
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,488 @@
// 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 {
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);
}

View File

@@ -7,6 +7,10 @@
#include <memory>
#include "core/hle/service/service.h"
namespace Kernel {
class Event;
}
namespace Service {
namespace NVFlinger {
class NVFlinger;
@@ -14,6 +18,109 @@ 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,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger);

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,385 +4,13 @@
#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::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
}
void AcquireForegroundRights(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::ResponseBuilder 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(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
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"},
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
};
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::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder 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::ResponseBuilder 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::ResponseBuilder 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::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called enabled=%u", static_cast<u32>(enabled));
}
void LockExit(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void UnlockExit(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void 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, "(STUBBED) called");
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
};
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::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event);
LOG_WARNING(Service, "(STUBBED) called");
}
void ReceiveMessage(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(15);
LOG_WARNING(Service, "(STUBBED) called");
}
void GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder 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::ResponseBuilder 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::ResponseBuilder 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::ResponseBuilder 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::ResponseBuilder 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::ResponseBuilder rb{ctx, 2, 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::ResponseBuilder rb{ctx, 2, 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::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called, result=0x%08X", result);
}
void GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(SystemLanguage::English);
LOG_WARNING(Service, "(STUBBED) called");
}
void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void 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, "(STUBBED) called");
}
};
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
public:
ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {}
};
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public:
IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
@@ -405,56 +33,56 @@ private:
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::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::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::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::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISelfController>(nvflinger);
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_AM, "called");
}
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
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::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::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;
@@ -464,7 +92,7 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationProxy>(nvflinger);
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_AM, "called");
}
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)

View File

@@ -15,12 +15,6 @@ class NVFlinger;
namespace AM {
// TODO: Add more languages
enum SystemLanguage {
Japanese = 0,
English = 1,
};
class AppletOE final : public ServiceFramework<AppletOE> {
public:
AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);

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::ResponseBuilder 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::ResponseBuilder 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::ResponseBuilder rb{ctx, 2, 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

@@ -52,7 +52,9 @@ public:
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
}
~IAudioOut() = default;
~IAudioOut() {
CoreTiming::UnscheduleEvent(audio_event, 0);
}
private:
void StartAudioOut(Kernel::HLERequestContext& ctx) {
@@ -99,8 +101,6 @@ private:
void GetReleasedAudioOutBuffer_1(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
const auto& buffer = ctx.BufferDescriptorB()[0];
// 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
@@ -112,7 +112,7 @@ private:
queue_keys.pop_back();
}
Memory::WriteBlock(buffer.Address(), &key, sizeof(u64));
ctx.WriteBuffer(&key, sizeof(u64));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -158,10 +158,8 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
auto& buffer = ctx.BufferDescriptorB()[0];
const std::string audio_interface = "AudioInterface";
Memory::WriteBlock(buffer.Address(), &audio_interface[0], audio_interface.size());
ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);

View File

@@ -3,13 +3,18 @@
// Refer to the license.txt file included.
#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/audren_u.h"
namespace Service {
namespace Audio {
/// TODO(bunnei): Find a proper value for the audio_ticks
constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 200)};
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
IAudioRenderer() : ServiceFramework("IAudioRenderer") {
@@ -18,27 +23,169 @@ public:
{0x1, nullptr, "GetAudioRendererSampleCount"},
{0x2, nullptr, "GetAudioRendererMixBufferCount"},
{0x3, nullptr, "GetAudioRendererState"},
{0x4, nullptr, "RequestUpdateAudioRenderer"},
{0x5, nullptr, "StartAudioRenderer"},
{0x6, nullptr, "StopAudioRenderer"},
{0x7, nullptr, "QuerySystemEvent"},
{0x4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"},
{0x5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"},
{0x6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"},
{0x7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
{0x8, nullptr, "SetAudioRendererRenderingTimeLimit"},
{0x9, nullptr, "GetAudioRendererRenderingTimeLimit"},
};
RegisterHandlers(functions);
system_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent");
// Register event callback to update the Audio Buffer
audio_event = CoreTiming::RegisterEvent(
"IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) {
UpdateAudioCallback();
CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
});
// Start the audio event
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
}
~IAudioRenderer() = default;
~IAudioRenderer() {
CoreTiming::UnscheduleEvent(audio_event, 0);
}
private:
void UpdateAudioCallback() {
system_event->Signal();
}
void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "%s", ctx.Description().c_str());
AudioRendererResponseData response_data{};
response_data.section_0_size =
response_data.state_entries.size() * sizeof(AudioRendererStateEntry);
response_data.section_1_size = response_data.section_1.size();
response_data.section_2_size = response_data.section_2.size();
response_data.section_3_size = response_data.section_3.size();
response_data.section_4_size = response_data.section_4.size();
response_data.section_5_size = response_data.section_5.size();
response_data.total_size = sizeof(AudioRendererResponseData);
for (unsigned i = 0; i < response_data.state_entries.size(); i++) {
// 4 = Busy and 5 = Ready?
response_data.state_entries[i].state = 5;
}
ctx.WriteBuffer(&response_data, response_data.total_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void StartAudioRenderer(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void StopAudioRenderer(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
// system_event->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(system_event);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
struct AudioRendererStateEntry {
u32_le state;
u32_le unknown_4;
u32_le unknown_8;
u32_le unknown_c;
};
static_assert(sizeof(AudioRendererStateEntry) == 0x10,
"AudioRendererStateEntry has wrong size");
struct AudioRendererResponseData {
u32_le unknown_0;
u32_le section_5_size;
u32_le section_0_size;
u32_le section_1_size;
u32_le unknown_10;
u32_le section_2_size;
u32_le unknown_18;
u32_le section_3_size;
u32_le section_4_size;
u32_le unknown_24;
u32_le unknown_28;
u32_le unknown_2c;
u32_le unknown_30;
u32_le unknown_34;
u32_le unknown_38;
u32_le total_size;
std::array<AudioRendererStateEntry, 0x18e> state_entries;
std::array<u8, 0x600> section_1;
std::array<u8, 0xe0> section_2;
std::array<u8, 0x20> section_3;
std::array<u8, 0x10> section_4;
std::array<u8, 0xb0> section_5;
};
static_assert(sizeof(AudioRendererResponseData) == 0x20e0,
"AudioRendererResponseData has wrong size");
/// This is used to trigger the audio event callback.
CoreTiming::EventType* audio_event;
Kernel::SharedPtr<Kernel::Event> system_event;
};
AudRenU::AudRenU() : ServiceFramework("audren:u") {
static const FunctionInfo functions[] = {
{0x00000000, nullptr, "OpenAudioRenderer"},
{0x00000001, nullptr, "GetAudioRendererWorkBufferSize"},
{0x00000002, nullptr, "GetAudioRenderersProcessMasterVolume"},
{0x00000003, nullptr, "SetAudioRenderersProcessMasterVolume"},
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
{1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"},
{2, &AudRenU::GetAudioRenderersProcessMasterVolume, "GetAudioRenderersProcessMasterVolume"},
{3, nullptr, "SetAudioRenderersProcessMasterVolume"},
};
RegisterHandlers(functions);
}
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<Audio::IAudioRenderer>();
LOG_DEBUG(Service_Audio, "called");
}
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0x400);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void AudRenU::GetAudioRenderersProcessMasterVolume(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(100);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
} // namespace Audio
} // namespace Service

View File

@@ -17,6 +17,11 @@ class AudRenU final : public ServiceFramework<AudRenU> {
public:
explicit AudRenU();
~AudRenU() = default;
private:
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
void GetAudioRenderersProcessMasterVolume(Kernel::HLERequestContext& ctx);
};
} // namespace Audio

View File

@@ -3,7 +3,9 @@
// Refer to the license.txt file included.
#include <boost/container/flat_map.hpp>
#include "common/file_util.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h"
@@ -41,12 +43,30 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
return itr->second->Open(path);
}
void UnregisterFileSystems() {
ResultCode FormatFileSystem(Type type) {
LOG_TRACE(Service_FS, "Formatting FileSystem with type=%d", type);
auto itr = filesystem_map.find(type);
if (itr == filesystem_map.end()) {
// TODO(bunnei): Find a better error code for this
return ResultCode(-1);
}
FileSys::Path unused;
return itr->second->Format(unused);
}
void RegisterFileSystems() {
filesystem_map.clear();
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory));
RegisterFileSystem(std::move(savedata), Type::SaveData);
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
UnregisterFileSystems();
RegisterFileSystems();
std::make_shared<FSP_SRV>()->InstallAsService(service_manager);
}

View File

@@ -25,6 +25,7 @@ namespace FileSystem {
/// Supported FileSystem types
enum class Type {
RomFS = 1,
SaveData = 2,
};
/**
@@ -43,6 +44,13 @@ ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& fact
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
FileSys::Path& path);
/**
* Formats a file system
* @param type Type of the file system to format
* @return ResultCode of the operation
*/
ResultCode FormatFileSystem(Type type);
/// Registers all Filesystem services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/filesystem.h"
@@ -33,12 +34,10 @@ private:
IPC::RequestParser rp{ctx};
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
const auto& descriptor = ctx.BufferDescriptorB()[0];
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
// Error checking
ASSERT_MSG(length == descriptor.Size(), "unexpected size difference");
if (length < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
@@ -60,17 +59,196 @@ private:
}
// Write the data to memory
Memory::WriteBlock(descriptor.Address(), output.data(), descriptor.Size());
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
};
class IFile final : public ServiceFramework<IFile> {
public:
explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend)
: ServiceFramework("IFile"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, {2, nullptr, "Flush"},
{3, nullptr, "SetSize"}, {4, nullptr, "GetSize"},
};
RegisterHandlers(functions);
}
private:
std::unique_ptr<FileSys::StorageBackend> backend;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 unk = rp.Pop<u64>();
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
// Error checking
if (length < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
return;
}
if (offset < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
return;
}
// Read the data from the Storage backend
std::vector<u8> output(length);
ResultVal<size_t> res = backend->Read(offset, length, output.data());
if (res.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
return;
}
// Write the data to memory
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(*res));
}
void Write(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 unk = rp.Pop<u64>();
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
// Error checking
if (length < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
return;
}
if (offset < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
return;
}
// Write the data to the Storage backend
std::vector<u8> data = ctx.ReadBuffer();
ResultVal<size_t> res = backend->Write(offset, length, true, data.data());
if (res.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
};
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend)
: ServiceFramework("IFileSystem"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"},
{7, &IFileSystem::GetEntryType, "GetEntryType"},
{8, &IFileSystem::OpenFile, "OpenFile"},
{10, &IFileSystem::Commit, "Commit"},
};
RegisterHandlers(functions);
}
void CreateFile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto file_buffer = ctx.ReadBuffer();
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
std::string name(file_buffer.begin(), end);
u64 mode = rp.Pop<u64>();
u32 size = rp.Pop<u32>();
LOG_DEBUG(Service_FS, "called file %s mode 0x%" PRIX64 " size 0x%08X", name.c_str(), mode,
size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend->CreateFile(name, size));
}
void OpenFile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto file_buffer = ctx.ReadBuffer();
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
std::string name(file_buffer.begin(), end);
auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
LOG_DEBUG(Service_FS, "called file %s mode %u", name.c_str(), static_cast<u32>(mode));
auto result = backend->OpenFile(name, mode);
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
auto file = std::move(result.Unwrap());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IFile>(std::move(file));
}
void GetEntryType(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto file_buffer = ctx.ReadBuffer();
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
std::string name(file_buffer.begin(), end);
LOG_DEBUG(Service_FS, "called file %s", name.c_str());
auto result = backend->GetEntryType(name);
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(*result));
}
void Commit(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
private:
std::unique_ptr<FileSys::FileSystemBackend> backend;
};
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
static const FunctionInfo functions[] = {
{1, &FSP_SRV::Initalize, "Initalize"},
{18, &FSP_SRV::MountSdCard, "MountSdCard"},
{22, &FSP_SRV::CreateSaveData, "CreateSaveData"},
{51, &FSP_SRV::MountSaveData, "MountSaveData"},
{200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
{202, nullptr, "OpenDataStorageByDataId"},
{203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"},
{1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"},
};
@@ -95,6 +273,37 @@ void FSP_SRV::Initalize(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<std::array<u8, 0x40>>();
auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
LOG_WARNING(Service_FS, "(STUBBED) called uid = %016" PRIX64 "%016" PRIX64, uid[1], uid[0]);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
FileSys::Path unused;
auto filesystem = OpenFileSystem(Type::SaveData, unused).Unwrap();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
}
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");

View File

@@ -23,6 +23,9 @@ private:
void TryLoadRomFS();
void Initalize(Kernel::HLERequestContext& ctx);
void MountSdCard(Kernel::HLERequestContext& ctx);
void CreateSaveData(Kernel::HLERequestContext& ctx);
void MountSaveData(Kernel::HLERequestContext& ctx);
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenRomStorage(Kernel::HLERequestContext& ctx);

View File

@@ -0,0 +1,28 @@
// 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/friend/friend.h"
#include "core/hle/service/friend/friend_a.h"
namespace Service {
namespace Friend {
void Module::Interface::Unknown(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Friend, "(STUBBED) called");
}
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {}
void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module = std::make_shared<Module>();
std::make_shared<Friend_A>(module)->InstallAsService(service_manager);
}
} // namespace Friend
} // namespace Service

View File

@@ -0,0 +1,29 @@
// 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 Friend {
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> module, const char* name);
void Unknown(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
};
};
/// Registers all Friend services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace Friend
} // namespace Service

View File

@@ -0,0 +1,19 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/friend/friend_a.h"
namespace Service {
namespace Friend {
Friend_A::Friend_A(std::shared_ptr<Module> module)
: Module::Interface(std::move(module), "friend:a") {
static const FunctionInfo functions[] = {
{0, &Friend_A::Unknown, "Unknown"},
};
RegisterHandlers(functions);
}
} // namespace Friend
} // namespace Service

View File

@@ -0,0 +1,18 @@
// 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/friend/friend.h"
namespace Service {
namespace Friend {
class Friend_A final : public Module::Interface {
public:
explicit Friend_A(std::shared_ptr<Module> module);
};
} // namespace Friend
} // namespace Service

View File

@@ -9,6 +9,7 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/service.h"
@@ -44,12 +45,16 @@ public:
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
}
~IAppletResource() {
CoreTiming::UnscheduleEvent(pad_update_event, 0);
}
private:
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(shared_mem);
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_HID, "called");
}
void LoadInputDevices() {
@@ -151,18 +156,61 @@ private:
buttons;
};
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
public:
IActiveVibrationDeviceList() : ServiceFramework("IActiveVibrationDeviceList") {
static const FunctionInfo functions[] = {
{0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"},
};
RegisterHandlers(functions);
}
private:
void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
};
class Hid final : public ServiceFramework<Hid> {
public:
Hid() : ServiceFramework("hid") {
static const FunctionInfo functions[] = {
{0x00000000, &Hid::CreateAppletResource, "CreateAppletResource"},
{0, &Hid::CreateAppletResource, "CreateAppletResource"},
{1, &Hid::ActivateDebugPad, "ActivateDebugPad"},
{11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
{21, &Hid::ActivateMouse, "ActivateMouse"},
{31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
{66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
{79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
{103, &Hid::ActivateNpad, "ActivateNpad"},
{106, &Hid::AcquireNpadStyleSetUpdateEventHandle,
"AcquireNpadStyleSetUpdateEventHandle"},
{120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
{122, &Hid::SetNpadJoyAssignmentModeSingleByDefault,
"SetNpadJoyAssignmentModeSingleByDefault"},
{124, nullptr, "SetNpadJoyAssignmentModeDual"},
{128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
{200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
{201, &Hid::SendVibrationValue, "SendVibrationValue"},
{202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
{203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
{206, &Hid::SendVibrationValues, "SendVibrationValues"},
};
RegisterHandlers(functions);
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "hid:EventHandle");
}
~Hid() = default;
private:
std::shared_ptr<IAppletResource> applet_resource;
u32 joy_hold_type{0};
Kernel::SharedPtr<Kernel::Event> event;
void CreateAppletResource(Kernel::HLERequestContext& ctx) {
if (applet_resource == nullptr) {
@@ -172,7 +220,125 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IAppletResource>(applet_resource);
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_HID, "called");
}
void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateMouse(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateNpad(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(joy_hold_type);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SendVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IActiveVibrationDeviceList>();
LOG_DEBUG(Service_HID, "called");
}
void SendVibrationValues(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
};

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <sstream>
#include <string>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
@@ -28,19 +29,28 @@ private:
IsHead = 1,
IsTail = 2,
};
enum Severity : u32_le {
Trace,
Info,
Warning,
Error,
Critical,
};
u64_le pid;
u64_le threadContext;
union {
BitField<0, 16, Flags> flags;
BitField<16, 8, u32_le> severity;
BitField<16, 8, Severity> severity;
BitField<24, 8, u32_le> verbosity;
};
u32_le payload_size;
/// Returns true if this is part of a single log message
bool IsSingleMessage() const {
return (flags & Flags::IsHead) && (flags & Flags::IsTail);
bool IsHeadLog() const {
return flags & Flags::IsHead;
}
bool IsTailLog() const {
return flags & Flags::IsTail;
}
};
static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
@@ -57,7 +67,7 @@ private:
};
/**
* LM::Initialize service function
* LM::Log service function
* Inputs:
* 0: 0x00000000
* Outputs:
@@ -75,9 +85,9 @@ private:
Memory::ReadBlock(addr, &header, sizeof(MessageHeader));
addr += sizeof(MessageHeader);
if (!header.IsSingleMessage()) {
LOG_WARNING(Service, "Multi message logs are unimplemeneted");
return;
if (header.IsHeadLog()) {
log_stream.str("");
log_stream.clear();
}
// Parse out log metadata
@@ -85,7 +95,7 @@ private:
std::string message, filename, function;
while (addr < end_addr) {
const Field field{static_cast<Field>(Memory::Read8(addr++))};
size_t length{Memory::Read8(addr++)};
const size_t length{Memory::Read8(addr++)};
if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) {
++addr;
@@ -110,28 +120,47 @@ private:
}
// Empty log - nothing to do here
if (message.empty()) {
if (log_stream.str().empty() && message.empty()) {
return;
}
// Format a nicely printable string out of the log metadata
std::string output;
if (filename.size()) {
output += filename + ':';
if (!filename.empty()) {
log_stream << filename << ':';
}
if (function.size()) {
output += function + ':';
if (!function.empty()) {
log_stream << function << ':';
}
if (line) {
output += std::to_string(line) + ':';
log_stream << std::to_string(line) << ':';
}
if (output.back() == ':') {
output += ' ';
if (log_stream.str().length() > 0 && log_stream.str().back() == ':') {
log_stream << ' ';
}
output += message;
log_stream << message;
LOG_INFO(Debug_Emulated, "%s", output.c_str());
if (header.IsTailLog()) {
switch (header.severity) {
case MessageHeader::Severity::Trace:
LOG_TRACE(Debug_Emulated, "%s", log_stream.str().c_str());
break;
case MessageHeader::Severity::Info:
LOG_INFO(Debug_Emulated, "%s", log_stream.str().c_str());
break;
case MessageHeader::Severity::Warning:
LOG_WARNING(Debug_Emulated, "%s", log_stream.str().c_str());
break;
case MessageHeader::Severity::Error:
LOG_ERROR(Debug_Emulated, "%s", log_stream.str().c_str());
break;
case MessageHeader::Severity::Critical:
LOG_CRITICAL(Debug_Emulated, "%s", log_stream.str().c_str());
break;
}
}
}
std::ostringstream log_stream;
};
void InstallInterfaces(SM::ServiceManager& service_manager) {
@@ -150,7 +179,7 @@ void LM::Initialize(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<Logger>();
LOG_DEBUG(Service, "called");
LOG_DEBUG(Service_LM, "called");
}
LM::LM() : ServiceFramework("lm") {

View File

@@ -0,0 +1,213 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/nifm/nifm_a.h"
#include "core/hle/service/nifm/nifm_s.h"
#include "core/hle/service/nifm/nifm_u.h"
namespace Service {
namespace NIFM {
class IScanRequest final : public ServiceFramework<IScanRequest> {
public:
explicit IScanRequest() : ServiceFramework("IScanRequest") {
static const FunctionInfo functions[] = {
{0, nullptr, "Submit"},
{1, nullptr, "IsProcessing"},
{2, nullptr, "GetResult"},
{3, nullptr, "GetSystemEventReadableHandle"},
};
RegisterHandlers(functions);
}
};
class IRequest final : public ServiceFramework<IRequest> {
public:
explicit IRequest() : ServiceFramework("IRequest") {
static const FunctionInfo functions[] = {
{0, &IRequest::GetRequestState, "GetRequestState"},
{1, &IRequest::GetResult, "GetResult"},
{2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"},
{3, &IRequest::Cancel, "Cancel"},
{4, nullptr, "Submit"},
{5, nullptr, "SetRequirement"},
{6, nullptr, "SetRequirementPreset"},
{8, nullptr, "SetPriority"},
{9, nullptr, "SetNetworkProfileId"},
{10, nullptr, "SetRejectable"},
{11, nullptr, "SetConnectionConfirmationOption"},
{12, nullptr, "SetPersistent"},
{13, nullptr, "SetInstant"},
{14, nullptr, "SetSustainable"},
{15, nullptr, "SetRawPriority"},
{16, nullptr, "SetGreedy"},
{17, nullptr, "SetSharable"},
{18, nullptr, "SetRequirementByRevision"},
{19, nullptr, "GetRequirement"},
{20, nullptr, "GetRevision"},
{21, nullptr, "GetAppletInfo"},
{22, nullptr, "GetAdditionalInfo"},
{23, nullptr, "SetKeptInSleep"},
{24, nullptr, "RegisterSocketDescriptor"},
{25, nullptr, "UnregisterSocketDescriptor"},
};
RegisterHandlers(functions);
event1 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event1");
event2 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event2");
}
private:
void GetRequestState(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
void GetResult(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 2};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event1, event2);
}
void Cancel(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
Kernel::SharedPtr<Kernel::Event> event1, event2;
};
class INetworkProfile final : public ServiceFramework<INetworkProfile> {
public:
explicit INetworkProfile() : ServiceFramework("INetworkProfile") {
static const FunctionInfo functions[] = {
{0, nullptr, "Update"},
{1, nullptr, "PersistOld"},
{2, nullptr, "Persist"},
};
RegisterHandlers(functions);
}
};
class IGeneralService final : public ServiceFramework<IGeneralService> {
public:
IGeneralService();
private:
void GetClientId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
}
void CreateScanRequest(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IScanRequest>();
LOG_DEBUG(Service_NIFM, "called");
}
void CreateRequest(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IRequest>();
LOG_DEBUG(Service_NIFM, "called");
}
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<INetworkProfile>();
LOG_DEBUG(Service_NIFM, "called");
}
};
IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
static const FunctionInfo functions[] = {
{1, &IGeneralService::GetClientId, "GetClientId"},
{2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
{4, &IGeneralService::CreateRequest, "CreateRequest"},
{5, nullptr, "GetCurrentNetworkProfile"},
{6, nullptr, "EnumerateNetworkInterfaces"},
{7, nullptr, "EnumerateNetworkProfiles"},
{8, nullptr, "GetNetworkProfile"},
{9, nullptr, "SetNetworkProfile"},
{10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"},
{11, nullptr, "GetScanDataOld"},
{12, nullptr, "GetCurrentIpAddress"},
{13, nullptr, "GetCurrentAccessPointOld"},
{14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
{15, nullptr, "GetCurrentIpConfigInfo"},
{16, nullptr, "SetWirelessCommunicationEnabled"},
{17, nullptr, "IsWirelessCommunicationEnabled"},
{18, nullptr, "GetInternetConnectionStatus"},
{19, nullptr, "SetEthernetCommunicationEnabled"},
{20, nullptr, "IsEthernetCommunicationEnabled"},
{21, nullptr, "IsAnyInternetRequestAccepted"},
{22, nullptr, "IsAnyForegroundRequestAccepted"},
{23, nullptr, "PutToSleep"},
{24, nullptr, "WakeUp"},
{25, nullptr, "GetSsidListVersion"},
{26, nullptr, "SetExclusiveClient"},
{27, nullptr, "GetDefaultIpSetting"},
{28, nullptr, "SetDefaultIpSetting"},
{29, nullptr, "SetWirelessCommunicationEnabledForTest"},
{30, nullptr, "SetEthernetCommunicationEnabledForTest"},
{31, nullptr, "GetTelemetorySystemEventReadableHandle"},
{32, nullptr, "GetTelemetryInfo"},
{33, nullptr, "ConfirmSystemAvailability"},
{34, nullptr, "SetBackgroundRequestEnabled"},
{35, nullptr, "GetScanData"},
{36, nullptr, "GetCurrentAccessPoint"},
{37, nullptr, "Shutdown"},
};
RegisterHandlers(functions);
}
void Module::Interface::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IGeneralService>();
LOG_DEBUG(Service_NIFM, "called");
}
void Module::Interface::CreateGeneralService(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IGeneralService>();
LOG_DEBUG(Service_NIFM, "called");
}
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {}
void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module = std::make_shared<Module>();
std::make_shared<NIFM_A>(module)->InstallAsService(service_manager);
std::make_shared<NIFM_S>(module)->InstallAsService(service_manager);
std::make_shared<NIFM_U>(module)->InstallAsService(service_manager);
}
} // namespace NIFM
} // namespace Service

View File

@@ -0,0 +1,29 @@
// 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 NIFM {
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> module, const char* name);
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
void CreateGeneralService(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
};
};
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace NIFM
} // namespace Service

View File

@@ -0,0 +1,19 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/nifm/nifm_a.h"
namespace Service {
namespace NIFM {
NIFM_A::NIFM_A(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:a") {
static const FunctionInfo functions[] = {
{4, &NIFM_A::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
{5, &NIFM_A::CreateGeneralService, "CreateGeneralService"},
};
RegisterHandlers(functions);
}
} // namespace NIFM
} // namespace Service

View File

@@ -0,0 +1,18 @@
// 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/nifm/nifm.h"
namespace Service {
namespace NIFM {
class NIFM_A final : public Module::Interface {
public:
explicit NIFM_A(std::shared_ptr<Module> module);
};
} // namespace NIFM
} // namespace Service

View File

@@ -0,0 +1,19 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/nifm/nifm_s.h"
namespace Service {
namespace NIFM {
NIFM_S::NIFM_S(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:s") {
static const FunctionInfo functions[] = {
{4, &NIFM_S::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
{5, &NIFM_S::CreateGeneralService, "CreateGeneralService"},
};
RegisterHandlers(functions);
}
} // namespace NIFM
} // namespace Service

View File

@@ -0,0 +1,18 @@
// 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/nifm/nifm.h"
namespace Service {
namespace NIFM {
class NIFM_S final : public Module::Interface {
public:
explicit NIFM_S(std::shared_ptr<Module> module);
};
} // namespace NIFM
} // namespace Service

View File

@@ -0,0 +1,19 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/nifm/nifm_u.h"
namespace Service {
namespace NIFM {
NIFM_U::NIFM_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:u") {
static const FunctionInfo functions[] = {
{4, &NIFM_U::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
{5, &NIFM_U::CreateGeneralService, "CreateGeneralService"},
};
RegisterHandlers(functions);
}
} // namespace NIFM
} // namespace Service

View File

@@ -0,0 +1,18 @@
// 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/nifm/nifm.h"
namespace Service {
namespace NIFM {
class NIFM_U final : public Module::Interface {
public:
explicit NIFM_U(std::shared_ptr<Module> module);
};
} // namespace NIFM
} // namespace Service

View File

@@ -0,0 +1,16 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/ns/pl_u.h"
namespace Service {
namespace NS {
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<PL_U>()->InstallAsService(service_manager);
}
} // namespace NS
} // namespace Service

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