Compare commits
273 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e3222363c | ||
|
|
9c468e0c55 | ||
|
|
d7b1ebe4a8 | ||
|
|
4bdb46e4c2 | ||
|
|
a3e10b1a72 | ||
|
|
db0cfb8e8b | ||
|
|
0e4b9cdde4 | ||
|
|
a1cf5020e6 | ||
|
|
6a0902e56d | ||
|
|
a90ab1dec7 | ||
|
|
1d8b6ad13b | ||
|
|
d16e08454d | ||
|
|
0e72d0d826 | ||
|
|
ef875d6a35 | ||
|
|
b2d7c92cae | ||
|
|
dcae0c9a4f | ||
|
|
cff7b29bba | ||
|
|
23a0d2d7b7 | ||
|
|
2dc3a56e96 | ||
|
|
2332a44b68 | ||
|
|
c1c92c30f9 | ||
|
|
c86af6939c | ||
|
|
2faa83ca13 | ||
|
|
019f1a0cf0 | ||
|
|
e353b9fb3d | ||
|
|
03156d0c9a | ||
|
|
6317a0b2ca | ||
|
|
7b6868e908 | ||
|
|
85d820b1b4 | ||
|
|
a64b936cbe | ||
|
|
46f9d4b4a3 | ||
|
|
aa586fa268 | ||
|
|
7ac8657432 | ||
|
|
29981fa2eb | ||
|
|
ccb8da1512 | ||
|
|
88698c156f | ||
|
|
66dae22790 | ||
|
|
1d9d9c16e8 | ||
|
|
579000e747 | ||
|
|
516ef4f19f | ||
|
|
c286921739 | ||
|
|
f93d769a1c | ||
|
|
d2888f7e90 | ||
|
|
cd4e8a989c | ||
|
|
29feece4b8 | ||
|
|
0eff775264 | ||
|
|
e453b09a61 | ||
|
|
bf310a41b8 | ||
|
|
cbec739e7b | ||
|
|
494275fd38 | ||
|
|
e7ba2a4447 | ||
|
|
cc6f22e0e4 | ||
|
|
e9a857ce82 | ||
|
|
403f8e79ea | ||
|
|
34a29ad051 | ||
|
|
8581404482 | ||
|
|
69ee9edd8d | ||
|
|
3923b0f589 | ||
|
|
8be7131033 | ||
|
|
07ae1f972d | ||
|
|
fbfa7ddd62 | ||
|
|
9289255314 | ||
|
|
ec030a542f | ||
|
|
cde9386e0f | ||
|
|
7d6653268f | ||
|
|
8538e0bc3d | ||
|
|
5750f6f046 | ||
|
|
5fb4c718cc | ||
|
|
80562aaf64 | ||
|
|
84e1c0a430 | ||
|
|
e4b7a1d160 | ||
|
|
0eefe6e4d1 | ||
|
|
3c3d5eeddf | ||
|
|
248881fa7f | ||
|
|
7e7110b3b9 | ||
|
|
aef7a15b93 | ||
|
|
656e7aab29 | ||
|
|
28669872d9 | ||
|
|
46fc7d8502 | ||
|
|
3209cff530 | ||
|
|
827f8ca3c7 | ||
|
|
cc6e4ae6cf | ||
|
|
d140c8ecf7 | ||
|
|
b1709410dd | ||
|
|
c74af07c49 | ||
|
|
6e38081165 | ||
|
|
65f3119074 | ||
|
|
926604fc14 | ||
|
|
f1beb69899 | ||
|
|
7e45669ccb | ||
|
|
e29710818f | ||
|
|
487f8bc018 | ||
|
|
fd3806fd30 | ||
|
|
2b28fd7809 | ||
|
|
08c6ac02cf | ||
|
|
bc88cae0c7 | ||
|
|
2b41c6e573 | ||
|
|
c45173c9a6 | ||
|
|
32c509b82d | ||
|
|
6bf7108545 | ||
|
|
a63d4fa5b4 | ||
|
|
e4f94ee30b | ||
|
|
910198a29a | ||
|
|
7f0ecbf859 | ||
|
|
fa28dbe0f3 | ||
|
|
944132dbe5 | ||
|
|
32d127ad3e | ||
|
|
e8b9731af3 | ||
|
|
cc368de1a0 | ||
|
|
1cdc74c5e9 | ||
|
|
1289a3c3c1 | ||
|
|
827152d1fd | ||
|
|
1572c45aa0 | ||
|
|
6a2197806e | ||
|
|
587f22b610 | ||
|
|
46931a9566 | ||
|
|
678574972a | ||
|
|
7bee3427d0 | ||
|
|
1d491d636d | ||
|
|
23fe6f5be3 | ||
|
|
8db80d8389 | ||
|
|
ad55d22130 | ||
|
|
5ab285f1f9 | ||
|
|
ac81c02ed9 | ||
|
|
c78d495161 | ||
|
|
2d4a6883bc | ||
|
|
cec0d4f191 | ||
|
|
de43db3400 | ||
|
|
ec39c9eb32 | ||
|
|
5babad5de5 | ||
|
|
94ee8fc97b | ||
|
|
b36ce74d18 | ||
|
|
f6e548fbc0 | ||
|
|
416f692f6e | ||
|
|
d758332425 | ||
|
|
2662de6e52 | ||
|
|
1b64160d83 | ||
|
|
bd6432f1ff | ||
|
|
3388208597 | ||
|
|
947831ff1e | ||
|
|
6797d4a907 | ||
|
|
98ffad4303 | ||
|
|
7a1917e0fd | ||
|
|
35d0d06885 | ||
|
|
d18446f63a | ||
|
|
b78ffc4abf | ||
|
|
8dee5663b3 | ||
|
|
df008a159b | ||
|
|
42c062c620 | ||
|
|
fa58d95027 | ||
|
|
e017184445 | ||
|
|
db873a232c | ||
|
|
88bfec37ce | ||
|
|
756e9f1484 | ||
|
|
a8d8c21e00 | ||
|
|
61c39f0fdd | ||
|
|
b65c096be5 | ||
|
|
d939792b9b | ||
|
|
fe0775d2f4 | ||
|
|
e6bf72877f | ||
|
|
2ade136ff4 | ||
|
|
eba57fce88 | ||
|
|
ee170cbcea | ||
|
|
983777a317 | ||
|
|
2d388a75f0 | ||
|
|
ce1fe0387f | ||
|
|
dc97117a0b | ||
|
|
c1146d2a5f | ||
|
|
1e33db8573 | ||
|
|
51ce224a96 | ||
|
|
f1b82634bc | ||
|
|
6fd19f05f1 | ||
|
|
c85e3a2234 | ||
|
|
516a95721c | ||
|
|
d6e52581ac | ||
|
|
f16bb1dfcf | ||
|
|
d42e77797e | ||
|
|
8f84665775 | ||
|
|
fc1b425520 | ||
|
|
490d0e36a0 | ||
|
|
1ce6fff064 | ||
|
|
4f8ee5e456 | ||
|
|
af8ae770ef | ||
|
|
0a55eb588b | ||
|
|
826e9c9782 | ||
|
|
87c3c93464 | ||
|
|
55de13efcc | ||
|
|
91e19deb39 | ||
|
|
a9e4e8294a | ||
|
|
4f969e2271 | ||
|
|
0a87eb71ba | ||
|
|
6085d32cf5 | ||
|
|
ce8006e851 | ||
|
|
3160f83607 | ||
|
|
be5ba4d952 | ||
|
|
ac61a7d1e6 | ||
|
|
6cddf9d88e | ||
|
|
e01a8f2187 | ||
|
|
890e98a33e | ||
|
|
ba2426aa3f | ||
|
|
deadcb39c2 | ||
|
|
6fce1414c3 | ||
|
|
068744db1b | ||
|
|
b26cdf1fe5 | ||
|
|
8e7da73214 | ||
|
|
0532de6559 | ||
|
|
c83a1b2320 | ||
|
|
725304094e | ||
|
|
63de56ee0f | ||
|
|
309276a317 | ||
|
|
1add3b20c4 | ||
|
|
3b35202280 | ||
|
|
45e5a67676 | ||
|
|
22caeee64f | ||
|
|
576f0cf027 | ||
|
|
ca99063600 | ||
|
|
205daab50d | ||
|
|
d3bbed5e78 | ||
|
|
dc0a137e5b | ||
|
|
db11c9a0b9 | ||
|
|
a39a65cbe0 | ||
|
|
c711253798 | ||
|
|
196f8dff08 | ||
|
|
703880c9ab | ||
|
|
869d65e923 | ||
|
|
335096e19a | ||
|
|
2b75b52489 | ||
|
|
8d300b2d7e | ||
|
|
1cd9438945 | ||
|
|
903beb43a8 | ||
|
|
1963222933 | ||
|
|
d129905a66 | ||
|
|
c83f69841f | ||
|
|
294b2b2c17 | ||
|
|
e33117c00a | ||
|
|
22bc951d7e | ||
|
|
f9ba5a7e11 | ||
|
|
35517ca92c | ||
|
|
1d51b25ed1 | ||
|
|
fe99052599 | ||
|
|
8e1dbb26bd | ||
|
|
c689fe8424 | ||
|
|
fc1359dc03 | ||
|
|
649960b4eb | ||
|
|
8d2e4c3d39 | ||
|
|
69697535bf | ||
|
|
485c6541cf | ||
|
|
8a5833f7ad | ||
|
|
119f02a439 | ||
|
|
ad97414057 | ||
|
|
ea615ef5a4 | ||
|
|
764bbaa19c | ||
|
|
65cfe09b62 | ||
|
|
a947f16b63 | ||
|
|
6674e8e048 | ||
|
|
1ddc18454e | ||
|
|
42fc437268 | ||
|
|
0b6b147939 | ||
|
|
eaf75ea970 | ||
|
|
272058d7d9 | ||
|
|
647364db8f | ||
|
|
f9c9ce2005 | ||
|
|
abc4be8e0f | ||
|
|
b5bdaf3441 | ||
|
|
72c5bfb1fa | ||
|
|
f67a8d87a0 | ||
|
|
1a8f5bfb8e | ||
|
|
5ad9b3e19d | ||
|
|
1909802156 | ||
|
|
2e6776909b | ||
|
|
524c12a5f8 | ||
|
|
58601abd1c | ||
|
|
eaa9f968a6 |
13
.travis.yml
13
.travis.yml
@@ -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
3
.travis/clang-format/build.sh
Executable 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
3
.travis/clang-format/deps.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
docker pull ubuntu:18.04
|
||||
8
.travis/clang-format/docker.sh
Executable file
8
.travis/clang-format/docker.sh
Executable 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
|
||||
@@ -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
|
||||
|
||||
|
||||
2
externals/catch
vendored
2
externals/catch
vendored
Submodule externals/catch updated: 62dae592c3...cd76f5730c
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: a6d17e6bb0...6b4c6b06a9
2
externals/glad/Readme.md
vendored
2
externals/glad/Readme.md
vendored
@@ -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
|
||||
```
|
||||
|
||||
6
externals/glad/include/KHR/khrplatform.h
vendored
6
externals/glad/include/KHR/khrplatform.h
vendored
@@ -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.
|
||||
*/
|
||||
|
||||
13755
externals/glad/include/glad/glad.h
vendored
13755
externals/glad/include/glad/glad.h
vendored
File diff suppressed because one or more lines are too long
8704
externals/glad/src/glad.c
vendored
8704
externals/glad/src/glad.c
vendored
File diff suppressed because one or more lines are too long
2
externals/xbyak
vendored
2
externals/xbyak
vendored
Submodule externals/xbyak updated: d512551e91...2794cde79e
@@ -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) \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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{};
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
146
src/core/file_sys/disk_filesystem.cpp
Normal file
146
src/core/file_sys/disk_filesystem.cpp
Normal 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
|
||||
66
src/core/file_sys/disk_filesystem.h
Normal file
66
src/core/file_sys/disk_filesystem.h
Normal 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
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
114
src/core/file_sys/program_metadata.cpp
Normal file
114
src/core/file_sys/program_metadata.cpp
Normal 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
|
||||
154
src/core/file_sys/program_metadata.h
Normal file
154
src/core/file_sys/program_metadata.h
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
57
src/core/file_sys/savedata_factory.cpp
Normal file
57
src/core/file_sys/savedata_factory.cpp
Normal 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
|
||||
33
src/core/file_sys/savedata_factory.h
Normal file
33
src/core/file_sys/savedata_factory.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -41,7 +41,6 @@ void Shutdown() {
|
||||
g_object_address_table.Clear();
|
||||
|
||||
Kernel::ThreadingShutdown();
|
||||
g_current_process = nullptr;
|
||||
|
||||
Kernel::TimersShutdown();
|
||||
Kernel::ResourceLimitsShutdown();
|
||||
|
||||
@@ -33,10 +33,6 @@ enum class HandleType : u32 {
|
||||
ServerSession,
|
||||
};
|
||||
|
||||
enum {
|
||||
DEFAULT_STACK_SIZE = 0x10000,
|
||||
};
|
||||
|
||||
enum class ResetType {
|
||||
OneShot,
|
||||
Sticky,
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
135
src/core/hle/kernel/scheduler.cpp
Normal file
135
src/core/hle/kernel/scheduler.cpp
Normal 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
|
||||
73
src/core/hle/kernel/scheduler.h
Normal file
73
src/core/hle/kernel/scheduler.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
112
src/core/hle/service/am/applet_ae.cpp
Normal file
112
src/core/hle/service/am/applet_ae.cpp
Normal 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
|
||||
30
src/core/hle/service/am/applet_ae.h
Normal file
30
src/core/hle/service/am/applet_ae.h
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
66
src/core/hle/service/apm/interface.cpp
Normal file
66
src/core/hle/service/apm/interface.cpp
Normal 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
|
||||
27
src/core/hle/service/apm/interface.h
Normal file
27
src/core/hle/service/apm/interface.h
Normal 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
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
28
src/core/hle/service/friend/friend.cpp
Normal file
28
src/core/hle/service/friend/friend.cpp
Normal 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
|
||||
29
src/core/hle/service/friend/friend.h
Normal file
29
src/core/hle/service/friend/friend.h
Normal 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
|
||||
19
src/core/hle/service/friend/friend_a.cpp
Normal file
19
src/core/hle/service/friend/friend_a.cpp
Normal 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
|
||||
18
src/core/hle/service/friend/friend_a.h
Normal file
18
src/core/hle/service/friend/friend_a.h
Normal 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
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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") {
|
||||
|
||||
213
src/core/hle/service/nifm/nifm.cpp
Normal file
213
src/core/hle/service/nifm/nifm.cpp
Normal 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
|
||||
29
src/core/hle/service/nifm/nifm.h
Normal file
29
src/core/hle/service/nifm/nifm.h
Normal 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
|
||||
19
src/core/hle/service/nifm/nifm_a.cpp
Normal file
19
src/core/hle/service/nifm/nifm_a.cpp
Normal 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
|
||||
18
src/core/hle/service/nifm/nifm_a.h
Normal file
18
src/core/hle/service/nifm/nifm_a.h
Normal 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
|
||||
19
src/core/hle/service/nifm/nifm_s.cpp
Normal file
19
src/core/hle/service/nifm/nifm_s.cpp
Normal 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
|
||||
18
src/core/hle/service/nifm/nifm_s.h
Normal file
18
src/core/hle/service/nifm/nifm_s.h
Normal 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
|
||||
19
src/core/hle/service/nifm/nifm_u.cpp
Normal file
19
src/core/hle/service/nifm/nifm_u.cpp
Normal 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
|
||||
18
src/core/hle/service/nifm/nifm_u.h
Normal file
18
src/core/hle/service/nifm/nifm_u.h
Normal 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
|
||||
16
src/core/hle/service/ns/ns.cpp
Normal file
16
src/core/hle/service/ns/ns.cpp
Normal 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
Reference in New Issue
Block a user