summaryrefslogtreecommitdiff
path: root/src/core/hle
diff options
context:
space:
mode:
authorGravatar bunnei2017-10-09 23:56:20 -0400
committerGravatar bunnei2017-10-09 23:56:20 -0400
commitb1d5db1cf60344b6b081c9d03cb6ccc3264326cd (patch)
treefde377c4ba3c0f92c032e6f5ec8627aae37270ef /src/core/hle
parentloader: Various improvements for NSO/NRO loaders. (diff)
parentMerge pull request #2996 from MerryMage/split-travis (diff)
downloadyuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.tar.gz
yuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.tar.xz
yuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.zip
Merge remote-tracking branch 'upstream/master' into nx
# Conflicts: # src/core/CMakeLists.txt # src/core/arm/dynarmic/arm_dynarmic.cpp # src/core/arm/dyncom/arm_dyncom.cpp # src/core/hle/kernel/process.cpp # src/core/hle/kernel/thread.cpp # src/core/hle/kernel/thread.h # src/core/hle/kernel/vm_manager.cpp # src/core/loader/3dsx.cpp # src/core/loader/elf.cpp # src/core/loader/ncch.cpp # src/core/memory.cpp # src/core/memory.h # src/core/memory_setup.h
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/applets/erreula.cpp4
-rw-r--r--src/core/hle/applets/mii_selector.cpp10
-rw-r--r--src/core/hle/applets/mii_selector.h57
-rw-r--r--src/core/hle/applets/mint.cpp4
-rw-r--r--src/core/hle/applets/swkbd.cpp4
-rw-r--r--src/core/hle/function_wrappers.h108
-rw-r--r--src/core/hle/ipc.h8
-rw-r--r--src/core/hle/ipc_helpers.h12
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp2
-rw-r--r--src/core/hle/kernel/kernel.h5
-rw-r--r--src/core/hle/kernel/memory.cpp30
-rw-r--r--src/core/hle/kernel/memory.h2
-rw-r--r--src/core/hle/kernel/mutex.cpp5
-rw-r--r--src/core/hle/kernel/mutex.h3
-rw-r--r--src/core/hle/kernel/process.cpp3
-rw-r--r--src/core/hle/kernel/resource_limit.cpp2
-rw-r--r--src/core/hle/kernel/resource_limit.h2
-rw-r--r--src/core/hle/kernel/semaphore.cpp3
-rw-r--r--src/core/hle/kernel/semaphore.h3
-rw-r--r--src/core/hle/kernel/shared_memory.cpp30
-rw-r--r--src/core/hle/kernel/shared_memory.h2
-rw-r--r--src/core/hle/kernel/thread.cpp68
-rw-r--r--src/core/hle/kernel/thread.h48
-rw-r--r--src/core/hle/kernel/vm_manager.cpp19
-rw-r--r--src/core/hle/kernel/vm_manager.h6
-rw-r--r--src/core/hle/kernel/wait_object.cpp13
-rw-r--r--src/core/hle/lock.cpp11
-rw-r--r--src/core/hle/lock.h18
-rw-r--r--src/core/hle/service/apt/apt.cpp729
-rw-r--r--src/core/hle/service/apt/apt.h20
-rw-r--r--src/core/hle/service/apt/apt_s.cpp8
-rw-r--r--src/core/hle/service/cam/cam.cpp2
-rw-r--r--src/core/hle/service/cfg/cfg.cpp6
-rw-r--r--src/core/hle/service/cfg/cfg.h2
-rw-r--r--src/core/hle/service/csnd_snd.cpp2
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.cpp21
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.cpp18
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.cpp9
-rw-r--r--src/core/hle/service/dsp_dsp.cpp7
-rw-r--r--src/core/hle/service/fs/archive.cpp20
-rw-r--r--src/core/hle/service/fs/archive.h7
-rw-r--r--src/core/hle/service/hid/hid.cpp44
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/ir/ir_rst.cpp2
-rw-r--r--src/core/hle/service/ldr_ro/cro_helper.h6
-rw-r--r--src/core/hle/service/nim/nim.cpp18
-rw-r--r--src/core/hle/service/nim/nim.h11
-rw-r--r--src/core/hle/service/nim/nim_u.cpp2
-rw-r--r--src/core/hle/service/ns/ns.cpp16
-rw-r--r--src/core/hle/service/ns/ns.h16
-rw-r--r--src/core/hle/service/ns/ns_s.cpp34
-rw-r--r--src/core/hle/service/ns/ns_s.h (renamed from src/core/hle/service/ns_s.h)9
-rw-r--r--src/core/hle/service/ns_s.cpp33
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp485
-rw-r--r--src/core/hle/service/nwm/nwm_uds.h12
-rw-r--r--src/core/hle/service/nwm/uds_beacon.cpp7
-rw-r--r--src/core/hle/service/nwm/uds_beacon.h30
-rw-r--r--src/core/hle/service/nwm/uds_connection.cpp88
-rw-r--r--src/core/hle/service/nwm/uds_connection.h56
-rw-r--r--src/core/hle/service/nwm/uds_data.cpp103
-rw-r--r--src/core/hle/service/nwm/uds_data.h86
-rw-r--r--src/core/hle/service/service.cpp5
-rw-r--r--src/core/hle/service/sm/sm.cpp4
-rw-r--r--src/core/hle/service/sm/sm.h3
-rw-r--r--src/core/hle/service/sm/srv.cpp28
-rw-r--r--src/core/hle/service/sm/srv.h1
66 files changed, 1824 insertions, 610 deletions
diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp
index 75d7fd9fc..518f371f5 100644
--- a/src/core/hle/applets/erreula.cpp
+++ b/src/core/hle/applets/erreula.cpp
@@ -31,8 +31,8 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param
31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
32 // Create a SharedMemory that directly points to this heap block. 32 // Create a SharedMemory that directly points to this heap block.
33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
34 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 34 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
35 MemoryPermission::ReadWrite, "ErrEula Memory"); 35 "ErrEula Memory");
36 36
37 // Send the response message with the newly created SharedMemory 37 // Send the response message with the newly created SharedMemory
38 Service::APT::MessageParameter result; 38 Service::APT::MessageParameter result;
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 89f08daa2..f225c23a5 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -38,8 +38,8 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
38 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 38 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
39 // Create a SharedMemory that directly points to this heap block. 39 // Create a SharedMemory that directly points to this heap block.
40 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 40 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
41 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 41 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
42 MemoryPermission::ReadWrite, "MiiSelector Memory"); 42 "MiiSelector Memory");
43 43
44 // Send the response message with the newly created SharedMemory 44 // Send the response message with the newly created SharedMemory
45 Service::APT::MessageParameter result; 45 Service::APT::MessageParameter result;
@@ -66,7 +66,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
66 // continue. 66 // continue.
67 MiiResult result; 67 MiiResult result;
68 memset(&result, 0, sizeof(result)); 68 memset(&result, 0, sizeof(result));
69 result.result_code = 0; 69 result.return_code = 0;
70 70
71 // Let the application know that we're closing 71 // Let the application know that we're closing
72 Service::APT::MessageParameter message; 72 Service::APT::MessageParameter message;
@@ -82,5 +82,5 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
82} 82}
83 83
84void MiiSelector::Update() {} 84void MiiSelector::Update() {}
85} 85} // namespace Applets
86} // namespace 86} // namespace HLE
diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h
index ec00e29d2..136ce8948 100644
--- a/src/core/hle/applets/mii_selector.h
+++ b/src/core/hle/applets/mii_selector.h
@@ -16,51 +16,46 @@ namespace HLE {
16namespace Applets { 16namespace Applets {
17 17
18struct MiiConfig { 18struct MiiConfig {
19 u8 unk_000; 19 u8 enable_cancel_button;
20 u8 unk_001; 20 u8 enable_guest_mii;
21 u8 unk_002; 21 u8 show_on_top_screen;
22 u8 unk_003; 22 INSERT_PADDING_BYTES(5);
23 u8 unk_004; 23 u16 title[0x40];
24 INSERT_PADDING_BYTES(4);
25 u8 show_guest_miis;
24 INSERT_PADDING_BYTES(3); 26 INSERT_PADDING_BYTES(3);
25 u16 unk_008; 27 u32 initially_selected_mii_index;
26 INSERT_PADDING_BYTES(0x82); 28 u8 guest_mii_whitelist[6];
27 u8 unk_08C; 29 u8 user_mii_whitelist[0x64];
28 INSERT_PADDING_BYTES(3);
29 u16 unk_090;
30 INSERT_PADDING_BYTES(2); 30 INSERT_PADDING_BYTES(2);
31 u32 unk_094; 31 u32 magic_value;
32 u16 unk_098;
33 u8 unk_09A[0x64];
34 u8 unk_0FE;
35 u8 unk_0FF;
36 u32 unk_100;
37}; 32};
38
39static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size"); 33static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size");
40#define ASSERT_REG_POSITION(field_name, position) \ 34#define ASSERT_REG_POSITION(field_name, position) \
41 static_assert(offsetof(MiiConfig, field_name) == position, \ 35 static_assert(offsetof(MiiConfig, field_name) == position, \
42 "Field " #field_name " has invalid position") 36 "Field " #field_name " has invalid position")
43ASSERT_REG_POSITION(unk_008, 0x08); 37ASSERT_REG_POSITION(title, 0x08);
44ASSERT_REG_POSITION(unk_08C, 0x8C); 38ASSERT_REG_POSITION(show_guest_miis, 0x8C);
45ASSERT_REG_POSITION(unk_090, 0x90); 39ASSERT_REG_POSITION(initially_selected_mii_index, 0x90);
46ASSERT_REG_POSITION(unk_094, 0x94); 40ASSERT_REG_POSITION(guest_mii_whitelist, 0x94);
47ASSERT_REG_POSITION(unk_0FE, 0xFE);
48#undef ASSERT_REG_POSITION 41#undef ASSERT_REG_POSITION
49 42
50struct MiiResult { 43struct MiiResult {
51 u32 result_code; 44 u32 return_code;
52 u8 unk_04; 45 u32 is_guest_mii_selected;
53 INSERT_PADDING_BYTES(7); 46 u32 selected_guest_mii_index;
54 u8 unk_0C[0x60]; 47 // TODO(mailwl): expand to Mii Format structure: https://www.3dbrew.org/wiki/Mii
55 u8 unk_6C[0x16]; 48 u8 selected_mii_data[0x5C];
56 INSERT_PADDING_BYTES(2); 49 INSERT_PADDING_BYTES(2);
50 u16 mii_data_checksum;
51 u16 guest_mii_name[0xC];
57}; 52};
58static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size"); 53static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size");
59#define ASSERT_REG_POSITION(field_name, position) \ 54#define ASSERT_REG_POSITION(field_name, position) \
60 static_assert(offsetof(MiiResult, field_name) == position, \ 55 static_assert(offsetof(MiiResult, field_name) == position, \
61 "Field " #field_name " has invalid position") 56 "Field " #field_name " has invalid position")
62ASSERT_REG_POSITION(unk_0C, 0x0C); 57ASSERT_REG_POSITION(selected_mii_data, 0x0C);
63ASSERT_REG_POSITION(unk_6C, 0x6C); 58ASSERT_REG_POSITION(guest_mii_name, 0x6C);
64#undef ASSERT_REG_POSITION 59#undef ASSERT_REG_POSITION
65 60
66class MiiSelector final : public Applet { 61class MiiSelector final : public Applet {
@@ -79,5 +74,5 @@ private:
79 74
80 MiiConfig config; 75 MiiConfig config;
81}; 76};
82} 77} // namespace Applets
83} // namespace 78} // namespace HLE
diff --git a/src/core/hle/applets/mint.cpp b/src/core/hle/applets/mint.cpp
index 31a79ea17..50d79190b 100644
--- a/src/core/hle/applets/mint.cpp
+++ b/src/core/hle/applets/mint.cpp
@@ -31,8 +31,8 @@ ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& paramete
31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
32 // Create a SharedMemory that directly points to this heap block. 32 // Create a SharedMemory that directly points to this heap block.
33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
34 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 34 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
35 MemoryPermission::ReadWrite, "Mint Memory"); 35 "Mint Memory");
36 36
37 // Send the response message with the newly created SharedMemory 37 // Send the response message with the newly created SharedMemory
38 Service::APT::MessageParameter result; 38 Service::APT::MessageParameter result;
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index fdf8807b0..0bc471a3a 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -41,8 +41,8 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
41 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 41 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
42 // Create a SharedMemory that directly points to this heap block. 42 // Create a SharedMemory that directly points to this heap block.
43 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 43 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
44 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 44 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
45 MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); 45 "SoftwareKeyboard Memory");
46 46
47 // Send the response message with the newly created SharedMemory 47 // Send the response message with the newly created SharedMemory
48 Service::APT::MessageParameter result; 48 Service::APT::MessageParameter result;
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index bc81c06b4..31fda6db3 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -24,19 +24,37 @@ static inline void FuncReturn(u64 res) {
24 Core::CPU().SetReg(0, res); 24 Core::CPU().SetReg(0, res);
25} 25}
26 26
27/**
28 * HLE a function return (64-bit) from the current ARM11 userland process
29 * @param res Result to return (64-bit)
30 * @todo Verify that this function is correct
31 */
32static inline void FuncReturn64(u64 res) {
33 Core::CPU().SetReg(0, (u32)(res & 0xFFFFFFFF));
34 Core::CPU().SetReg(1, (u32)((res >> 32) & 0xFFFFFFFF));
35}
36
37//////////////////////////////////////////////////////////////////////////////////////////////////// 27////////////////////////////////////////////////////////////////////////////////////////////////////
38// Function wrappers that return type ResultCode 28// Function wrappers that return type ResultCode
39 29
30template <ResultCode func(u64)>
31void Wrap() {
32 FuncReturn(func(PARAM(0)).raw);
33}
34
35template <ResultCode func(u32, u64, u32)>
36void Wrap() {
37 FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw);
38}
39
40template <ResultCode func(u64, u32)>
41void Wrap() {
42 FuncReturn(func(PARAM(0), PARAM(1)).raw);
43}
44
45template <ResultCode func(u64, u64, u64)>
46void Wrap() {
47 FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw);
48}
49
50template <ResultCode func(u64*, u64, u64, u64)>
51void Wrap() {
52 u64 param_1 = 0;
53 u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3)).raw;
54 Core::CPU().SetReg(1, param_1);
55 FuncReturn(retval);
56}
57
40template <ResultCode func(u32, u32, u32, u32)> 58template <ResultCode func(u32, u32, u32, u32)>
41void Wrap() { 59void Wrap() {
42 FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)).raw); 60 FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)).raw);
@@ -58,22 +76,21 @@ void Wrap() {
58 FuncReturn(retval); 76 FuncReturn(retval);
59} 77}
60 78
61template <ResultCode func(s32*, u32*, s32, bool, s64)> 79template <ResultCode func(s32*, VAddr, s32, bool, s64)>
62void Wrap() { 80void Wrap() {
63 s32 param_1 = 0; 81 s32 param_1 = 0;
64 s32 retval = func(&param_1, (Kernel::Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), 82 s32 retval =
65 (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))) 83 func(&param_1, PARAM(1), (s32)PARAM(2), (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0)))
66 .raw; 84 .raw;
67 85
68 Core::CPU().SetReg(1, (u32)param_1); 86 Core::CPU().SetReg(1, (u32)param_1);
69 FuncReturn(retval); 87 FuncReturn(retval);
70} 88}
71 89
72template <ResultCode func(s32*, u32*, s32, u32)> 90template <ResultCode func(s32*, VAddr, s32, u32)>
73void Wrap() { 91void Wrap() {
74 s32 param_1 = 0; 92 s32 param_1 = 0;
75 u32 retval = 93 u32 retval = func(&param_1, PARAM(1), (s32)PARAM(2), PARAM(3)).raw;
76 func(&param_1, (Kernel::Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), PARAM(3)).raw;
77 94
78 Core::CPU().SetReg(1, (u32)param_1); 95 Core::CPU().SetReg(1, (u32)param_1);
79 FuncReturn(retval); 96 FuncReturn(retval);
@@ -85,6 +102,14 @@ void Wrap() {
85 func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))).raw); 102 func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))).raw);
86} 103}
87 104
105template <ResultCode func(u32, u64*)>
106void Wrap() {
107 u64 param_1 = 0;
108 u32 retval = func(PARAM(0), &param_1).raw;
109 Core::CPU().SetReg(1, param_1);
110 FuncReturn(retval);
111}
112
88template <ResultCode func(u32*)> 113template <ResultCode func(u32*)>
89void Wrap() { 114void Wrap() {
90 u32 param_1 = 0; 115 u32 param_1 = 0;
@@ -100,16 +125,17 @@ void Wrap() {
100 FuncReturn(retval); 125 FuncReturn(retval);
101} 126}
102 127
103template <ResultCode func(MemoryInfo*, PageInfo*, u32)> 128template <ResultCode func(MemoryInfo*, PageInfo*, u64)>
104void Wrap() { 129void Wrap() {
105 MemoryInfo memory_info = {}; 130 MemoryInfo memory_info = {};
106 PageInfo page_info = {}; 131 PageInfo page_info = {};
107 u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; 132 u32 retval = func(&memory_info, &page_info, PARAM(2)).raw;
108 Core::CPU().SetReg(1, memory_info.base_address); 133
109 Core::CPU().SetReg(2, memory_info.size); 134 Memory::Write64(PARAM(0), memory_info.base_address);
110 Core::CPU().SetReg(3, memory_info.permission); 135 Memory::Write64(PARAM(0) + 8, memory_info.size);
111 Core::CPU().SetReg(4, memory_info.state); 136 Memory::Write64(PARAM(0) + 16, memory_info.permission);
112 Core::CPU().SetReg(5, page_info.flags); 137 Memory::Write64(PARAM(0) + 24, memory_info.state);
138
113 FuncReturn(retval); 139 FuncReturn(retval);
114} 140}
115 141
@@ -139,7 +165,7 @@ void Wrap() {
139 FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw); 165 FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw);
140} 166}
141 167
142template <ResultCode func(u32*, u32)> 168template <ResultCode func(u32*, u64)>
143void Wrap() { 169void Wrap() {
144 u32 param_1 = 0; 170 u32 param_1 = 0;
145 u32 retval = func(&param_1, PARAM(1)).raw; 171 u32 retval = func(&param_1, PARAM(1)).raw;
@@ -152,21 +178,6 @@ void Wrap() {
152 FuncReturn(func(PARAM(0)).raw); 178 FuncReturn(func(PARAM(0)).raw);
153} 179}
154 180
155template <ResultCode func(s64*, u32, u32*, u32)>
156void Wrap() {
157 FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1),
158 (u32*)Memory::GetPointer(PARAM(2)), (s32)PARAM(3))
159 .raw);
160}
161
162template <ResultCode func(u32*, const char*)>
163void Wrap() {
164 u32 param_1 = 0;
165 u32 retval = func(&param_1, (char*)Memory::GetPointer(PARAM(1))).raw;
166 Core::CPU().SetReg(1, param_1);
167 FuncReturn(retval);
168}
169
170template <ResultCode func(u32*, s32, s32)> 181template <ResultCode func(u32*, s32, s32)>
171void Wrap() { 182void Wrap() {
172 u32 param_1 = 0; 183 u32 param_1 = 0;
@@ -222,13 +233,11 @@ void Wrap() {
222 FuncReturn(func(PARAM(0), PARAM(1)).raw); 233 FuncReturn(func(PARAM(0), PARAM(1)).raw);
223} 234}
224 235
225template <ResultCode func(Kernel::Handle*, Kernel::Handle*, const char*, u32)> 236template <ResultCode func(Kernel::Handle*, Kernel::Handle*, VAddr, u32)>
226void Wrap() { 237void Wrap() {
227 Kernel::Handle param_1 = 0; 238 Kernel::Handle param_1 = 0;
228 Kernel::Handle param_2 = 0; 239 Kernel::Handle param_2 = 0;
229 u32 retval = func(&param_1, &param_2, 240 u32 retval = func(&param_1, &param_2, PARAM(2), PARAM(3)).raw;
230 reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3))
231 .raw;
232 Core::CPU().SetReg(1, param_1); 241 Core::CPU().SetReg(1, param_1);
233 Core::CPU().SetReg(2, param_2); 242 Core::CPU().SetReg(2, param_2);
234 FuncReturn(retval); 243 FuncReturn(retval);
@@ -244,6 +253,11 @@ void Wrap() {
244 FuncReturn(retval); 253 FuncReturn(retval);
245} 254}
246 255
256template <ResultCode func(u32, u32, u32)>
257void Wrap() {
258 FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw);
259}
260
247//////////////////////////////////////////////////////////////////////////////////////////////////// 261////////////////////////////////////////////////////////////////////////////////////////////////////
248// Function wrappers that return type u32 262// Function wrappers that return type u32
249 263
@@ -268,14 +282,14 @@ void Wrap() {
268 func(((s64)PARAM(1) << 32) | PARAM(0)); 282 func(((s64)PARAM(1) << 32) | PARAM(0));
269} 283}
270 284
271template <void func(const char*, int len)> 285template <void func(VAddr, int len)>
272void Wrap() { 286void Wrap() {
273 func((char*)Memory::GetPointer(PARAM(0)), PARAM(1)); 287 func(PARAM(0), PARAM(1));
274} 288}
275 289
276template <void func(u8)> 290template <void func(u64, u64, u64)>
277void Wrap() { 291void Wrap() {
278 func((u8)PARAM(0)); 292 func(PARAM(0), PARAM(1), PARAM(2));
279} 293}
280 294
281#undef PARAM 295#undef PARAM
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index f7f96125a..87ed85df6 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -122,11 +122,11 @@ union StaticBufferDescInfo {
122 BitField<14, 18, u32> size; 122 BitField<14, 18, u32> size;
123}; 123};
124 124
125inline u32 StaticBufferDesc(u32 size, u8 buffer_id) { 125inline u32 StaticBufferDesc(size_t size, u8 buffer_id) {
126 StaticBufferDescInfo info{}; 126 StaticBufferDescInfo info{};
127 info.descriptor_type.Assign(StaticBuffer); 127 info.descriptor_type.Assign(StaticBuffer);
128 info.buffer_id.Assign(buffer_id); 128 info.buffer_id.Assign(buffer_id);
129 info.size.Assign(size); 129 info.size.Assign(static_cast<u32>(size));
130 return info.raw; 130 return info.raw;
131} 131}
132 132
@@ -160,11 +160,11 @@ union MappedBufferDescInfo {
160 BitField<4, 28, u32> size; 160 BitField<4, 28, u32> size;
161}; 161};
162 162
163inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { 163inline u32 MappedBufferDesc(size_t size, MappedBufferPermissions perms) {
164 MappedBufferDescInfo info{}; 164 MappedBufferDescInfo info{};
165 info.flags.Assign(MappedBuffer); 165 info.flags.Assign(MappedBuffer);
166 info.perms.Assign(perms); 166 info.perms.Assign(perms);
167 info.size.Assign(size); 167 info.size.Assign(static_cast<u32>(size));
168 return info.raw; 168 return info.raw;
169} 169}
170 170
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index f0d89cffe..7cb95cbac 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -117,9 +117,9 @@ public:
117 117
118 void PushCurrentPIDHandle(); 118 void PushCurrentPIDHandle();
119 119
120 void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id); 120 void PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id);
121 121
122 void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms); 122 void PushMappedBuffer(VAddr buffer_vaddr, size_t size, MappedBufferPermissions perms);
123}; 123};
124 124
125/// Push /// 125/// Push ///
@@ -190,12 +190,12 @@ inline void RequestBuilder::PushCurrentPIDHandle() {
190 Push(u32(0)); 190 Push(u32(0));
191} 191}
192 192
193inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) { 193inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id) {
194 Push(StaticBufferDesc(size, buffer_id)); 194 Push(StaticBufferDesc(size, buffer_id));
195 Push(buffer_vaddr); 195 Push(buffer_vaddr);
196} 196}
197 197
198inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size, 198inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, size_t size,
199 MappedBufferPermissions perms) { 199 MappedBufferPermissions perms) {
200 Push(MappedBufferDesc(size, perms)); 200 Push(MappedBufferDesc(size, perms));
201 Push(buffer_vaddr); 201 Push(buffer_vaddr);
@@ -227,8 +227,8 @@ public:
227 bool validateHeader = true) { 227 bool validateHeader = true) {
228 if (validateHeader) 228 if (validateHeader)
229 ValidateHeader(); 229 ValidateHeader();
230 Header builderHeader{ 230 Header builderHeader{MakeHeader(static_cast<u16>(header.command_id), normal_params_size,
231 MakeHeader(header.command_id, normal_params_size, translate_params_size)}; 231 translate_params_size)};
232 if (context != nullptr) 232 if (context != nullptr)
233 return {*context, builderHeader}; 233 return {*context, builderHeader};
234 else 234 else
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 5ebe2eca4..6020e9764 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -37,7 +37,7 @@ SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const
37 37
38u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { 38u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) {
39 request_handles.push_back(std::move(object)); 39 request_handles.push_back(std::move(object));
40 return request_handles.size() - 1; 40 return static_cast<u32>(request_handles.size() - 1);
41} 41}
42 42
43void HLERequestContext::ClearIncomingObjects() { 43void HLERequestContext::ClearIncomingObjects() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 9cf288b08..73fab3981 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -8,6 +8,7 @@
8#include <string> 8#include <string>
9#include <utility> 9#include <utility>
10#include <boost/smart_ptr/intrusive_ptr.hpp> 10#include <boost/smart_ptr/intrusive_ptr.hpp>
11#include "common/assert.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
13namespace Kernel { 14namespace Kernel {
@@ -84,6 +85,8 @@ public:
84 case HandleType::ClientSession: 85 case HandleType::ClientSession:
85 return false; 86 return false;
86 } 87 }
88
89 UNREACHABLE();
87 } 90 }
88 91
89public: 92public:
@@ -129,4 +132,4 @@ void Init(u32 system_mode);
129/// Shutdown the kernel 132/// Shutdown the kernel
130void Shutdown(); 133void Shutdown();
131 134
132} // namespace 135} // namespace Kernel
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 496d07cb5..7f27e9655 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -8,7 +8,6 @@
8#include <memory> 8#include <memory>
9#include <utility> 9#include <utility>
10#include <vector> 10#include <vector>
11#include "audio_core/audio_core.h"
12#include "common/assert.h" 11#include "common/assert.h"
13#include "common/common_types.h" 12#include "common/common_types.h"
14#include "common/logging/log.h" 13#include "common/logging/log.h"
@@ -24,7 +23,7 @@
24 23
25namespace Kernel { 24namespace Kernel {
26 25
27static MemoryRegionInfo memory_regions[3]; 26MemoryRegionInfo memory_regions[3];
28 27
29/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system 28/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system
30/// memory configuration type. 29/// memory configuration type.
@@ -96,9 +95,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) {
96 } 95 }
97} 96}
98 97
99std::array<u8, Memory::VRAM_SIZE> vram;
100std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
101
102void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { 98void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) {
103 using namespace Memory; 99 using namespace Memory;
104 100
@@ -143,30 +139,14 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin
143 return; 139 return;
144 } 140 }
145 141
146 // TODO(yuriks): Use GetPhysicalPointer when that becomes independent of the virtual 142 u8* target_pointer = Memory::GetPhysicalPointer(area->paddr_base + offset_into_region);
147 // mappings.
148 u8* target_pointer = nullptr;
149 switch (area->paddr_base) {
150 case VRAM_PADDR:
151 target_pointer = vram.data();
152 break;
153 case DSP_RAM_PADDR:
154 target_pointer = AudioCore::GetDspMemory().data();
155 break;
156 case N3DS_EXTRA_RAM_PADDR:
157 target_pointer = n3ds_extra_ram.data();
158 break;
159 default:
160 UNREACHABLE();
161 }
162 143
163 // TODO(yuriks): This flag seems to have some other effect, but it's unknown what 144 // TODO(yuriks): This flag seems to have some other effect, but it's unknown what
164 MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO; 145 MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO;
165 146
166 auto vma = address_space 147 auto vma =
167 .MapBackingMemory(mapping.address, target_pointer + offset_into_region, 148 address_space.MapBackingMemory(mapping.address, target_pointer, mapping.size, memory_state)
168 mapping.size, memory_state) 149 .Unwrap();
169 .Unwrap();
170 address_space.Reprotect(vma, 150 address_space.Reprotect(vma,
171 mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); 151 mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite);
172} 152}
diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h
index 08c1a9989..da6bb3563 100644
--- a/src/core/hle/kernel/memory.h
+++ b/src/core/hle/kernel/memory.h
@@ -26,4 +26,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region);
26 26
27void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); 27void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
28void MapSharedPages(VMManager& address_space); 28void MapSharedPages(VMManager& address_space);
29
30extern MemoryRegionInfo memory_regions[3];
29} // namespace Kernel 31} // namespace Kernel
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index cef961289..30dade552 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -25,10 +25,11 @@ void ReleaseThreadMutexes(Thread* thread) {
25Mutex::Mutex() {} 25Mutex::Mutex() {}
26Mutex::~Mutex() {} 26Mutex::~Mutex() {}
27 27
28SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { 28SharedPtr<Mutex> Mutex::Create(bool initial_locked, VAddr addr, std::string name) {
29 SharedPtr<Mutex> mutex(new Mutex); 29 SharedPtr<Mutex> mutex(new Mutex);
30 30
31 mutex->lock_count = 0; 31 mutex->lock_count = 0;
32 mutex->addr = addr;
32 mutex->name = std::move(name); 33 mutex->name = std::move(name);
33 mutex->holding_thread = nullptr; 34 mutex->holding_thread = nullptr;
34 35
@@ -90,7 +91,7 @@ void Mutex::UpdatePriority() {
90 if (!holding_thread) 91 if (!holding_thread)
91 return; 92 return;
92 93
93 s32 best_priority = THREADPRIO_LOWEST; 94 u32 best_priority = THREADPRIO_LOWEST;
94 for (auto& waiter : GetWaitingThreads()) { 95 for (auto& waiter : GetWaitingThreads()) {
95 if (waiter->current_priority < best_priority) 96 if (waiter->current_priority < best_priority)
96 best_priority = waiter->current_priority; 97 best_priority = waiter->current_priority;
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index bacacd690..503d3ee75 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -21,7 +21,7 @@ public:
21 * @param name Optional name of mutex 21 * @param name Optional name of mutex
22 * @return Pointer to new Mutex object 22 * @return Pointer to new Mutex object
23 */ 23 */
24 static SharedPtr<Mutex> Create(bool initial_locked, std::string name = "Unknown"); 24 static SharedPtr<Mutex> Create(bool initial_locked, VAddr addr, std::string name = "Unknown");
25 25
26 std::string GetTypeName() const override { 26 std::string GetTypeName() const override {
27 return "Mutex"; 27 return "Mutex";
@@ -39,6 +39,7 @@ public:
39 u32 priority; ///< The priority of the mutex, used for priority inheritance. 39 u32 priority; ///< The priority of the mutex, used for priority inheritance.
40 std::string name; ///< Name of mutex (optional) 40 std::string name; ///< Name of mutex (optional)
41 SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex 41 SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex
42 VAddr addr;
42 43
43 /** 44 /**
44 * Elevate the mutex priority to the best priority 45 * Elevate the mutex priority to the best priority
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 84ebdbc58..9e145866f 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -129,7 +129,8 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
129 } 129 }
130 130
131 vm_manager.LogLayout(Log::Level::Debug); 131 vm_manager.LogLayout(Log::Level::Debug);
132 Kernel::SetupMainThread(entry_point, main_thread_priority); 132
133 Kernel::SetupMainThread(entry_point, main_thread_priority, this);
133} 134}
134 135
135void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { 136void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index a8f10a3ee..517dc47a8 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -61,7 +61,7 @@ s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const {
61 } 61 }
62} 62}
63 63
64s32 ResourceLimit::GetMaxResourceValue(u32 resource) const { 64u32 ResourceLimit::GetMaxResourceValue(u32 resource) const {
65 switch (resource) { 65 switch (resource) {
66 case PRIORITY: 66 case PRIORITY:
67 return max_priority; 67 return max_priority;
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index 6cdfbcf8d..42874eb8d 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -67,7 +67,7 @@ public:
67 * @param resource Requested resource type 67 * @param resource Requested resource type
68 * @returns The max value of the resource type 68 * @returns The max value of the resource type
69 */ 69 */
70 s32 GetMaxResourceValue(u32 resource) const; 70 u32 GetMaxResourceValue(u32 resource) const;
71 71
72 /// Name of resource limit object. 72 /// Name of resource limit object.
73 std::string name; 73 std::string name;
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
index fcf586728..2605b2595 100644
--- a/src/core/hle/kernel/semaphore.cpp
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -13,7 +13,7 @@ namespace Kernel {
13Semaphore::Semaphore() {} 13Semaphore::Semaphore() {}
14Semaphore::~Semaphore() {} 14Semaphore::~Semaphore() {}
15 15
16ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, 16ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, VAddr address,
17 std::string name) { 17 std::string name) {
18 18
19 if (initial_count > max_count) 19 if (initial_count > max_count)
@@ -25,6 +25,7 @@ ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_cou
25 // and the rest is reserved for the caller thread 25 // and the rest is reserved for the caller thread
26 semaphore->max_count = max_count; 26 semaphore->max_count = max_count;
27 semaphore->available_count = initial_count; 27 semaphore->available_count = initial_count;
28 semaphore->address = address;
28 semaphore->name = std::move(name); 29 semaphore->name = std::move(name);
29 30
30 return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore)); 31 return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore));
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
index 7b0cacf2e..77c491a24 100644
--- a/src/core/hle/kernel/semaphore.h
+++ b/src/core/hle/kernel/semaphore.h
@@ -22,7 +22,7 @@ public:
22 * @param name Optional name of semaphore 22 * @param name Optional name of semaphore
23 * @return The created semaphore 23 * @return The created semaphore
24 */ 24 */
25 static ResultVal<SharedPtr<Semaphore>> Create(s32 initial_count, s32 max_count, 25 static ResultVal<SharedPtr<Semaphore>> Create(s32 initial_count, s32 max_count, VAddr address,
26 std::string name = "Unknown"); 26 std::string name = "Unknown");
27 27
28 std::string GetTypeName() const override { 28 std::string GetTypeName() const override {
@@ -39,6 +39,7 @@ public:
39 39
40 s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have 40 s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
41 s32 available_count; ///< Number of free slots left in the semaphore 41 s32 available_count; ///< Number of free slots left in the semaphore
42 VAddr address;
42 std::string name; ///< Name of semaphore (optional) 43 std::string name; ///< Name of semaphore (optional)
43 44
44 bool ShouldWait(Thread* thread) const override; 45 bool ShouldWait(Thread* thread) const override;
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index a7b66142f..d45daca35 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -42,7 +42,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
42 memory_region->used += size; 42 memory_region->used += size;
43 43
44 shared_memory->linear_heap_phys_address = 44 shared_memory->linear_heap_phys_address =
45 Memory::FCRAM_PADDR + memory_region->base + shared_memory->backing_block_offset; 45 Memory::FCRAM_PADDR + memory_region->base +
46 static_cast<PAddr>(shared_memory->backing_block_offset);
46 47
47 // Increase the amount of used linear heap memory for the owner process. 48 // Increase the amount of used linear heap memory for the owner process.
48 if (shared_memory->owner_process != nullptr) { 49 if (shared_memory->owner_process != nullptr) {
@@ -54,22 +55,19 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
54 Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); 55 Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
55 } 56 }
56 } else { 57 } else {
57 // TODO(Subv): What happens if an application tries to create multiple memory blocks
58 // pointing to the same address?
59 auto& vm_manager = shared_memory->owner_process->vm_manager; 58 auto& vm_manager = shared_memory->owner_process->vm_manager;
60 // The memory is already available and mapped in the owner process. 59 // The memory is already available and mapped in the owner process.
61 auto vma = vm_manager.FindVMA(address)->second; 60 auto vma = vm_manager.FindVMA(address);
62 // Copy it over to our own storage 61 ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
63 shared_memory->backing_block = std::make_shared<std::vector<u8>>( 62 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
64 vma.backing_block->data() + vma.offset, vma.backing_block->data() + vma.offset + size); 63
65 shared_memory->backing_block_offset = 0; 64 // The returned VMA might be a bigger one encompassing the desired address.
66 // Unmap the existing pages 65 auto vma_offset = address - vma->first;
67 vm_manager.UnmapRange(address, size); 66 ASSERT_MSG(vma_offset + size <= vma->second.size,
68 // Map our own block into the address space 67 "Shared memory exceeds bounds of mapped block");
69 vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size, 68
70 MemoryState::Shared); 69 shared_memory->backing_block = vma->second.backing_block;
71 // Reprotect the block with the new permissions 70 shared_memory->backing_block_offset = vma->second.offset + vma_offset;
72 vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions));
73 } 71 }
74 72
75 shared_memory->base_address = address; 73 shared_memory->base_address = address;
@@ -183,4 +181,4 @@ u8* SharedMemory::GetPointer(u32 offset) {
183 return backing_block->data() + backing_block_offset + offset; 181 return backing_block->data() + backing_block_offset + offset;
184} 182}
185 183
186} // namespace 184} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 94b335ed1..93a6f2182 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -114,7 +114,7 @@ public:
114 /// Backing memory for this shared memory block. 114 /// Backing memory for this shared memory block.
115 std::shared_ptr<std::vector<u8>> backing_block; 115 std::shared_ptr<std::vector<u8>> backing_block;
116 /// Offset into the backing block for this shared memory. 116 /// Offset into the backing block for this shared memory.
117 u32 backing_block_offset; 117 size_t backing_block_offset;
118 /// Size of the memory block. Page-aligned. 118 /// Size of the memory block. Page-aligned.
119 u32 size; 119 u32 size;
120 /// Permission restrictions applied to the process which created the block. 120 /// Permission restrictions applied to the process which created the block.
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c01d08ebb..75df49ac2 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -111,7 +111,7 @@ void Thread::Stop() {
111 111
112Thread* ArbitrateHighestPriorityThread(u32 address) { 112Thread* ArbitrateHighestPriorityThread(u32 address) {
113 Thread* highest_priority_thread = nullptr; 113 Thread* highest_priority_thread = nullptr;
114 s32 priority = THREADPRIO_LOWEST; 114 u32 priority = THREADPRIO_LOWEST;
115 115
116 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 116 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
117 for (auto& thread : thread_list) { 117 for (auto& thread : thread_list) {
@@ -171,15 +171,24 @@ static void SwitchContext(Thread* new_thread) {
171 // Cancel any outstanding wakeup events for this thread 171 // Cancel any outstanding wakeup events for this thread
172 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); 172 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
173 173
174 auto previous_process = Kernel::g_current_process;
175
174 current_thread = new_thread; 176 current_thread = new_thread;
175 177
176 ready_queue.remove(new_thread->current_priority, new_thread); 178 ready_queue.remove(new_thread->current_priority, new_thread);
177 new_thread->status = THREADSTATUS_RUNNING; 179 new_thread->status = THREADSTATUS_RUNNING;
178 180
181 if (previous_process != current_thread->owner_process) {
182 Kernel::g_current_process = current_thread->owner_process;
183 SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
184 }
185
179 Core::CPU().LoadContext(new_thread->context); 186 Core::CPU().LoadContext(new_thread->context);
180 Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); 187 Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress());
181 } else { 188 } else {
182 current_thread = nullptr; 189 current_thread = nullptr;
190 // Note: We do not reset the current process and current page table when idling because
191 // technically we haven't changed processes, our threads are just paused.
183 } 192 }
184} 193}
185 194
@@ -238,12 +247,15 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
238 247
239 if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || 248 if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
240 thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { 249 thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) {
241 thread->wait_set_output = false; 250
251 // Invoke the wakeup callback before clearing the wait objects
252 if (thread->wakeup_callback)
253 thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr);
254
242 // Remove the thread from each of its waiting objects' waitlists 255 // Remove the thread from each of its waiting objects' waitlists
243 for (auto& object : thread->wait_objects) 256 for (auto& object : thread->wait_objects)
244 object->RemoveWaitingThread(thread.get()); 257 object->RemoveWaitingThread(thread.get());
245 thread->wait_objects.clear(); 258 thread->wait_objects.clear();
246 thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
247 } 259 }
248 260
249 thread->ResumeFromWait(); 261 thread->ResumeFromWait();
@@ -269,6 +281,9 @@ void Thread::ResumeFromWait() {
269 break; 281 break;
270 282
271 case THREADSTATUS_READY: 283 case THREADSTATUS_READY:
284 // The thread's wakeup callback must have already been cleared when the thread was first
285 // awoken.
286 ASSERT(wakeup_callback == nullptr);
272 // If the thread is waiting on multiple wait objects, it might be awoken more than once 287 // If the thread is waiting on multiple wait objects, it might be awoken more than once
273 // before actually resuming. We can ignore subsequent wakeups if the thread status has 288 // before actually resuming. We can ignore subsequent wakeups if the thread status has
274 // already been set to THREADSTATUS_READY. 289 // already been set to THREADSTATUS_READY.
@@ -284,6 +299,8 @@ void Thread::ResumeFromWait() {
284 return; 299 return;
285 } 300 }
286 301
302 wakeup_callback = nullptr;
303
287 ready_queue.push_back(current_priority, this); 304 ready_queue.push_back(current_priority, this);
288 status = THREADSTATUS_READY; 305 status = THREADSTATUS_READY;
289 Core::System::GetInstance().PrepareReschedule(); 306 Core::System::GetInstance().PrepareReschedule();
@@ -302,7 +319,7 @@ static void DebugThreadQueue() {
302 } 319 }
303 320
304 for (auto& t : thread_list) { 321 for (auto& t : thread_list) {
305 s32 priority = ready_queue.contains(t.get()); 322 u32 priority = ready_queue.contains(t.get());
306 if (priority != -1) { 323 if (priority != -1) {
307 LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); 324 LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId());
308 } 325 }
@@ -352,7 +369,8 @@ static void ResetThreadContext(ARM_Interface::ThreadContext& context, VAddr stac
352} 369}
353 370
354ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority, 371ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority,
355 u32 arg, s32 processor_id, VAddr stack_top) { 372 u32 arg, s32 processor_id, VAddr stack_top,
373 SharedPtr<Process> owner_process) {
356 // Check if priority is in ranged. Lowest priority -> highest priority id. 374 // Check if priority is in ranged. Lowest priority -> highest priority id.
357 if (priority > THREADPRIO_LOWEST) { 375 if (priority > THREADPRIO_LOWEST) {
358 LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority); 376 LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority);
@@ -366,7 +384,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
366 384
367 // TODO(yuriks): Other checks, returning 0xD9001BEA 385 // TODO(yuriks): Other checks, returning 0xD9001BEA
368 386
369 if (!Memory::IsValidVirtualAddress(entry_point)) { 387 if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
370 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); 388 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
371 // TODO: Verify error 389 // TODO: Verify error
372 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, 390 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
@@ -385,15 +403,14 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
385 thread->nominal_priority = thread->current_priority = priority; 403 thread->nominal_priority = thread->current_priority = priority;
386 thread->last_running_ticks = CoreTiming::GetTicks(); 404 thread->last_running_ticks = CoreTiming::GetTicks();
387 thread->processor_id = processor_id; 405 thread->processor_id = processor_id;
388 thread->wait_set_output = false;
389 thread->wait_objects.clear(); 406 thread->wait_objects.clear();
390 thread->wait_address = 0; 407 thread->wait_address = 0;
391 thread->name = std::move(name); 408 thread->name = std::move(name);
392 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); 409 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
393 thread->owner_process = g_current_process; 410 thread->owner_process = owner_process;
394 411
395 // Find the next available TLS index, and mark it as used 412 // Find the next available TLS index, and mark it as used
396 auto& tls_slots = Kernel::g_current_process->tls_slots; 413 auto& tls_slots = owner_process->tls_slots;
397 bool needs_allocation = true; 414 bool needs_allocation = true;
398 u32 available_page; // Which allocated page has free space 415 u32 available_page; // Which allocated page has free space
399 u32 available_slot; // Which slot within the page is free 416 u32 available_slot; // Which slot within the page is free
@@ -412,18 +429,18 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
412 return ERR_OUT_OF_MEMORY; 429 return ERR_OUT_OF_MEMORY;
413 } 430 }
414 431
415 u32 offset = linheap_memory->size(); 432 size_t offset = linheap_memory->size();
416 433
417 // Allocate some memory from the end of the linear heap for this region. 434 // Allocate some memory from the end of the linear heap for this region.
418 linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); 435 linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0);
419 memory_region->used += Memory::PAGE_SIZE; 436 memory_region->used += Memory::PAGE_SIZE;
420 Kernel::g_current_process->linear_heap_used += Memory::PAGE_SIZE; 437 owner_process->linear_heap_used += Memory::PAGE_SIZE;
421 438
422 tls_slots.emplace_back(0); // The page is completely available at the start 439 tls_slots.emplace_back(0); // The page is completely available at the start
423 available_page = tls_slots.size() - 1; 440 available_page = static_cast<u32>(tls_slots.size() - 1);
424 available_slot = 0; // Use the first slot in the new page 441 available_slot = 0; // Use the first slot in the new page
425 442
426 auto& vm_manager = Kernel::g_current_process->vm_manager; 443 auto& vm_manager = owner_process->vm_manager;
427 vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); 444 vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
428 445
429 // Map the page to the current process' address space. 446 // Map the page to the current process' address space.
@@ -447,7 +464,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
447 return MakeResult<SharedPtr<Thread>>(std::move(thread)); 464 return MakeResult<SharedPtr<Thread>>(std::move(thread));
448} 465}
449 466
450void Thread::SetPriority(s32 priority) { 467void Thread::SetPriority(u32 priority) {
451 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, 468 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
452 "Invalid priority value."); 469 "Invalid priority value.");
453 // If thread was ready, adjust queues 470 // If thread was ready, adjust queues
@@ -460,7 +477,7 @@ void Thread::SetPriority(s32 priority) {
460} 477}
461 478
462void Thread::UpdatePriority() { 479void Thread::UpdatePriority() {
463 s32 best_priority = nominal_priority; 480 u32 best_priority = nominal_priority;
464 for (auto& mutex : held_mutexes) { 481 for (auto& mutex : held_mutexes) {
465 if (mutex->priority < best_priority) 482 if (mutex->priority < best_priority)
466 best_priority = mutex->priority; 483 best_priority = mutex->priority;
@@ -468,7 +485,7 @@ void Thread::UpdatePriority() {
468 BoostPriority(best_priority); 485 BoostPriority(best_priority);
469} 486}
470 487
471void Thread::BoostPriority(s32 priority) { 488void Thread::BoostPriority(u32 priority) {
472 // If thread was ready, adjust queues 489 // If thread was ready, adjust queues
473 if (status == THREADSTATUS_READY) 490 if (status == THREADSTATUS_READY)
474 ready_queue.move(this, current_priority, priority); 491 ready_queue.move(this, current_priority, priority);
@@ -477,21 +494,20 @@ void Thread::BoostPriority(s32 priority) {
477 current_priority = priority; 494 current_priority = priority;
478} 495}
479 496
480SharedPtr<Thread> SetupMainThread(VAddr entry_point, s32 priority) { 497SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process) {
481 DEBUG_ASSERT(!GetCurrentThread()); 498 // Setup page table so we can write to memory
499 SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
482 500
483 // Initialize new "main" thread 501 // Initialize new "main" thread
484 auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, 502 auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
485 Memory::HEAP_VADDR_END); 503 Memory::HEAP_VADDR_END, owner_process);
486 504
487 SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); 505 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
488 506
489 thread->context.fpscr = 507 thread->context.fpscr =
490 FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 508 FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010
491 509
492 // Run new "main" thread 510 // Note: The newly created thread will be run when the scheduler fires.
493 SwitchContext(thread.get());
494
495 return thread; 511 return thread;
496} 512}
497 513
@@ -525,7 +541,13 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
525s32 Thread::GetWaitObjectIndex(WaitObject* object) const { 541s32 Thread::GetWaitObjectIndex(WaitObject* object) const {
526 ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); 542 ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything");
527 auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); 543 auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
528 return std::distance(match, wait_objects.rend()) - 1; 544 return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1);
545}
546
547VAddr Thread::GetCommandBufferAddress() const {
548 // Offset from the start of TLS at which the IPC command buffer begins.
549 static constexpr int CommandHeaderOffset = 0x80;
550 return GetTLSAddress() + CommandHeaderOffset;
529} 551}
530 552
531//////////////////////////////////////////////////////////////////////////////////////////////////// 553////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 2cadb91db..fafcab156 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -15,7 +15,7 @@
15#include "core/hle/kernel/wait_object.h" 15#include "core/hle/kernel/wait_object.h"
16#include "core/hle/result.h" 16#include "core/hle/result.h"
17 17
18enum ThreadPriority : s32 { 18enum ThreadPriority : u32 {
19 THREADPRIO_HIGHEST = 0, ///< Highest thread priority 19 THREADPRIO_HIGHEST = 0, ///< Highest thread priority
20 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps 20 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
21 THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps 21 THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps
@@ -41,6 +41,11 @@ enum ThreadStatus {
41 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated 41 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
42}; 42};
43 43
44enum class ThreadWakeupReason {
45 Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal.
46 Timeout // The thread was woken up due to a wait timeout.
47};
48
44namespace Kernel { 49namespace Kernel {
45 50
46class Mutex; 51class Mutex;
@@ -56,10 +61,12 @@ public:
56 * @param arg User data to pass to the thread 61 * @param arg User data to pass to the thread
57 * @param processor_id The ID(s) of the processors on which the thread is desired to be run 62 * @param processor_id The ID(s) of the processors on which the thread is desired to be run
58 * @param stack_top The address of the thread's stack top 63 * @param stack_top The address of the thread's stack top
64 * @param owner_process The parent process for the thread
59 * @return A shared pointer to the newly created thread 65 * @return A shared pointer to the newly created thread
60 */ 66 */
61 static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority, 67 static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority,
62 u32 arg, s32 processor_id, VAddr stack_top); 68 u32 arg, s32 processor_id, VAddr stack_top,
69 SharedPtr<Process> owner_process);
63 70
64 std::string GetName() const override { 71 std::string GetName() const override {
65 return name; 72 return name;
@@ -80,7 +87,7 @@ public:
80 * Gets the thread's current priority 87 * Gets the thread's current priority
81 * @return The current thread's priority 88 * @return The current thread's priority
82 */ 89 */
83 s32 GetPriority() const { 90 u32 GetPriority() const {
84 return current_priority; 91 return current_priority;
85 } 92 }
86 93
@@ -88,7 +95,7 @@ public:
88 * Sets the thread's current priority 95 * Sets the thread's current priority
89 * @param priority The new priority 96 * @param priority The new priority
90 */ 97 */
91 void SetPriority(s32 priority); 98 void SetPriority(u32 priority);
92 99
93 /** 100 /**
94 * Boost's a thread's priority to the best priority among the thread's held mutexes. 101 * Boost's a thread's priority to the best priority among the thread's held mutexes.
@@ -100,7 +107,7 @@ public:
100 * Temporarily boosts the thread's priority until the next time it is scheduled 107 * Temporarily boosts the thread's priority until the next time it is scheduled
101 * @param priority The new priority 108 * @param priority The new priority
102 */ 109 */
103 void BoostPriority(s32 priority); 110 void BoostPriority(u32 priority);
104 111
105 /** 112 /**
106 * Gets the thread's thread ID 113 * Gets the thread's thread ID
@@ -116,9 +123,9 @@ public:
116 void ResumeFromWait(); 123 void ResumeFromWait();
117 124
118 /** 125 /**
119 * Schedules an event to wake up the specified thread after the specified delay 126 * Schedules an event to wake up the specified thread after the specified delay
120 * @param nanoseconds The time this thread will be allowed to sleep for 127 * @param nanoseconds The time this thread will be allowed to sleep for
121 */ 128 */
122 void WakeAfterDelay(s64 nanoseconds); 129 void WakeAfterDelay(s64 nanoseconds);
123 130
124 /** 131 /**
@@ -157,6 +164,12 @@ public:
157 return tls_address; 164 return tls_address;
158 } 165 }
159 166
167 /*
168 * Returns the address of the current thread's command buffer, located in the TLS.
169 * @returns VAddr of the thread's command buffer.
170 */
171 VAddr GetCommandBufferAddress() const;
172
160 /** 173 /**
161 * Returns whether this thread is waiting for all the objects in 174 * Returns whether this thread is waiting for all the objects in
162 * its wait list to become ready, as a result of a WaitSynchronizationN call 175 * its wait list to become ready, as a result of a WaitSynchronizationN call
@@ -174,8 +187,8 @@ public:
174 VAddr entry_point; 187 VAddr entry_point;
175 VAddr stack_top; 188 VAddr stack_top;
176 189
177 s32 nominal_priority; ///< Nominal thread priority, as set by the emulated application 190 u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application
178 s32 current_priority; ///< Current thread priority, can be temporarily changed 191 u32 current_priority; ///< Current thread priority, can be temporarily changed
179 192
180 u64 last_running_ticks; ///< CPU tick when thread was last running 193 u64 last_running_ticks; ///< CPU tick when thread was last running
181 194
@@ -197,14 +210,18 @@ public:
197 210
198 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address 211 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address
199 212
200 /// True if the WaitSynchronizationN output parameter should be set on thread wakeup.
201 bool wait_set_output;
202
203 std::string name; 213 std::string name;
204 214
205 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. 215 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
206 Handle callback_handle; 216 Handle callback_handle;
207 217
218 using WakeupCallback = void(ThreadWakeupReason reason, SharedPtr<Thread> thread,
219 SharedPtr<WaitObject> object);
220 // Callback that will be invoked when the thread is resumed from a waiting state. If the thread
221 // was waiting via WaitSynchronizationN then the object will be the last object that became
222 // available. In case of a timeout, the object will be nullptr.
223 std::function<WakeupCallback> wakeup_callback;
224
208private: 225private:
209 Thread(); 226 Thread();
210 ~Thread() override; 227 ~Thread() override;
@@ -214,9 +231,10 @@ private:
214 * Sets up the primary application thread 231 * Sets up the primary application thread
215 * @param entry_point The address at which the thread should start execution 232 * @param entry_point The address at which the thread should start execution
216 * @param priority The priority to give the main thread 233 * @param priority The priority to give the main thread
234 * @param owner_process The parent process for the main thread
217 * @return A shared pointer to the main thread 235 * @return A shared pointer to the main thread
218 */ 236 */
219SharedPtr<Thread> SetupMainThread(VAddr entry_point, s32 priority); 237SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process);
220 238
221/** 239/**
222 * Returns whether there are any threads that are ready to run. 240 * Returns whether there are any threads that are ready to run.
@@ -276,4 +294,4 @@ void ThreadingShutdown();
276 */ 294 */
277const std::vector<SharedPtr<Thread>>& GetThreadList(); 295const std::vector<SharedPtr<Thread>>& GetThreadList();
278 296
279} // namespace 297} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index f70c32501..9762ef535 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -4,8 +4,10 @@
4 4
5#include <iterator> 5#include <iterator>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/arm/arm_interface.h"
7#include "core/hle/kernel/errors.h" 8#include "core/hle/kernel/errors.h"
8#include "core/hle/kernel/vm_manager.h" 9#include "core/hle/kernel/vm_manager.h"
10#include "core/core.h"
9#include "core/memory.h" 11#include "core/memory.h"
10#include "core/memory_setup.h" 12#include "core/memory_setup.h"
11#include "core/mmio.h" 13#include "core/mmio.h"
@@ -56,6 +58,10 @@ void VMManager::Reset() {
56 initial_vma.size = MAX_ADDRESS; 58 initial_vma.size = MAX_ADDRESS;
57 vma_map.emplace(initial_vma.base, initial_vma); 59 vma_map.emplace(initial_vma.base, initial_vma);
58 60
61 page_table.pointers.fill(nullptr);
62 page_table.attributes.fill(Memory::PageType::Unmapped);
63 page_table.cached_res_count.fill(0);
64
59 //UpdatePageTableForVMA(initial_vma); 65 //UpdatePageTableForVMA(initial_vma);
60} 66}
61 67
@@ -79,6 +85,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
79 VirtualMemoryArea& final_vma = vma_handle->second; 85 VirtualMemoryArea& final_vma = vma_handle->second;
80 ASSERT(final_vma.size == size); 86 ASSERT(final_vma.size == size);
81 87
88 Core::CPU().MapBackingMemory(target, size, block->data() + offset, VMAPermission::ReadWriteExecute);
89
82 final_vma.type = VMAType::AllocatedMemoryBlock; 90 final_vma.type = VMAType::AllocatedMemoryBlock;
83 final_vma.permissions = VMAPermission::ReadWrite; 91 final_vma.permissions = VMAPermission::ReadWrite;
84 final_vma.meminfo_state = state; 92 final_vma.meminfo_state = state;
@@ -98,6 +106,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
98 VirtualMemoryArea& final_vma = vma_handle->second; 106 VirtualMemoryArea& final_vma = vma_handle->second;
99 ASSERT(final_vma.size == size); 107 ASSERT(final_vma.size == size);
100 108
109 Core::CPU().MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
110
101 final_vma.type = VMAType::BackingMemory; 111 final_vma.type = VMAType::BackingMemory;
102 final_vma.permissions = VMAPermission::ReadWrite; 112 final_vma.permissions = VMAPermission::ReadWrite;
103 final_vma.meminfo_state = state; 113 final_vma.meminfo_state = state;
@@ -328,16 +338,17 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
328void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { 338void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
329 switch (vma.type) { 339 switch (vma.type) {
330 case VMAType::Free: 340 case VMAType::Free:
331 Memory::UnmapRegion(vma.base, vma.size); 341 Memory::UnmapRegion(page_table, vma.base, vma.size);
332 break; 342 break;
333 case VMAType::AllocatedMemoryBlock: 343 case VMAType::AllocatedMemoryBlock:
334 Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_block->data() + vma.offset); 344 Memory::MapMemoryRegion(page_table, vma.base, vma.size,
345 vma.backing_block->data() + vma.offset);
335 break; 346 break;
336 case VMAType::BackingMemory: 347 case VMAType::BackingMemory:
337 Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); 348 Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
338 break; 349 break;
339 case VMAType::MMIO: 350 case VMAType::MMIO:
340 Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); 351 Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
341 break; 352 break;
342 } 353 }
343} 354}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index aa2265ce6..cb5bb8243 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -9,6 +9,7 @@
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12#include "core/memory.h"
12#include "core/mmio.h" 13#include "core/mmio.h"
13 14
14namespace Kernel { 15namespace Kernel {
@@ -102,7 +103,6 @@ struct VirtualMemoryArea {
102 * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ 103 * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
103 */ 104 */
104class VMManager final { 105class VMManager final {
105 // TODO(yuriks): Make page tables switchable to support multiple VMManagers
106public: 106public:
107 /** 107 /**
108 * The maximum amount of address space managed by the kernel. Addresses above this are never 108 * The maximum amount of address space managed by the kernel. Addresses above this are never
@@ -184,6 +184,10 @@ public:
184 /// Dumps the address space layout to the log, for debugging 184 /// Dumps the address space layout to the log, for debugging
185 void LogLayout(Log::Level log_level) const; 185 void LogLayout(Log::Level log_level) const;
186 186
187 /// Each VMManager has its own page table, which is set as the main one when the owning process
188 /// is scheduled.
189 Memory::PageTable page_table;
190
187private: 191private:
188 using VMAIter = decltype(vma_map)::iterator; 192 using VMAIter = decltype(vma_map)::iterator;
189 193
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index f245eda6c..469554908 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -34,7 +34,7 @@ void WaitObject::RemoveWaitingThread(Thread* thread) {
34 34
35SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { 35SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
36 Thread* candidate = nullptr; 36 Thread* candidate = nullptr;
37 s32 candidate_priority = THREADPRIO_LOWEST + 1; 37 u32 candidate_priority = THREADPRIO_LOWEST + 1;
38 38
39 for (const auto& thread : waiting_threads) { 39 for (const auto& thread : waiting_threads) {
40 // The list of waiting threads must not contain threads that are not waiting to be awakened. 40 // The list of waiting threads must not contain threads that are not waiting to be awakened.
@@ -71,23 +71,20 @@ void WaitObject::WakeupAllWaitingThreads() {
71 while (auto thread = GetHighestPriorityReadyThread()) { 71 while (auto thread = GetHighestPriorityReadyThread()) {
72 if (!thread->IsSleepingOnWaitAll()) { 72 if (!thread->IsSleepingOnWaitAll()) {
73 Acquire(thread.get()); 73 Acquire(thread.get());
74 // Set the output index of the WaitSynchronizationN call to the index of this object.
75 if (thread->wait_set_output) {
76 thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this));
77 thread->wait_set_output = false;
78 }
79 } else { 74 } else {
80 for (auto& object : thread->wait_objects) { 75 for (auto& object : thread->wait_objects) {
81 object->Acquire(thread.get()); 76 object->Acquire(thread.get());
82 } 77 }
83 // Note: This case doesn't update the output index of WaitSynchronizationN.
84 } 78 }
85 79
80 // Invoke the wakeup callback before clearing the wait objects
81 if (thread->wakeup_callback)
82 thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this);
83
86 for (auto& object : thread->wait_objects) 84 for (auto& object : thread->wait_objects)
87 object->RemoveWaitingThread(thread.get()); 85 object->RemoveWaitingThread(thread.get());
88 thread->wait_objects.clear(); 86 thread->wait_objects.clear();
89 87
90 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
91 thread->ResumeFromWait(); 88 thread->ResumeFromWait();
92 } 89 }
93} 90}
diff --git a/src/core/hle/lock.cpp b/src/core/hle/lock.cpp
new file mode 100644
index 000000000..1c24c7ce9
--- /dev/null
+++ b/src/core/hle/lock.cpp
@@ -0,0 +1,11 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <core/hle/lock.h>
8
9namespace HLE {
10std::recursive_mutex g_hle_lock;
11}
diff --git a/src/core/hle/lock.h b/src/core/hle/lock.h
new file mode 100644
index 000000000..5c99fe996
--- /dev/null
+++ b/src/core/hle/lock.h
@@ -0,0 +1,18 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <mutex>
8
9namespace HLE {
10/*
11 * Synchronizes access to the internal HLE kernel structures, it is acquired when a guest
12 * application thread performs a syscall. It should be acquired by any host threads that read or
13 * modify the HLE kernel state. Note: Any operation that directly or indirectly reads from or writes
14 * to the emulated memory is not protected by this mutex, and should be avoided in any threads other
15 * than the CPU thread.
16 */
17extern std::recursive_mutex g_hle_lock;
18} // namespace HLE
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 5c44b43bb..912ab550d 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <boost/optional.hpp>
5#include "common/common_paths.h" 6#include "common/common_paths.h"
6#include "common/file_util.h" 7#include "common/file_util.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
@@ -18,6 +19,7 @@
18#include "core/hle/service/apt/apt_s.h" 19#include "core/hle/service/apt/apt_s.h"
19#include "core/hle/service/apt/apt_u.h" 20#include "core/hle/service/apt/apt_u.h"
20#include "core/hle/service/apt/bcfnt/bcfnt.h" 21#include "core/hle/service/apt/bcfnt/bcfnt.h"
22#include "core/hle/service/cfg/cfg.h"
21#include "core/hle/service/fs/archive.h" 23#include "core/hle/service/fs/archive.h"
22#include "core/hle/service/ptm/ptm.h" 24#include "core/hle/service/ptm/ptm.h"
23#include "core/hle/service/service.h" 25#include "core/hle/service/service.h"
@@ -33,8 +35,6 @@ static bool shared_font_loaded = false;
33static bool shared_font_relocated = false; 35static bool shared_font_relocated = false;
34 36
35static Kernel::SharedPtr<Kernel::Mutex> lock; 37static Kernel::SharedPtr<Kernel::Mutex> lock;
36static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
37static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event
38 38
39static u32 cpu_percent; ///< CPU time available to the running application 39static u32 cpu_percent; ///< CPU time available to the running application
40 40
@@ -43,43 +43,344 @@ static u8 unknown_ns_state_field;
43 43
44static ScreencapPostPermission screen_capture_post_permission; 44static ScreencapPostPermission screen_capture_post_permission;
45 45
46/// Parameter data to be returned in the next call to Glance/ReceiveParameter 46/// Parameter data to be returned in the next call to Glance/ReceiveParameter.
47static MessageParameter next_parameter; 47/// TODO(Subv): Use std::optional once we migrate to C++17.
48static boost::optional<MessageParameter> next_parameter;
49
50enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 };
51
52static constexpr size_t NumAppletSlot = 4;
53
54enum class AppletSlot : u8 {
55 Application,
56 SystemApplet,
57 HomeMenu,
58 LibraryApplet,
59
60 // An invalid tag
61 Error,
62};
63
64union AppletAttributes {
65 u32 raw;
66
67 BitField<0, 3, u32> applet_pos;
68 BitField<29, 1, u32> is_home_menu;
69
70 AppletAttributes() : raw(0) {}
71 AppletAttributes(u32 attributes) : raw(attributes) {}
72};
73
74struct AppletSlotData {
75 AppletId applet_id;
76 AppletSlot slot;
77 bool registered;
78 AppletAttributes attributes;
79 Kernel::SharedPtr<Kernel::Event> notification_event;
80 Kernel::SharedPtr<Kernel::Event> parameter_event;
81};
82
83// Holds data about the concurrently running applets in the system.
84static std::array<AppletSlotData, NumAppletSlot> applet_slots = {};
85
86// This overload returns nullptr if no applet with the specified id has been started.
87static AppletSlotData* GetAppletSlotData(AppletId id) {
88 auto GetSlot = [](AppletSlot slot) -> AppletSlotData* {
89 return &applet_slots[static_cast<size_t>(slot)];
90 };
91
92 if (id == AppletId::Application) {
93 auto* slot = GetSlot(AppletSlot::Application);
94 if (slot->applet_id != AppletId::None)
95 return slot;
96
97 return nullptr;
98 }
99
100 if (id == AppletId::AnySystemApplet) {
101 auto* system_slot = GetSlot(AppletSlot::SystemApplet);
102 if (system_slot->applet_id != AppletId::None)
103 return system_slot;
104
105 // The Home Menu is also a system applet, but it lives in its own slot to be able to run
106 // concurrently with other system applets.
107 auto* home_slot = GetSlot(AppletSlot::HomeMenu);
108 if (home_slot->applet_id != AppletId::None)
109 return home_slot;
110
111 return nullptr;
112 }
113
114 if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) {
115 auto* slot = GetSlot(AppletSlot::LibraryApplet);
116 if (slot->applet_id == AppletId::None)
117 return nullptr;
118
119 u32 applet_pos = slot->attributes.applet_pos;
120
121 if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast<u32>(AppletPos::Library))
122 return slot;
123
124 if (id == AppletId::AnySysLibraryApplet &&
125 applet_pos == static_cast<u32>(AppletPos::SysLibrary))
126 return slot;
127
128 return nullptr;
129 }
130
131 if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) {
132 auto* slot = GetSlot(AppletSlot::HomeMenu);
133 if (slot->applet_id != AppletId::None)
134 return slot;
135
136 return nullptr;
137 }
138
139 for (auto& slot : applet_slots) {
140 if (slot.applet_id == id)
141 return &slot;
142 }
143
144 return nullptr;
145}
146
147static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) {
148 // Mapping from AppletPos to AppletSlot
149 static constexpr std::array<AppletSlot, 6> applet_position_slots = {
150 AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet,
151 AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet};
152
153 u32 applet_pos = attributes.applet_pos;
154 if (applet_pos >= applet_position_slots.size())
155 return nullptr;
156
157 AppletSlot slot = applet_position_slots[applet_pos];
158
159 if (slot == AppletSlot::Error)
160 return nullptr;
161
162 // The Home Menu is a system applet, however, it has its own applet slot so that it can run
163 // concurrently with other system applets.
164 if (slot == AppletSlot::SystemApplet && attributes.is_home_menu)
165 return &applet_slots[static_cast<size_t>(AppletSlot::HomeMenu)];
166
167 return &applet_slots[static_cast<size_t>(slot)];
168}
48 169
49void SendParameter(const MessageParameter& parameter) { 170void SendParameter(const MessageParameter& parameter) {
50 next_parameter = parameter; 171 next_parameter = parameter;
51 // Signal the event to let the application know that a new parameter is ready to be read 172 // Signal the event to let the receiver know that a new parameter is ready to be read
52 parameter_event->Signal(); 173 auto* const slot_data = GetAppletSlotData(static_cast<AppletId>(parameter.destination_id));
174 if (slot_data == nullptr) {
175 LOG_DEBUG(Service_APT, "No applet was registered with the id %03X",
176 parameter.destination_id);
177 return;
178 }
179
180 slot_data->parameter_event->Signal();
53} 181}
54 182
55void Initialize(Service::Interface* self) { 183void Initialize(Service::Interface* self) {
56 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080 184 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080
57 u32 app_id = rp.Pop<u32>(); 185 u32 app_id = rp.Pop<u32>();
58 u32 flags = rp.Pop<u32>(); 186 u32 attributes = rp.Pop<u32>();
187
188 LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", app_id, attributes);
189
190 auto* const slot_data = GetAppletSlotData(attributes);
191
192 // Note: The real NS service does not check if the attributes value is valid before accessing
193 // the data in the array
194 ASSERT_MSG(slot_data, "Invalid application attributes");
195
196 if (slot_data->registered) {
197 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
198 rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
199 ErrorSummary::InvalidState, ErrorLevel::Status));
200 return;
201 }
202
203 slot_data->applet_id = static_cast<AppletId>(app_id);
204 slot_data->attributes.raw = attributes;
205
59 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); 206 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
60 rb.Push(RESULT_SUCCESS); 207 rb.Push(RESULT_SUCCESS);
61 rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(), 208 rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(),
62 Kernel::g_handle_table.Create(parameter_event).Unwrap()); 209 Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap());
210
211 if (slot_data->applet_id == AppletId::Application ||
212 slot_data->applet_id == AppletId::HomeMenu) {
213 // Initialize the APT parameter to wake up the application.
214 next_parameter.emplace();
215 next_parameter->signal = static_cast<u32>(SignalType::Wakeup);
216 next_parameter->sender_id = static_cast<u32>(AppletId::None);
217 next_parameter->destination_id = app_id;
218 // Not signaling the parameter event will cause the application (or Home Menu) to hang
219 // during startup. In the real console, it is usually the Kernel and Home Menu who cause NS
220 // to signal the HomeMenu and Application parameter events, respectively.
221 slot_data->parameter_event->Signal();
222 }
223}
63 224
64 // TODO(bunnei): Check if these events are cleared every time Initialize is called. 225static u32 DecompressLZ11(const u8* in, u8* out) {
65 notification_event->Clear(); 226 u32_le decompressed_size;
66 parameter_event->Clear(); 227 memcpy(&decompressed_size, in, sizeof(u32));
228 in += 4;
229
230 u8 type = decompressed_size & 0xFF;
231 ASSERT(type == 0x11);
232 decompressed_size >>= 8;
67 233
68 ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); 234 u32 current_out_size = 0;
69 lock->Release(); 235 u8 flags = 0, mask = 1;
236 while (current_out_size < decompressed_size) {
237 if (mask == 1) {
238 flags = *(in++);
239 mask = 0x80;
240 } else {
241 mask >>= 1;
242 }
70 243
71 LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); 244 if (flags & mask) {
245 u8 byte1 = *(in++);
246 u32 length = byte1 >> 4;
247 u32 offset;
248 if (length == 0) {
249 u8 byte2 = *(in++);
250 u8 byte3 = *(in++);
251 length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11;
252 offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1;
253 } else if (length == 1) {
254 u8 byte2 = *(in++);
255 u8 byte3 = *(in++);
256 u8 byte4 = *(in++);
257 length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111;
258 offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1;
259 } else {
260 u8 byte2 = *(in++);
261 length = (byte1 >> 4) + 0x1;
262 offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1;
263 }
264
265 for (u32 i = 0; i < length; i++) {
266 *out = *(out - offset);
267 ++out;
268 }
269
270 current_out_size += length;
271 } else {
272 *(out++) = *(in++);
273 current_out_size++;
274 }
275 }
276 return decompressed_size;
277}
278
279static bool LoadSharedFont() {
280 u8 font_region_code;
281 switch (CFG::GetRegionValue()) {
282 case 4: // CHN
283 font_region_code = 2;
284 break;
285 case 5: // KOR
286 font_region_code = 3;
287 break;
288 case 6: // TWN
289 font_region_code = 4;
290 break;
291 default: // JPN/EUR/USA
292 font_region_code = 1;
293 break;
294 }
295
296 const u64_le shared_font_archive_id_low = 0x0004009b00014002 | ((font_region_code - 1) << 8);
297 const u64_le shared_font_archive_id_high = 0x00000001ffffff00;
298 std::vector<u8> shared_font_archive_id(16);
299 std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64));
300 std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64));
301 FileSys::Path archive_path(shared_font_archive_id);
302 auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path);
303 if (archive_result.Failed())
304 return false;
305
306 std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
307 FileSys::Path file_path(romfs_path);
308 FileSys::Mode open_mode = {};
309 open_mode.read_flag.Assign(1);
310 auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode);
311 if (file_result.Failed())
312 return false;
313
314 auto romfs = std::move(file_result).Unwrap();
315 std::vector<u8> romfs_buffer(romfs->backend->GetSize());
316 romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data());
317 romfs->backend->Close();
318
319 const char16_t* file_name[4] = {u"cbf_std.bcfnt.lz", u"cbf_zh-Hans-CN.bcfnt.lz",
320 u"cbf_ko-Hang-KR.bcfnt.lz", u"cbf_zh-Hant-TW.bcfnt.lz"};
321 const u8* font_file =
322 RomFS::GetFilePointer(romfs_buffer.data(), {file_name[font_region_code - 1]});
323 if (font_file == nullptr)
324 return false;
325
326 struct {
327 u32_le status;
328 u32_le region;
329 u32_le decompressed_size;
330 INSERT_PADDING_WORDS(0x1D);
331 } shared_font_header{};
332 static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size");
333
334 shared_font_header.status = 2; // successfully loaded
335 shared_font_header.region = font_region_code;
336 shared_font_header.decompressed_size =
337 DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80));
338 std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header));
339 *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
340
341 return true;
342}
343
344static bool LoadLegacySharedFont() {
345 // This is the legacy method to load shared font.
346 // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
347 // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
348 // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
349 // "shared_font.bin" in the Citra "sysdata" directory.
350 std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
351
352 FileUtil::CreateFullPath(filepath); // Create path if not already created
353 FileUtil::IOFile file(filepath, "rb");
354 if (file.IsOpen()) {
355 file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
356 return true;
357 }
358
359 return false;
72} 360}
73 361
74void GetSharedFont(Service::Interface* self) { 362void GetSharedFont(Service::Interface* self) {
75 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000 363 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000
76 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); 364 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
365
366 // Log in telemetry if the game uses the shared font
367 Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true);
368
77 if (!shared_font_loaded) { 369 if (!shared_font_loaded) {
78 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); 370 // On real 3DS, font loading happens on booting. However, we load it on demand to coordinate
79 rb.Push<u32>(-1); // TODO: Find the right error code 371 // with CFG region auto configuration, which happens later than APT initialization.
80 rb.Skip(1 + 2, true); 372 if (LoadSharedFont()) {
81 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); 373 shared_font_loaded = true;
82 return; 374 } else if (LoadLegacySharedFont()) {
375 LOG_WARNING(Service_APT, "Loaded shared font by legacy method");
376 shared_font_loaded = true;
377 } else {
378 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
379 rb.Push<u32>(-1); // TODO: Find the right error code
380 rb.Skip(1 + 2, true);
381 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont);
382 return;
383 }
83 } 384 }
84 385
85 // The shared font has to be relocated to the new address before being passed to the 386 // The shared font has to be relocated to the new address before being passed to the
@@ -115,7 +416,12 @@ void GetLockHandle(Service::Interface* self) {
115 // this will cause the app to wait until parameter_event is signaled. 416 // this will cause the app to wait until parameter_event is signaled.
116 u32 applet_attributes = rp.Pop<u32>(); 417 u32 applet_attributes = rp.Pop<u32>();
117 IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); 418 IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
118 rb.Push(RESULT_SUCCESS); // No error 419 rb.Push(RESULT_SUCCESS); // No error
420
421 // TODO(Subv): The output attributes should have an AppletPos of either Library or System |
422 // Library (depending on the type of the last launched applet) if the input attributes'
423 // AppletPos has the Library bit set.
424
119 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable. 425 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable.
120 rb.Push<u32>(0); // Least significant bit = power button state 426 rb.Push<u32>(0); // Least significant bit = power button state
121 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap(); 427 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap();
@@ -128,10 +434,22 @@ void GetLockHandle(Service::Interface* self) {
128void Enable(Service::Interface* self) { 434void Enable(Service::Interface* self) {
129 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040 435 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040
130 u32 attributes = rp.Pop<u32>(); 436 u32 attributes = rp.Pop<u32>();
437
438 LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes);
439
131 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 440 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
132 rb.Push(RESULT_SUCCESS); // No error 441
133 parameter_event->Signal(); // Let the application know that it has been started 442 auto* const slot_data = GetAppletSlotData(attributes);
134 LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); 443
444 if (!slot_data) {
445 rb.Push(ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet,
446 ErrorSummary::InvalidState, ErrorLevel::Status));
447 return;
448 }
449
450 slot_data->registered = true;
451
452 rb.Push(RESULT_SUCCESS);
135} 453}
136 454
137void GetAppletManInfo(Service::Interface* self) { 455void GetAppletManInfo(Service::Interface* self) {
@@ -149,22 +467,27 @@ void GetAppletManInfo(Service::Interface* self) {
149 467
150void IsRegistered(Service::Interface* self) { 468void IsRegistered(Service::Interface* self) {
151 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040 469 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040
152 u32 app_id = rp.Pop<u32>(); 470 AppletId app_id = static_cast<AppletId>(rp.Pop<u32>());
153 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); 471 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
154 rb.Push(RESULT_SUCCESS); // No error 472 rb.Push(RESULT_SUCCESS); // No error
155 473
156 // TODO(Subv): An application is considered "registered" if it has already called APT::Enable 474 auto* const slot_data = GetAppletSlotData(app_id);
157 // handle this properly once we implement multiprocess support. 475
158 bool is_registered = false; // Set to not registered by default 476 // Check if an LLE applet was registered first, then fallback to HLE applets
477 bool is_registered = slot_data && slot_data->registered;
159 478
160 if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) { 479 if (!is_registered) {
161 is_registered = HLE::Applets::IsLibraryAppletRunning(); 480 if (app_id == AppletId::AnyLibraryApplet) {
162 } else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) { 481 is_registered = HLE::Applets::IsLibraryAppletRunning();
163 is_registered = true; // Set to registered 482 } else if (auto applet = HLE::Applets::Applet::Get(app_id)) {
483 // The applet exists, set it as registered.
484 is_registered = true;
485 }
164 } 486 }
487
165 rb.Push(is_registered); 488 rb.Push(is_registered);
166 489
167 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); 490 LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast<u32>(app_id));
168} 491}
169 492
170void InquireNotification(Service::Interface* self) { 493void InquireNotification(Service::Interface* self) {
@@ -186,14 +509,17 @@ void SendParameter(Service::Interface* self) {
186 size_t size; 509 size_t size;
187 VAddr buffer = rp.PopStaticBuffer(&size); 510 VAddr buffer = rp.PopStaticBuffer(&size);
188 511
189 std::shared_ptr<HLE::Applets::Applet> dest_applet = 512 LOG_DEBUG(Service_APT,
190 HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id)); 513 "called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X,"
514 "buffer_size=0x%08X, handle=0x%08X, size=0x%08zX, in_param_buffer_ptr=0x%08X",
515 src_app_id, dst_app_id, signal_type, buffer_size, handle, size, buffer);
191 516
192 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 517 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
193 518
194 if (dest_applet == nullptr) { 519 // A new parameter can not be sent if the previous one hasn't been consumed yet
195 LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id); 520 if (next_parameter) {
196 rb.Push<u32>(-1); // TODO(Subv): Find the right error code 521 rb.Push(ResultCode(ErrCodes::ParameterPresent, ErrorModule::Applet,
522 ErrorSummary::InvalidState, ErrorLevel::Status));
197 return; 523 return;
198 } 524 }
199 525
@@ -205,12 +531,14 @@ void SendParameter(Service::Interface* self) {
205 param.buffer.resize(buffer_size); 531 param.buffer.resize(buffer_size);
206 Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size()); 532 Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size());
207 533
208 rb.Push(dest_applet->ReceiveParameter(param)); 534 SendParameter(param);
209 535
210 LOG_WARNING(Service_APT, 536 // If the applet is running in HLE mode, use the HLE interface to communicate with it.
211 "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X," 537 if (auto dest_applet = HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id))) {
212 "buffer_size=0x%08X, handle=0x%08X, size=0x%08zX, in_param_buffer_ptr=0x%08X", 538 rb.Push(dest_applet->ReceiveParameter(param));
213 src_app_id, dst_app_id, signal_type, buffer_size, handle, size, buffer); 539 } else {
540 rb.Push(RESULT_SUCCESS);
541 }
214} 542}
215 543
216void ReceiveParameter(Service::Interface* self) { 544void ReceiveParameter(Service::Interface* self) {
@@ -226,21 +554,40 @@ void ReceiveParameter(Service::Interface* self) {
226 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", 554 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
227 buffer_size, static_buff_size); 555 buffer_size, static_buff_size);
228 556
557 LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
558
559 if (!next_parameter) {
560 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
561 rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet,
562 ErrorSummary::InvalidState, ErrorLevel::Status));
563 return;
564 }
565
566 if (next_parameter->destination_id != app_id) {
567 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
568 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
569 ErrorLevel::Status));
570 return;
571 }
572
229 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4); 573 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
574
230 rb.Push(RESULT_SUCCESS); // No error 575 rb.Push(RESULT_SUCCESS); // No error
231 rb.Push(next_parameter.sender_id); 576 rb.Push(next_parameter->sender_id);
232 rb.Push(next_parameter.signal); // Signal type 577 rb.Push(next_parameter->signal); // Signal type
233 ASSERT_MSG(next_parameter.buffer.size() <= buffer_size, "Input static buffer is too small !"); 578 ASSERT_MSG(next_parameter->buffer.size() <= buffer_size, "Input static buffer is too small !");
234 rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size 579 rb.Push(static_cast<u32>(next_parameter->buffer.size())); // Parameter buffer size
235 580
236 rb.PushMoveHandles((next_parameter.object != nullptr) 581 rb.PushMoveHandles((next_parameter->object != nullptr)
237 ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap() 582 ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap()
238 : 0); 583 : 0);
239 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0);
240 584
241 Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size()); 585 rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
586
587 Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
242 588
243 LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); 589 // Clear the parameter
590 next_parameter = boost::none;
244} 591}
245 592
246void GlanceParameter(Service::Interface* self) { 593void GlanceParameter(Service::Interface* self) {
@@ -256,37 +603,74 @@ void GlanceParameter(Service::Interface* self) {
256 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", 603 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
257 buffer_size, static_buff_size); 604 buffer_size, static_buff_size);
258 605
606 LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
607
608 if (!next_parameter) {
609 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
610 rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet,
611 ErrorSummary::InvalidState, ErrorLevel::Status));
612 return;
613 }
614
615 if (next_parameter->destination_id != app_id) {
616 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
617 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
618 ErrorLevel::Status));
619 return;
620 }
621
259 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4); 622 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
260 rb.Push(RESULT_SUCCESS); // No error 623 rb.Push(RESULT_SUCCESS); // No error
261 rb.Push(next_parameter.sender_id); 624 rb.Push(next_parameter->sender_id);
262 rb.Push(next_parameter.signal); // Signal type 625 rb.Push(next_parameter->signal); // Signal type
263 ASSERT_MSG(next_parameter.buffer.size() <= buffer_size, "Input static buffer is too small !"); 626 ASSERT_MSG(next_parameter->buffer.size() <= buffer_size, "Input static buffer is too small !");
264 rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size 627 rb.Push(static_cast<u32>(next_parameter->buffer.size())); // Parameter buffer size
265 628
266 rb.PushCopyHandles((next_parameter.object != nullptr) 629 rb.PushMoveHandles((next_parameter->object != nullptr)
267 ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap() 630 ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap()
268 : 0); 631 : 0);
269 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0);
270 632
271 Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size()); 633 rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
634
635 Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
272 636
273 LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); 637 // Note: The NS module always clears the DSPSleep and DSPWakeup signals even in GlanceParameter.
638 if (next_parameter->signal == static_cast<u32>(SignalType::DspSleep) ||
639 next_parameter->signal == static_cast<u32>(SignalType::DspWakeup))
640 next_parameter = boost::none;
274} 641}
275 642
276void CancelParameter(Service::Interface* self) { 643void CancelParameter(Service::Interface* self) {
277 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 4, 0); // 0xF0100 644 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 4, 0); // 0xF0100
278 645
279 u32 check_sender = rp.Pop<u32>(); 646 bool check_sender = rp.Pop<bool>();
280 u32 sender_appid = rp.Pop<u32>(); 647 u32 sender_appid = rp.Pop<u32>();
281 u32 check_receiver = rp.Pop<u32>(); 648 bool check_receiver = rp.Pop<bool>();
282 u32 receiver_appid = rp.Pop<u32>(); 649 u32 receiver_appid = rp.Pop<u32>();
650
651 bool cancellation_success = true;
652
653 if (!next_parameter) {
654 cancellation_success = false;
655 } else {
656 if (check_sender && next_parameter->sender_id != sender_appid)
657 cancellation_success = false;
658
659 if (check_receiver && next_parameter->destination_id != receiver_appid)
660 cancellation_success = false;
661 }
662
663 if (cancellation_success)
664 next_parameter = boost::none;
665
283 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); 666 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
667
284 rb.Push(RESULT_SUCCESS); // No error 668 rb.Push(RESULT_SUCCESS); // No error
285 rb.Push(true); // Set to Success 669 rb.Push(cancellation_success);
286 670
287 LOG_WARNING(Service_APT, "(STUBBED) called check_sender=0x%08X, sender_appid=0x%08X, " 671 LOG_DEBUG(Service_APT, "called check_sender=%u, sender_appid=0x%08X, "
288 "check_receiver=0x%08X, receiver_appid=0x%08X", 672 "check_receiver=%u, receiver_appid=0x%08X",
289 check_sender, sender_appid, check_receiver, receiver_appid); 673 check_sender, sender_appid, check_receiver, receiver_appid);
290} 674}
291 675
292void PrepareToStartApplication(Service::Interface* self) { 676void PrepareToStartApplication(Service::Interface* self) {
@@ -383,7 +767,12 @@ void PrepareToStartLibraryApplet(Service::Interface* self) {
383 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040 767 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040
384 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); 768 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
385 769
770 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
771
386 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 772 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
773
774 // TODO(Subv): Launch the requested applet application.
775
387 auto applet = HLE::Applets::Applet::Get(applet_id); 776 auto applet = HLE::Applets::Applet::Get(applet_id);
388 if (applet) { 777 if (applet) {
389 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); 778 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
@@ -391,14 +780,32 @@ void PrepareToStartLibraryApplet(Service::Interface* self) {
391 } else { 780 } else {
392 rb.Push(HLE::Applets::Applet::Create(applet_id)); 781 rb.Push(HLE::Applets::Applet::Create(applet_id));
393 } 782 }
394 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); 783}
784
785void PrepareToStartNewestHomeMenu(Service::Interface* self) {
786 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); // 0x1A0000
787 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
788
789 // TODO(Subv): This command can only be called by a System Applet (return 0xC8A0CC04 otherwise).
790
791 // This command must return an error when called, otherwise the Home Menu will try to reboot the
792 // system.
793 rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
794 ErrorSummary::InvalidState, ErrorLevel::Status));
795
796 LOG_DEBUG(Service_APT, "called");
395} 797}
396 798
397void PreloadLibraryApplet(Service::Interface* self) { 799void PreloadLibraryApplet(Service::Interface* self) {
398 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040 800 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040
399 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); 801 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
400 802
803 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
804
401 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 805 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
806
807 // TODO(Subv): Launch the requested applet application.
808
402 auto applet = HLE::Applets::Applet::Get(applet_id); 809 auto applet = HLE::Applets::Applet::Get(applet_id);
403 if (applet) { 810 if (applet) {
404 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); 811 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
@@ -406,34 +813,40 @@ void PreloadLibraryApplet(Service::Interface* self) {
406 } else { 813 } else {
407 rb.Push(HLE::Applets::Applet::Create(applet_id)); 814 rb.Push(HLE::Applets::Applet::Create(applet_id));
408 } 815 }
409 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
410} 816}
411 817
412void StartLibraryApplet(Service::Interface* self) { 818void StartLibraryApplet(Service::Interface* self) {
413 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 2, 4); // 0x1E0084 819 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 2, 4); // 0x1E0084
414 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); 820 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
415 std::shared_ptr<HLE::Applets::Applet> applet = HLE::Applets::Applet::Get(applet_id);
416
417 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
418
419 if (applet == nullptr) {
420 LOG_ERROR(Service_APT, "unknown applet id=%08X", applet_id);
421 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, false);
422 rb.Push<u32>(-1); // TODO(Subv): Find the right error code
423 return;
424 }
425 821
426 size_t buffer_size = rp.Pop<u32>(); 822 size_t buffer_size = rp.Pop<u32>();
427 Kernel::Handle handle = rp.PopHandle(); 823 Kernel::Handle handle = rp.PopHandle();
428 VAddr buffer_addr = rp.PopStaticBuffer(); 824 VAddr buffer_addr = rp.PopStaticBuffer();
429 825
430 AppletStartupParameter parameter; 826 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
431 parameter.object = Kernel::g_handle_table.GetGeneric(handle);
432 parameter.buffer.resize(buffer_size);
433 Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size());
434 827
435 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 828 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
436 rb.Push(applet->Start(parameter)); 829
830 // Send the Wakeup signal to the applet
831 MessageParameter param;
832 param.destination_id = static_cast<u32>(applet_id);
833 param.sender_id = static_cast<u32>(AppletId::Application);
834 param.object = Kernel::g_handle_table.GetGeneric(handle);
835 param.signal = static_cast<u32>(SignalType::Wakeup);
836 param.buffer.resize(buffer_size);
837 Memory::ReadBlock(buffer_addr, param.buffer.data(), param.buffer.size());
838 SendParameter(param);
839
840 // In case the applet is being HLEd, attempt to communicate with it.
841 if (auto applet = HLE::Applets::Applet::Get(applet_id)) {
842 AppletStartupParameter parameter;
843 parameter.object = Kernel::g_handle_table.GetGeneric(handle);
844 parameter.buffer.resize(buffer_size);
845 Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size());
846 rb.Push(applet->Start(parameter));
847 } else {
848 rb.Push(RESULT_SUCCESS);
849 }
437} 850}
438 851
439void CancelLibraryApplet(Service::Interface* self) { 852void CancelLibraryApplet(Service::Interface* self) {
@@ -647,125 +1060,6 @@ void CheckNew3DS(Service::Interface* self) {
647 LOG_WARNING(Service_APT, "(STUBBED) called"); 1060 LOG_WARNING(Service_APT, "(STUBBED) called");
648} 1061}
649 1062
650static u32 DecompressLZ11(const u8* in, u8* out) {
651 u32_le decompressed_size;
652 memcpy(&decompressed_size, in, sizeof(u32));
653 in += 4;
654
655 u8 type = decompressed_size & 0xFF;
656 ASSERT(type == 0x11);
657 decompressed_size >>= 8;
658
659 u32 current_out_size = 0;
660 u8 flags = 0, mask = 1;
661 while (current_out_size < decompressed_size) {
662 if (mask == 1) {
663 flags = *(in++);
664 mask = 0x80;
665 } else {
666 mask >>= 1;
667 }
668
669 if (flags & mask) {
670 u8 byte1 = *(in++);
671 u32 length = byte1 >> 4;
672 u32 offset;
673 if (length == 0) {
674 u8 byte2 = *(in++);
675 u8 byte3 = *(in++);
676 length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11;
677 offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1;
678 } else if (length == 1) {
679 u8 byte2 = *(in++);
680 u8 byte3 = *(in++);
681 u8 byte4 = *(in++);
682 length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111;
683 offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1;
684 } else {
685 u8 byte2 = *(in++);
686 length = (byte1 >> 4) + 0x1;
687 offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1;
688 }
689
690 for (u32 i = 0; i < length; i++) {
691 *out = *(out - offset);
692 ++out;
693 }
694
695 current_out_size += length;
696 } else {
697 *(out++) = *(in++);
698 current_out_size++;
699 }
700 }
701 return decompressed_size;
702}
703
704static bool LoadSharedFont() {
705 // TODO (wwylele): load different font archive for region CHN/KOR/TWN
706 const u64_le shared_font_archive_id_low = 0x0004009b00014002;
707 const u64_le shared_font_archive_id_high = 0x00000001ffffff00;
708 std::vector<u8> shared_font_archive_id(16);
709 std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64));
710 std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64));
711 FileSys::Path archive_path(shared_font_archive_id);
712 auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path);
713 if (archive_result.Failed())
714 return false;
715
716 std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
717 FileSys::Path file_path(romfs_path);
718 FileSys::Mode open_mode = {};
719 open_mode.read_flag.Assign(1);
720 auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode);
721 if (file_result.Failed())
722 return false;
723
724 auto romfs = std::move(file_result).Unwrap();
725 std::vector<u8> romfs_buffer(romfs->backend->GetSize());
726 romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data());
727 romfs->backend->Close();
728
729 const u8* font_file = RomFS::GetFilePointer(romfs_buffer.data(), {u"cbf_std.bcfnt.lz"});
730 if (font_file == nullptr)
731 return false;
732
733 struct {
734 u32_le status;
735 u32_le region;
736 u32_le decompressed_size;
737 INSERT_PADDING_WORDS(0x1D);
738 } shared_font_header{};
739 static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size");
740
741 shared_font_header.status = 2; // successfully loaded
742 shared_font_header.region = 1; // region JPN/EUR/USA
743 shared_font_header.decompressed_size =
744 DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80));
745 std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header));
746 *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
747
748 return true;
749}
750
751static bool LoadLegacySharedFont() {
752 // This is the legacy method to load shared font.
753 // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
754 // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
755 // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
756 // "shared_font.bin" in the Citra "sysdata" directory.
757 std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
758
759 FileUtil::CreateFullPath(filepath); // Create path if not already created
760 FileUtil::IOFile file(filepath, "rb");
761 if (file.IsOpen()) {
762 file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
763 return true;
764 }
765
766 return false;
767}
768
769void Init() { 1063void Init() {
770 AddService(new APT_A_Interface); 1064 AddService(new APT_A_Interface);
771 AddService(new APT_S_Interface); 1065 AddService(new APT_S_Interface);
@@ -789,19 +1083,24 @@ void Init() {
789 shared_font_loaded = false; 1083 shared_font_loaded = false;
790 } 1084 }
791 1085
792 lock = Kernel::Mutex::Create(false, "APT_U:Lock"); 1086 lock = Kernel::Mutex::Create(false, 0, "APT_U:Lock");
793 1087
794 cpu_percent = 0; 1088 cpu_percent = 0;
795 unknown_ns_state_field = 0; 1089 unknown_ns_state_field = 0;
796 screen_capture_post_permission = 1090 screen_capture_post_permission =
797 ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value 1091 ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value
798 1092
799 // TODO(bunnei): Check if these are created in Initialize or on APT process startup. 1093 for (size_t slot = 0; slot < applet_slots.size(); ++slot) {
800 notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); 1094 auto& slot_data = applet_slots[slot];
801 parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); 1095 slot_data.slot = static_cast<AppletSlot>(slot);
802 1096 slot_data.applet_id = AppletId::None;
803 next_parameter.signal = static_cast<u32>(SignalType::Wakeup); 1097 slot_data.attributes.raw = 0;
804 next_parameter.destination_id = 0x300; 1098 slot_data.registered = false;
1099 slot_data.notification_event =
1100 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification");
1101 slot_data.parameter_event =
1102 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter");
1103 }
805} 1104}
806 1105
807void Shutdown() { 1106void Shutdown() {
@@ -809,10 +1108,14 @@ void Shutdown() {
809 shared_font_loaded = false; 1108 shared_font_loaded = false;
810 shared_font_relocated = false; 1109 shared_font_relocated = false;
811 lock = nullptr; 1110 lock = nullptr;
812 notification_event = nullptr;
813 parameter_event = nullptr;
814 1111
815 next_parameter.object = nullptr; 1112 for (auto& slot : applet_slots) {
1113 slot.registered = false;
1114 slot.notification_event = nullptr;
1115 slot.parameter_event = nullptr;
1116 }
1117
1118 next_parameter = boost::none;
816 1119
817 HLE::Applets::Shutdown(); 1120 HLE::Applets::Shutdown();
818} 1121}
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index ee80926d2..7b79e1f3e 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -72,6 +72,8 @@ enum class SignalType : u32 {
72 72
73/// App Id's used by APT functions 73/// App Id's used by APT functions
74enum class AppletId : u32 { 74enum class AppletId : u32 {
75 None = 0,
76 AnySystemApplet = 0x100,
75 HomeMenu = 0x101, 77 HomeMenu = 0x101,
76 AlternateMenu = 0x103, 78 AlternateMenu = 0x103,
77 Camera = 0x110, 79 Camera = 0x110,
@@ -83,6 +85,7 @@ enum class AppletId : u32 {
83 Miiverse = 0x117, 85 Miiverse = 0x117,
84 MiiversePost = 0x118, 86 MiiversePost = 0x118,
85 AmiiboSettings = 0x119, 87 AmiiboSettings = 0x119,
88 AnySysLibraryApplet = 0x200,
86 SoftwareKeyboard1 = 0x201, 89 SoftwareKeyboard1 = 0x201,
87 Ed1 = 0x202, 90 Ed1 = 0x202,
88 PnoteApp = 0x204, 91 PnoteApp = 0x204,
@@ -116,6 +119,13 @@ enum class ScreencapPostPermission : u32 {
116 DisableScreenshotPostingToMiiverse = 3 119 DisableScreenshotPostingToMiiverse = 3
117}; 120};
118 121
122namespace ErrCodes {
123enum {
124 ParameterPresent = 2,
125 InvalidAppletSlot = 4,
126};
127} // namespace ErrCodes
128
119/// Send a parameter to the currently-running application, which will read it via ReceiveParameter 129/// Send a parameter to the currently-running application, which will read it via ReceiveParameter
120void SendParameter(const MessageParameter& parameter); 130void SendParameter(const MessageParameter& parameter);
121 131
@@ -410,6 +420,16 @@ void GetAppCpuTimeLimit(Service::Interface* self);
410void PrepareToStartLibraryApplet(Service::Interface* self); 420void PrepareToStartLibraryApplet(Service::Interface* self);
411 421
412/** 422/**
423 * APT::PrepareToStartNewestHomeMenu service function
424 * Inputs:
425 * 0 : Command header [0x001A0000]
426 * Outputs:
427 * 0 : Return header
428 * 1 : Result of function
429 */
430void PrepareToStartNewestHomeMenu(Service::Interface* self);
431
432/**
413 * APT::PreloadLibraryApplet service function 433 * APT::PreloadLibraryApplet service function
414 * Inputs: 434 * Inputs:
415 * 0 : Command header [0x00160040] 435 * 0 : Command header [0x00160040]
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index ec5668d05..bb78ee7d7 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -17,10 +17,10 @@ const Interface::FunctionInfo FunctionTable[] = {
17 {0x00060040, GetAppletInfo, "GetAppletInfo"}, 17 {0x00060040, GetAppletInfo, "GetAppletInfo"},
18 {0x00070000, nullptr, "GetLastSignaledAppletId"}, 18 {0x00070000, nullptr, "GetLastSignaledAppletId"},
19 {0x00080000, nullptr, "CountRegisteredApplet"}, 19 {0x00080000, nullptr, "CountRegisteredApplet"},
20 {0x00090040, nullptr, "IsRegistered"}, 20 {0x00090040, IsRegistered, "IsRegistered"},
21 {0x000A0040, nullptr, "GetAttribute"}, 21 {0x000A0040, nullptr, "GetAttribute"},
22 {0x000B0040, InquireNotification, "InquireNotification"}, 22 {0x000B0040, InquireNotification, "InquireNotification"},
23 {0x000C0104, nullptr, "SendParameter"}, 23 {0x000C0104, SendParameter, "SendParameter"},
24 {0x000D0080, ReceiveParameter, "ReceiveParameter"}, 24 {0x000D0080, ReceiveParameter, "ReceiveParameter"},
25 {0x000E0080, GlanceParameter, "GlanceParameter"}, 25 {0x000E0080, GlanceParameter, "GlanceParameter"},
26 {0x000F0100, nullptr, "CancelParameter"}, 26 {0x000F0100, nullptr, "CancelParameter"},
@@ -34,11 +34,11 @@ const Interface::FunctionInfo FunctionTable[] = {
34 {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, 34 {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
35 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, 35 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
36 {0x00190040, nullptr, "PrepareToStartSystemApplet"}, 36 {0x00190040, nullptr, "PrepareToStartSystemApplet"},
37 {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, 37 {0x001A0000, PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"},
38 {0x001B00C4, nullptr, "StartApplication"}, 38 {0x001B00C4, nullptr, "StartApplication"},
39 {0x001C0000, nullptr, "WakeupApplication"}, 39 {0x001C0000, nullptr, "WakeupApplication"},
40 {0x001D0000, nullptr, "CancelApplication"}, 40 {0x001D0000, nullptr, "CancelApplication"},
41 {0x001E0084, nullptr, "StartLibraryApplet"}, 41 {0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
42 {0x001F0084, nullptr, "StartSystemApplet"}, 42 {0x001F0084, nullptr, "StartSystemApplet"},
43 {0x00200044, nullptr, "StartNewestHomeMenu"}, 43 {0x00200044, nullptr, "StartNewestHomeMenu"},
44 {0x00210000, nullptr, "OrderToCloseApplication"}, 44 {0x00210000, nullptr, "OrderToCloseApplication"},
diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp
index c9f9e9d95..8172edae8 100644
--- a/src/core/hle/service/cam/cam.cpp
+++ b/src/core/hle/service/cam/cam.cpp
@@ -177,7 +177,7 @@ void CompletionEventCallBack(u64 port_id, int) {
177 LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%zu)!", 177 LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%zu)!",
178 port.dest_size, buffer_size); 178 port.dest_size, buffer_size);
179 } 179 }
180 Memory::WriteBlock(port.dest, buffer.data(), std::min<u32>(port.dest_size, buffer_size)); 180 Memory::WriteBlock(port.dest, buffer.data(), std::min<size_t>(port.dest_size, buffer_size));
181 } 181 }
182 182
183 port.is_receiving = false; 183 port.is_receiving = false;
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 6624f1711..f78c25fb2 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -141,7 +141,7 @@ void GetCountryCodeString(Service::Interface* self) {
141 141
142void GetCountryCodeID(Service::Interface* self) { 142void GetCountryCodeID(Service::Interface* self) {
143 u32* cmd_buff = Kernel::GetCommandBuffer(); 143 u32* cmd_buff = Kernel::GetCommandBuffer();
144 u16 country_code = cmd_buff[1]; 144 u16 country_code = static_cast<u16>(cmd_buff[1]);
145 u16 country_code_id = 0; 145 u16 country_code_id = 0;
146 146
147 // The following algorithm will fail if the first country code isn't 0. 147 // The following algorithm will fail if the first country code isn't 0.
@@ -168,7 +168,7 @@ void GetCountryCodeID(Service::Interface* self) {
168 cmd_buff[2] = country_code_id; 168 cmd_buff[2] = country_code_id;
169} 169}
170 170
171static u32 GetRegionValue() { 171u32 GetRegionValue() {
172 if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) 172 if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT)
173 return preferred_region_code; 173 return preferred_region_code;
174 174
@@ -681,7 +681,7 @@ void GenerateConsoleUniqueId(u32& random_number, u64& console_id) {
681 CryptoPP::AutoSeededRandomPool rng; 681 CryptoPP::AutoSeededRandomPool rng;
682 random_number = rng.GenerateWord32(0, 0xFFFF); 682 random_number = rng.GenerateWord32(0, 0xFFFF);
683 u64_le local_friend_code_seed; 683 u64_le local_friend_code_seed;
684 rng.GenerateBlock(reinterpret_cast<byte*>(&local_friend_code_seed), 684 rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&local_friend_code_seed),
685 sizeof(local_friend_code_seed)); 685 sizeof(local_friend_code_seed));
686 console_id = (local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48); 686 console_id = (local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48);
687} 687}
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index 1659ebf32..282b6936b 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -101,6 +101,8 @@ void GetCountryCodeString(Service::Interface* self);
101 */ 101 */
102void GetCountryCodeID(Service::Interface* self); 102void GetCountryCodeID(Service::Interface* self);
103 103
104u32 GetRegionValue();
105
104/** 106/**
105 * CFG::SecureInfoGetRegion service function 107 * CFG::SecureInfoGetRegion service function
106 * Inputs: 108 * Inputs:
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
index 9471ec1ef..aac903ccb 100644
--- a/src/core/hle/service/csnd_snd.cpp
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -47,7 +47,7 @@ static void Initialize(Interface* self) {
47 MemoryPermission::ReadWrite, 0, 47 MemoryPermission::ReadWrite, 0,
48 Kernel::MemoryRegion::BASE, "CSND:SharedMemory"); 48 Kernel::MemoryRegion::BASE, "CSND:SharedMemory");
49 49
50 mutex = Kernel::Mutex::Create(false, "CSND:mutex"); 50 mutex = Kernel::Mutex::Create(false, 0, "CSND:mutex");
51 51
52 cmd_buff[1] = RESULT_SUCCESS.raw; 52 cmd_buff[1] = RESULT_SUCCESS.raw;
53 cmd_buff[2] = IPC::CopyHandleDesc(2); 53 cmd_buff[2] = IPC::CopyHandleDesc(2);
diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp
index 56f934b3f..6f2bf2061 100644
--- a/src/core/hle/service/dlp/dlp_clnt.cpp
+++ b/src/core/hle/service/dlp/dlp_clnt.cpp
@@ -8,7 +8,26 @@ namespace Service {
8namespace DLP { 8namespace DLP {
9 9
10const Interface::FunctionInfo FunctionTable[] = { 10const Interface::FunctionInfo FunctionTable[] = {
11 {0x000100C3, nullptr, "Initialize"}, {0x00110000, nullptr, "GetWirelessRebootPassphrase"}, 11 {0x000100C3, nullptr, "Initialize"},
12 {0x00020000, nullptr, "Finalize"},
13 {0x00030000, nullptr, "GetEventDesc"},
14 {0x00040000, nullptr, "GetChannel"},
15 {0x00050180, nullptr, "StartScan"},
16 {0x00060000, nullptr, "StopScan"},
17 {0x00070080, nullptr, "GetServerInfo"},
18 {0x00080100, nullptr, "GetTitleInfo"},
19 {0x00090040, nullptr, "GetTitleInfoInOrder"},
20 {0x000A0080, nullptr, "DeleteScanInfo"},
21 {0x000B0100, nullptr, "PrepareForSystemDownload"},
22 {0x000C0000, nullptr, "StartSystemDownload"},
23 {0x000D0100, nullptr, "StartTitleDownload"},
24 {0x000E0000, nullptr, "GetMyStatus"},
25 {0x000F0040, nullptr, "GetConnectingNodes"},
26 {0x00100040, nullptr, "GetNodeInfo"},
27 {0x00110000, nullptr, "GetWirelessRebootPassphrase"},
28 {0x00120000, nullptr, "StopSession"},
29 {0x00130100, nullptr, "GetCupVersion"},
30 {0x00140100, nullptr, "GetDupAvailability"},
12}; 31};
13 32
14DLP_CLNT_Interface::DLP_CLNT_Interface() { 33DLP_CLNT_Interface::DLP_CLNT_Interface() {
diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp
index 29b9d52e0..fe6be7d32 100644
--- a/src/core/hle/service/dlp/dlp_fkcl.cpp
+++ b/src/core/hle/service/dlp/dlp_fkcl.cpp
@@ -8,7 +8,23 @@ namespace Service {
8namespace DLP { 8namespace DLP {
9 9
10const Interface::FunctionInfo FunctionTable[] = { 10const Interface::FunctionInfo FunctionTable[] = {
11 {0x00010083, nullptr, "Initialize"}, {0x000F0000, nullptr, "GetWirelessRebootPassphrase"}, 11 {0x00010083, nullptr, "Initialize"},
12 {0x00020000, nullptr, "Finalize"},
13 {0x00030000, nullptr, "GetEventDesc"},
14 {0x00040000, nullptr, "GetChannels"},
15 {0x00050180, nullptr, "StartScan"},
16 {0x00060000, nullptr, "StopScan"},
17 {0x00070080, nullptr, "GetServerInfo"},
18 {0x00080100, nullptr, "GetTitleInfo"},
19 {0x00090040, nullptr, "GetTitleInfoInOrder"},
20 {0x000A0080, nullptr, "DeleteScanInfo"},
21 {0x000B0100, nullptr, "StartFakeSession"},
22 {0x000C0000, nullptr, "GetMyStatus"},
23 {0x000D0040, nullptr, "GetConnectingNodes"},
24 {0x000E0040, nullptr, "GetNodeInfo"},
25 {0x000F0000, nullptr, "GetWirelessRebootPassphrase"},
26 {0x00100000, nullptr, "StopSession"},
27 {0x00110203, nullptr, "Initialize2"},
12}; 28};
13 29
14DLP_FKCL_Interface::DLP_FKCL_Interface() { 30DLP_FKCL_Interface::DLP_FKCL_Interface() {
diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp
index 32cfa2c44..1bcea43d3 100644
--- a/src/core/hle/service/dlp/dlp_srvr.cpp
+++ b/src/core/hle/service/dlp/dlp_srvr.cpp
@@ -11,7 +11,7 @@
11namespace Service { 11namespace Service {
12namespace DLP { 12namespace DLP {
13 13
14static void unk_0x000E0040(Interface* self) { 14static void IsChild(Interface* self) {
15 u32* cmd_buff = Kernel::GetCommandBuffer(); 15 u32* cmd_buff = Kernel::GetCommandBuffer();
16 16
17 cmd_buff[1] = RESULT_SUCCESS.raw; 17 cmd_buff[1] = RESULT_SUCCESS.raw;
@@ -24,14 +24,19 @@ const Interface::FunctionInfo FunctionTable[] = {
24 {0x00010183, nullptr, "Initialize"}, 24 {0x00010183, nullptr, "Initialize"},
25 {0x00020000, nullptr, "Finalize"}, 25 {0x00020000, nullptr, "Finalize"},
26 {0x00030000, nullptr, "GetServerState"}, 26 {0x00030000, nullptr, "GetServerState"},
27 {0x00040000, nullptr, "GetEventDescription"},
27 {0x00050080, nullptr, "StartAccepting"}, 28 {0x00050080, nullptr, "StartAccepting"},
29 {0x00060000, nullptr, "EndAccepting"},
28 {0x00070000, nullptr, "StartDistribution"}, 30 {0x00070000, nullptr, "StartDistribution"},
29 {0x000800C0, nullptr, "SendWirelessRebootPassphrase"}, 31 {0x000800C0, nullptr, "SendWirelessRebootPassphrase"},
30 {0x00090040, nullptr, "AcceptClient"}, 32 {0x00090040, nullptr, "AcceptClient"},
33 {0x000A0040, nullptr, "DisconnectClient"},
31 {0x000B0042, nullptr, "GetConnectingClients"}, 34 {0x000B0042, nullptr, "GetConnectingClients"},
32 {0x000C0040, nullptr, "GetClientInfo"}, 35 {0x000C0040, nullptr, "GetClientInfo"},
33 {0x000D0040, nullptr, "GetClientState"}, 36 {0x000D0040, nullptr, "GetClientState"},
34 {0x000E0040, unk_0x000E0040, "unk_0x000E0040"}, 37 {0x000E0040, IsChild, "IsChild"},
38 {0x000F0303, nullptr, "InitializeWithName"},
39 {0x00100000, nullptr, "GetDupNoticeNeed"},
35}; 40};
36 41
37DLP_SRVR_Interface::DLP_SRVR_Interface() { 42DLP_SRVR_Interface::DLP_SRVR_Interface() {
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 7d746054f..42f8950f9 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -147,9 +147,10 @@ static void LoadComponent(Service::Interface* self) {
147 LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, 147 LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64,
148 Common::ComputeHash64(component_data.data(), component_data.size())); 148 Common::ComputeHash64(component_data.data(), component_data.size()));
149 // Some versions of the firmware have the location of DSP structures listed here. 149 // Some versions of the firmware have the location of DSP structures listed here.
150 ASSERT(size > 0x37C); 150 if (size > 0x37C) {
151 LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, 151 LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64,
152 Common::ComputeHash64(component_data.data() + 0x340, 60)); 152 Common::ComputeHash64(component_data.data() + 0x340, 60));
153 }
153 154
154 LOG_WARNING(Service_DSP, 155 LOG_WARNING(Service_DSP,
155 "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X", 156 "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X",
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 033fbc9aa..4ee7df73c 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -20,6 +20,7 @@
20#include "core/file_sys/archive_savedata.h" 20#include "core/file_sys/archive_savedata.h"
21#include "core/file_sys/archive_sdmc.h" 21#include "core/file_sys/archive_sdmc.h"
22#include "core/file_sys/archive_sdmcwriteonly.h" 22#include "core/file_sys/archive_sdmcwriteonly.h"
23#include "core/file_sys/archive_selfncch.h"
23#include "core/file_sys/archive_systemsavedata.h" 24#include "core/file_sys/archive_systemsavedata.h"
24#include "core/file_sys/directory_backend.h" 25#include "core/file_sys/directory_backend.h"
25#include "core/file_sys/errors.h" 26#include "core/file_sys/errors.h"
@@ -48,7 +49,7 @@ struct hash<Service::FS::ArchiveIdCode> {
48 return std::hash<Type>()(static_cast<Type>(id_code)); 49 return std::hash<Type>()(static_cast<Type>(id_code));
49 } 50 }
50}; 51};
51} 52} // namespace std
52 53
53static constexpr Kernel::Handle INVALID_HANDLE{}; 54static constexpr Kernel::Handle INVALID_HANDLE{};
54 55
@@ -216,7 +217,7 @@ void Directory::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> serve
216 LOG_TRACE(Service_FS, "Read %s: count=%d", GetName().c_str(), count); 217 LOG_TRACE(Service_FS, "Read %s: count=%d", GetName().c_str(), count);
217 218
218 // Number of entries actually read 219 // Number of entries actually read
219 u32 read = backend->Read(entries.size(), entries.data()); 220 u32 read = backend->Read(static_cast<u32>(entries.size()), entries.data());
220 cmd_buff[2] = read; 221 cmd_buff[2] = read;
221 Memory::WriteBlock(address, entries.data(), read * sizeof(FileSys::Entry)); 222 Memory::WriteBlock(address, entries.data(), read * sizeof(FileSys::Entry));
222 break; 223 break;
@@ -564,6 +565,21 @@ void RegisterArchiveTypes() {
564 auto systemsavedata_factory = 565 auto systemsavedata_factory =
565 std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); 566 std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory);
566 RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); 567 RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData);
568
569 auto selfncch_factory = std::make_unique<FileSys::ArchiveFactory_SelfNCCH>();
570 RegisterArchiveType(std::move(selfncch_factory), ArchiveIdCode::SelfNCCH);
571}
572
573void RegisterSelfNCCH(Loader::AppLoader& app_loader) {
574 auto itr = id_code_map.find(ArchiveIdCode::SelfNCCH);
575 if (itr == id_code_map.end()) {
576 LOG_ERROR(Service_FS,
577 "Could not register a new NCCH because the SelfNCCH archive hasn't been created");
578 return;
579 }
580
581 auto* factory = static_cast<FileSys::ArchiveFactory_SelfNCCH*>(itr->second.get());
582 factory->Register(app_loader);
567} 583}
568 584
569void UnregisterArchiveTypes() { 585void UnregisterArchiveTypes() {
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 3a3371c88..e3c8fc2ef 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -21,6 +21,10 @@ static constexpr char SYSTEM_ID[]{"00000000000000000000000000000000"};
21/// The scrambled SD card CID, also known as ID1 21/// The scrambled SD card CID, also known as ID1
22static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"}; 22static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"};
23 23
24namespace Loader {
25class AppLoader;
26}
27
24namespace Service { 28namespace Service {
25namespace FS { 29namespace FS {
26 30
@@ -259,6 +263,9 @@ void ArchiveInit();
259/// Shutdown archives 263/// Shutdown archives
260void ArchiveShutdown(); 264void ArchiveShutdown();
261 265
266/// Registers a new NCCH file with the SelfNCCH archive factory
267void RegisterSelfNCCH(Loader::AppLoader& app_loader);
268
262/// Register all archive types 269/// Register all archive types
263void RegisterArchiveTypes(); 270void RegisterArchiveTypes();
264 271
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 2014b8461..379fbd71c 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -7,8 +7,9 @@
7#include <cmath> 7#include <cmath>
8#include <memory> 8#include <memory>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/3ds.h"
11#include "core/core.h"
10#include "core/core_timing.h" 12#include "core/core_timing.h"
11#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h" 13#include "core/frontend/input.h"
13#include "core/hle/ipc.h" 14#include "core/hle/ipc.h"
14#include "core/hle/kernel/event.h" 15#include "core/hle/kernel/event.h"
@@ -18,7 +19,6 @@
18#include "core/hle/service/hid/hid_spvr.h" 19#include "core/hle/service/hid/hid_spvr.h"
19#include "core/hle/service/hid/hid_user.h" 20#include "core/hle/service/hid/hid_user.h"
20#include "core/hle/service/service.h" 21#include "core/hle/service/service.h"
21#include "video_core/video_core.h"
22 22
23namespace Service { 23namespace Service {
24namespace HID { 24namespace HID {
@@ -50,10 +50,15 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234;
50constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; 50constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104;
51constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; 51constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101;
52 52
53constexpr float accelerometer_coef = 512.0f; // measured from hw test result
54constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call
55
53static std::atomic<bool> is_device_reload_pending; 56static std::atomic<bool> is_device_reload_pending;
54static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 57static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
55 buttons; 58 buttons;
56static std::unique_ptr<Input::AnalogDevice> circle_pad; 59static std::unique_ptr<Input::AnalogDevice> circle_pad;
60static std::unique_ptr<Input::MotionDevice> motion_device;
61static std::unique_ptr<Input::TouchDevice> touch_device;
57 62
58DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { 63DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
59 // 30 degree and 60 degree are angular thresholds for directions 64 // 30 degree and 60 degree are angular thresholds for directions
@@ -90,6 +95,8 @@ static void LoadInputDevices() {
90 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 95 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
91 circle_pad = Input::CreateDevice<Input::AnalogDevice>( 96 circle_pad = Input::CreateDevice<Input::AnalogDevice>(
92 Settings::values.analogs[Settings::NativeAnalog::CirclePad]); 97 Settings::values.analogs[Settings::NativeAnalog::CirclePad]);
98 motion_device = Input::CreateDevice<Input::MotionDevice>(Settings::values.motion_device);
99 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
93} 100}
94 101
95static void UnloadInputDevices() { 102static void UnloadInputDevices() {
@@ -97,6 +104,8 @@ static void UnloadInputDevices() {
97 button.reset(); 104 button.reset();
98 } 105 }
99 circle_pad.reset(); 106 circle_pad.reset();
107 motion_device.reset();
108 touch_device.reset();
100} 109}
101 110
102static void UpdatePadCallback(u64 userdata, int cycles_late) { 111static void UpdatePadCallback(u64 userdata, int cycles_late) {
@@ -165,8 +174,10 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) {
165 // Get the current touch entry 174 // Get the current touch entry
166 TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index]; 175 TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index];
167 bool pressed = false; 176 bool pressed = false;
168 177 float x, y;
169 std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState(); 178 std::tie(x, y, pressed) = touch_device->GetStatus();
179 touch_entry.x = static_cast<u16>(x * Core::kScreenBottomWidth);
180 touch_entry.y = static_cast<u16>(y * Core::kScreenBottomHeight);
170 touch_entry.valid.Assign(pressed ? 1 : 0); 181 touch_entry.valid.Assign(pressed ? 1 : 0);
171 182
172 // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which 183 // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which
@@ -193,10 +204,19 @@ static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) {
193 mem->accelerometer.index = next_accelerometer_index; 204 mem->accelerometer.index = next_accelerometer_index;
194 next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size(); 205 next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size();
195 206
207 Math::Vec3<float> accel;
208 std::tie(accel, std::ignore) = motion_device->GetStatus();
209 accel *= accelerometer_coef;
210 // TODO(wwylele): do a time stretch like the one in UpdateGyroscopeCallback
211 // The time stretch formula should be like
212 // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
213
196 AccelerometerDataEntry& accelerometer_entry = 214 AccelerometerDataEntry& accelerometer_entry =
197 mem->accelerometer.entries[mem->accelerometer.index]; 215 mem->accelerometer.entries[mem->accelerometer.index];
198 std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) = 216
199 VideoCore::g_emu_window->GetAccelerometerState(); 217 accelerometer_entry.x = static_cast<s16>(accel.x);
218 accelerometer_entry.y = static_cast<s16>(accel.y);
219 accelerometer_entry.z = static_cast<s16>(accel.z);
200 220
201 // Make up "raw" entry 221 // Make up "raw" entry
202 // TODO(wwylele): 222 // TODO(wwylele):
@@ -227,8 +247,14 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) {
227 next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size(); 247 next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size();
228 248
229 GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index]; 249 GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index];
230 std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) = 250
231 VideoCore::g_emu_window->GetGyroscopeState(); 251 Math::Vec3<float> gyro;
252 std::tie(std::ignore, gyro) = motion_device->GetStatus();
253 double stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
254 gyro *= gyroscope_coef * static_cast<float>(stretch);
255 gyroscope_entry.x = static_cast<s16>(gyro.x);
256 gyroscope_entry.y = static_cast<s16>(gyro.y);
257 gyroscope_entry.z = static_cast<s16>(gyro.z);
232 258
233 // Make up "raw" entry 259 // Make up "raw" entry
234 mem->gyroscope.raw_entry.x = gyroscope_entry.x; 260 mem->gyroscope.raw_entry.x = gyroscope_entry.x;
@@ -326,7 +352,7 @@ void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) {
326 352
327 cmd_buff[1] = RESULT_SUCCESS.raw; 353 cmd_buff[1] = RESULT_SUCCESS.raw;
328 354
329 f32 coef = VideoCore::g_emu_window->GetGyroscopeRawToDpsCoefficient(); 355 f32 coef = gyroscope_coef;
330 memcpy(&cmd_buff[2], &coef, 4); 356 memcpy(&cmd_buff[2], &coef, 4);
331} 357}
332 358
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 1ef972e70..ef25926b5 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -24,7 +24,7 @@ namespace HID {
24 */ 24 */
25struct PadState { 25struct PadState {
26 union { 26 union {
27 u32 hex; 27 u32 hex{};
28 28
29 BitField<0, 1, u32> a; 29 BitField<0, 1, u32> a;
30 BitField<1, 1, u32> b; 30 BitField<1, 1, u32> b;
diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp
index 837413f93..0912d5756 100644
--- a/src/core/hle/service/ir/ir_rst.cpp
+++ b/src/core/hle/service/ir/ir_rst.cpp
@@ -18,7 +18,7 @@ namespace Service {
18namespace IR { 18namespace IR {
19 19
20union PadState { 20union PadState {
21 u32_le hex; 21 u32_le hex{};
22 22
23 BitField<14, 1, u32_le> zl; 23 BitField<14, 1, u32_le> zl;
24 BitField<15, 1, u32_le> zr; 24 BitField<15, 1, u32_le> zr;
diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h
index 3bc10dbdc..57b4fb6df 100644
--- a/src/core/hle/service/ldr_ro/cro_helper.h
+++ b/src/core/hle/service/ldr_ro/cro_helper.h
@@ -413,7 +413,8 @@ private:
413 */ 413 */
414 template <typename T> 414 template <typename T>
415 void GetEntry(std::size_t index, T& data) const { 415 void GetEntry(std::size_t index, T& data) const {
416 Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); 416 Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast<u32>(index * sizeof(T)),
417 &data, sizeof(T));
417 } 418 }
418 419
419 /** 420 /**
@@ -425,7 +426,8 @@ private:
425 */ 426 */
426 template <typename T> 427 template <typename T>
427 void SetEntry(std::size_t index, const T& data) { 428 void SetEntry(std::size_t index, const T& data) {
428 Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); 429 Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast<u32>(index * sizeof(T)),
430 &data, sizeof(T));
429 } 431 }
430 432
431 /** 433 /**
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index d5624fe54..b10d5852b 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -5,6 +5,8 @@
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/hle/ipc.h" 7#include "core/hle/ipc.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/event.h"
8#include "core/hle/service/nim/nim.h" 10#include "core/hle/service/nim/nim.h"
9#include "core/hle/service/nim/nim_aoc.h" 11#include "core/hle/service/nim/nim_aoc.h"
10#include "core/hle/service/nim/nim_s.h" 12#include "core/hle/service/nim/nim_s.h"
@@ -14,6 +16,16 @@
14namespace Service { 16namespace Service {
15namespace NIM { 17namespace NIM {
16 18
19static Kernel::SharedPtr<Kernel::Event> nim_system_update_event;
20
21void CheckForSysUpdateEvent(Service::Interface* self) {
22 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 0, 0); // 0x50000
23 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
24 rb.Push(RESULT_SUCCESS);
25 rb.PushCopyHandles(Kernel::g_handle_table.Create(nim_system_update_event).Unwrap());
26 LOG_TRACE(Service_NIM, "called");
27}
28
17void CheckSysUpdateAvailable(Service::Interface* self) { 29void CheckSysUpdateAvailable(Service::Interface* self) {
18 u32* cmd_buff = Kernel::GetCommandBuffer(); 30 u32* cmd_buff = Kernel::GetCommandBuffer();
19 31
@@ -29,9 +41,13 @@ void Init() {
29 AddService(new NIM_AOC_Interface); 41 AddService(new NIM_AOC_Interface);
30 AddService(new NIM_S_Interface); 42 AddService(new NIM_S_Interface);
31 AddService(new NIM_U_Interface); 43 AddService(new NIM_U_Interface);
44
45 nim_system_update_event = Kernel::Event::Create(ResetType::OneShot, "NIM System Update Event");
32} 46}
33 47
34void Shutdown() {} 48void Shutdown() {
49 nim_system_update_event = nullptr;
50}
35 51
36} // namespace NIM 52} // namespace NIM
37 53
diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h
index c3106f18b..dbf605e5a 100644
--- a/src/core/hle/service/nim/nim.h
+++ b/src/core/hle/service/nim/nim.h
@@ -11,6 +11,17 @@ class Interface;
11namespace NIM { 11namespace NIM {
12 12
13/** 13/**
14 * NIM::CheckForSysUpdateEvent service function
15 * Inputs:
16 * 1 : None
17 * Outputs:
18 * 1 : Result of function, 0 on success, otherwise error code
19 * 2 : Copy handle descriptor
20 * 3 : System Update event handle
21 */
22void CheckForSysUpdateEvent(Service::Interface* self);
23
24/**
14 * NIM::CheckSysUpdateAvailable service function 25 * NIM::CheckSysUpdateAvailable service function
15 * Inputs: 26 * Inputs:
16 * 1 : None 27 * 1 : None
diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp
index 7664bad60..569660278 100644
--- a/src/core/hle/service/nim/nim_u.cpp
+++ b/src/core/hle/service/nim/nim_u.cpp
@@ -12,7 +12,7 @@ const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010000, nullptr, "StartSysUpdate"}, 12 {0x00010000, nullptr, "StartSysUpdate"},
13 {0x00020000, nullptr, "GetUpdateDownloadProgress"}, 13 {0x00020000, nullptr, "GetUpdateDownloadProgress"},
14 {0x00040000, nullptr, "FinishTitlesInstall"}, 14 {0x00040000, nullptr, "FinishTitlesInstall"},
15 {0x00050000, nullptr, "CheckForSysUpdateEvent"}, 15 {0x00050000, CheckForSysUpdateEvent, "CheckForSysUpdateEvent"},
16 {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"}, 16 {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"},
17 {0x000A0000, nullptr, "GetState"}, 17 {0x000A0000, nullptr, "GetState"},
18 {0x000B0000, nullptr, "GetSystemTitleHash"}, 18 {0x000B0000, nullptr, "GetSystemTitleHash"},
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
new file mode 100644
index 000000000..9e19c38bf
--- /dev/null
+++ b/src/core/hle/service/ns/ns.cpp
@@ -0,0 +1,16 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ns/ns.h"
6#include "core/hle/service/ns/ns_s.h"
7
8namespace Service {
9namespace NS {
10
11void InstallInterfaces(SM::ServiceManager& service_manager) {
12 std::make_shared<NS_S>()->InstallAsService(service_manager);
13}
14
15} // namespace NS
16} // namespace Service
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
new file mode 100644
index 000000000..c3d67d98c
--- /dev/null
+++ b/src/core/hle/service/ns/ns.h
@@ -0,0 +1,16 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Service {
10namespace NS {
11
12/// Registers all NS services with the specified service manager.
13void InstallInterfaces(SM::ServiceManager& service_manager);
14
15} // namespace NS
16} // namespace Service
diff --git a/src/core/hle/service/ns/ns_s.cpp b/src/core/hle/service/ns/ns_s.cpp
new file mode 100644
index 000000000..d952888dc
--- /dev/null
+++ b/src/core/hle/service/ns/ns_s.cpp
@@ -0,0 +1,34 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ns/ns_s.h"
6
7namespace Service {
8namespace NS {
9
10NS_S::NS_S() : ServiceFramework("ns:s", 2) {
11 static const FunctionInfo functions[] = {
12 {0x000100C0, nullptr, "LaunchFIRM"},
13 {0x000200C0, nullptr, "LaunchTitle"},
14 {0x00030000, nullptr, "TerminateApplication"},
15 {0x00040040, nullptr, "TerminateProcess"},
16 {0x000500C0, nullptr, "LaunchApplicationFIRM"},
17 {0x00060042, nullptr, "SetFIRMParams4A0"},
18 {0x00070042, nullptr, "CardUpdateInitialize"},
19 {0x00080000, nullptr, "CardUpdateShutdown"},
20 {0x000D0140, nullptr, "SetTWLBannerHMAC"},
21 {0x000E0000, nullptr, "ShutdownAsync"},
22 {0x00100180, nullptr, "RebootSystem"},
23 {0x00110100, nullptr, "TerminateTitle"},
24 {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"},
25 {0x00150140, nullptr, "LaunchApplication"},
26 {0x00160000, nullptr, "RebootSystemClean"},
27 };
28 RegisterHandlers(functions);
29}
30
31NS_S::~NS_S() = default;
32
33} // namespace NS
34} // namespace Service
diff --git a/src/core/hle/service/ns_s.h b/src/core/hle/service/ns/ns_s.h
index 90288a521..660ae453f 100644
--- a/src/core/hle/service/ns_s.h
+++ b/src/core/hle/service/ns/ns_s.h
@@ -4,18 +4,17 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/kernel/kernel.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Service { 10namespace Service {
10namespace NS { 11namespace NS {
11 12
12class NS_S final : public Interface { 13/// Interface to "ns:s" service
14class NS_S final : public ServiceFramework<NS_S> {
13public: 15public:
14 NS_S(); 16 NS_S();
15 17 ~NS_S();
16 std::string GetPortName() const override {
17 return "ns:s";
18 }
19}; 18};
20 19
21} // namespace NS 20} // namespace NS
diff --git a/src/core/hle/service/ns_s.cpp b/src/core/hle/service/ns_s.cpp
deleted file mode 100644
index 215c9aacc..000000000
--- a/src/core/hle/service/ns_s.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ns_s.h"
6
7namespace Service {
8namespace NS {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 {0x000100C0, nullptr, "LaunchFIRM"},
12 {0x000200C0, nullptr, "LaunchTitle"},
13 {0x00030000, nullptr, "TerminateApplication"},
14 {0x00040040, nullptr, "TerminateProcess"},
15 {0x000500C0, nullptr, "LaunchApplicationFIRM"},
16 {0x00060042, nullptr, "SetFIRMParams4A0"},
17 {0x00070042, nullptr, "CardUpdateInitialize"},
18 {0x00080000, nullptr, "CardUpdateShutdown"},
19 {0x000D0140, nullptr, "SetTWLBannerHMAC"},
20 {0x000E0000, nullptr, "ShutdownAsync"},
21 {0x00100180, nullptr, "RebootSystem"},
22 {0x00110100, nullptr, "TerminateTitle"},
23 {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"},
24 {0x00150140, nullptr, "LaunchApplication"},
25 {0x00160000, nullptr, "RebootSystemClean"},
26};
27
28NS_S::NS_S() {
29 Register(FunctionTable);
30}
31
32} // namespace NS
33} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index 6dbdff044..87a6b0eca 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -2,8 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <array> 6#include <array>
6#include <cstring> 7#include <cstring>
8#include <list>
9#include <mutex>
7#include <unordered_map> 10#include <unordered_map>
8#include <vector> 11#include <vector>
9#include "common/common_types.h" 12#include "common/common_types.h"
@@ -12,11 +15,14 @@
12#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/event.h" 16#include "core/hle/kernel/event.h"
14#include "core/hle/kernel/shared_memory.h" 17#include "core/hle/kernel/shared_memory.h"
18#include "core/hle/lock.h"
15#include "core/hle/result.h" 19#include "core/hle/result.h"
16#include "core/hle/service/nwm/nwm_uds.h" 20#include "core/hle/service/nwm/nwm_uds.h"
17#include "core/hle/service/nwm/uds_beacon.h" 21#include "core/hle/service/nwm/uds_beacon.h"
22#include "core/hle/service/nwm/uds_connection.h"
18#include "core/hle/service/nwm/uds_data.h" 23#include "core/hle/service/nwm/uds_data.h"
19#include "core/memory.h" 24#include "core/memory.h"
25#include "network/network.h"
20 26
21namespace Service { 27namespace Service {
22namespace NWM { 28namespace NWM {
@@ -34,9 +40,12 @@ static ConnectionStatus connection_status{};
34/* Node information about the current network. 40/* Node information about the current network.
35 * The amount of elements in this vector is always the maximum number 41 * The amount of elements in this vector is always the maximum number
36 * of nodes specified in the network configuration. 42 * of nodes specified in the network configuration.
37 * The first node is always the host, so this always contains at least 1 entry. 43 * The first node is always the host.
38 */ 44 */
39static NodeList node_info(1); 45static NodeList node_info;
46
47// Node information about our own system.
48static NodeInfo current_node;
40 49
41// Mapping of bind node ids to their respective events. 50// Mapping of bind node ids to their respective events.
42static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; 51static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events;
@@ -51,6 +60,298 @@ static NetworkInfo network_info;
51// Event that will generate and send the 802.11 beacon frames. 60// Event that will generate and send the 802.11 beacon frames.
52static int beacon_broadcast_event; 61static int beacon_broadcast_event;
53 62
63// Mutex to synchronize access to the connection status between the emulation thread and the
64// network thread.
65static std::mutex connection_status_mutex;
66
67// Mutex to synchronize access to the list of received beacons between the emulation thread and the
68// network thread.
69static std::mutex beacon_mutex;
70
71// Number of beacons to store before we start dropping the old ones.
72// TODO(Subv): Find a more accurate value for this limit.
73constexpr size_t MaxBeaconFrames = 15;
74
75// List of the last <MaxBeaconFrames> beacons received from the network.
76static std::list<Network::WifiPacket> received_beacons;
77
78/**
79 * Returns a list of received 802.11 beacon frames from the specified sender since the last call.
80 */
81std::list<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) {
82 std::lock_guard<std::mutex> lock(beacon_mutex);
83 if (sender != Network::BroadcastMac) {
84 std::list<Network::WifiPacket> filtered_list;
85 const auto beacon = std::find_if(received_beacons.begin(), received_beacons.end(),
86 [&sender](const Network::WifiPacket& packet) {
87 return packet.transmitter_address == sender;
88 });
89 if (beacon != received_beacons.end()) {
90 filtered_list.push_back(*beacon);
91 // TODO(B3N30): Check if the complete deque is cleared or just the fetched entries
92 received_beacons.erase(beacon);
93 }
94 return filtered_list;
95 }
96 return std::move(received_beacons);
97}
98
99/// Sends a WifiPacket to the room we're currently connected to.
100void SendPacket(Network::WifiPacket& packet) {
101 // TODO(Subv): Implement.
102}
103
104/*
105 * Returns an available index in the nodes array for the
106 * currently-hosted UDS network.
107 */
108static u16 GetNextAvailableNodeId() {
109 for (u16 index = 0; index < connection_status.max_nodes; ++index) {
110 if ((connection_status.node_bitmask & (1 << index)) == 0)
111 return index;
112 }
113
114 // Any connection attempts to an already full network should have been refused.
115 ASSERT_MSG(false, "No available connection slots in the network");
116}
117
118// Inserts the received beacon frame in the beacon queue and removes any older beacons if the size
119// limit is exceeded.
120void HandleBeaconFrame(const Network::WifiPacket& packet) {
121 std::lock_guard<std::mutex> lock(beacon_mutex);
122 const auto unique_beacon =
123 std::find_if(received_beacons.begin(), received_beacons.end(),
124 [&packet](const Network::WifiPacket& new_packet) {
125 return new_packet.transmitter_address == packet.transmitter_address;
126 });
127 if (unique_beacon != received_beacons.end()) {
128 // We already have a beacon from the same mac in the deque, remove the old one;
129 received_beacons.erase(unique_beacon);
130 }
131
132 received_beacons.emplace_back(packet);
133
134 // Discard old beacons if the buffer is full.
135 if (received_beacons.size() > MaxBeaconFrames)
136 received_beacons.pop_front();
137}
138
139void HandleAssociationResponseFrame(const Network::WifiPacket& packet) {
140 auto assoc_result = GetAssociationResult(packet.data);
141
142 ASSERT_MSG(std::get<AssocStatus>(assoc_result) == AssocStatus::Successful,
143 "Could not join network");
144 {
145 std::lock_guard<std::mutex> lock(connection_status_mutex);
146 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::Connecting));
147 }
148
149 // Send the EAPoL-Start packet to the server.
150 using Network::WifiPacket;
151 WifiPacket eapol_start;
152 eapol_start.channel = network_channel;
153 eapol_start.data = GenerateEAPoLStartFrame(std::get<u16>(assoc_result), current_node);
154 // TODO(B3N30): Encrypt the packet.
155 eapol_start.destination_address = packet.transmitter_address;
156 eapol_start.type = WifiPacket::PacketType::Data;
157
158 SendPacket(eapol_start);
159}
160
161static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
162 std::lock_guard<std::mutex> lock(connection_status_mutex);
163
164 if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) {
165 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
166 LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u",
167 connection_status.status);
168 return;
169 }
170
171 auto node = DeserializeNodeInfoFromFrame(packet.data);
172
173 if (connection_status.max_nodes == connection_status.total_nodes) {
174 // Reject connection attempt
175 LOG_ERROR(Service_NWM, "Reached maximum nodes, but reject packet wasn't sent.");
176 // TODO(B3N30): Figure out what packet is sent here
177 return;
178 }
179
180 // Get an unused network node id
181 u16 node_id = GetNextAvailableNodeId();
182 node.network_node_id = node_id + 1;
183
184 connection_status.node_bitmask |= 1 << node_id;
185 connection_status.changed_nodes |= 1 << node_id;
186 connection_status.nodes[node_id] = node.network_node_id;
187 connection_status.total_nodes++;
188
189 u8 current_nodes = network_info.total_nodes;
190 node_info[current_nodes] = node;
191
192 network_info.total_nodes++;
193
194 // Send the EAPoL-Logoff packet.
195 using Network::WifiPacket;
196 WifiPacket eapol_logoff;
197 eapol_logoff.channel = network_channel;
198 eapol_logoff.data =
199 GenerateEAPoLLogoffFrame(packet.transmitter_address, node.network_node_id, node_info,
200 network_info.max_nodes, network_info.total_nodes);
201 // TODO(Subv): Encrypt the packet.
202 eapol_logoff.destination_address = packet.transmitter_address;
203 eapol_logoff.type = WifiPacket::PacketType::Data;
204
205 SendPacket(eapol_logoff);
206 // TODO(B3N30): Broadcast updated node list
207 // The 3ds does this presumably to support spectators.
208 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
209 connection_status_event->Signal();
210 } else {
211 if (connection_status.status != static_cast<u32>(NetworkStatus::NotConnected)) {
212 LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u",
213 connection_status.status);
214 return;
215 }
216 auto logoff = ParseEAPoLLogoffFrame(packet.data);
217
218 network_info.total_nodes = logoff.connected_nodes;
219 network_info.max_nodes = logoff.max_nodes;
220
221 connection_status.network_node_id = logoff.assigned_node_id;
222 connection_status.total_nodes = logoff.connected_nodes;
223 connection_status.max_nodes = logoff.max_nodes;
224
225 node_info.clear();
226 node_info.reserve(network_info.max_nodes);
227 for (size_t index = 0; index < logoff.connected_nodes; ++index) {
228 connection_status.node_bitmask |= 1 << index;
229 connection_status.changed_nodes |= 1 << index;
230 connection_status.nodes[index] = logoff.nodes[index].network_node_id;
231
232 node_info.emplace_back(DeserializeNodeInfo(logoff.nodes[index]));
233 }
234
235 // We're now connected, signal the application
236 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsClient);
237 // Some games require ConnectToNetwork to block, for now it doesn't
238 // If blocking is implemented this lock needs to be changed,
239 // otherwise it might cause deadlocks
240 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
241 connection_status_event->Signal();
242 }
243}
244
245/*
246 * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11
247 * authentication frame with SEQ1.
248 */
249void StartConnectionSequence(const MacAddress& server) {
250 using Network::WifiPacket;
251 WifiPacket auth_request;
252 {
253 std::lock_guard<std::mutex> lock(connection_status_mutex);
254 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected));
255
256 // TODO(Subv): Handle timeout.
257
258 // Send an authentication frame with SEQ1
259 auth_request.channel = network_channel;
260 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1);
261 auth_request.destination_address = server;
262 auth_request.type = WifiPacket::PacketType::Authentication;
263 }
264
265 SendPacket(auth_request);
266}
267
268/// Sends an Association Response frame to the specified mac address
269void SendAssociationResponseFrame(const MacAddress& address) {
270 using Network::WifiPacket;
271 WifiPacket assoc_response;
272
273 {
274 std::lock_guard<std::mutex> lock(connection_status_mutex);
275 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
276 LOG_ERROR(Service_NWM, "Connection sequence aborted, because connection status is %u",
277 connection_status.status);
278 return;
279 }
280
281 assoc_response.channel = network_channel;
282 // TODO(Subv): This will cause multiple clients to end up with the same association id, but
283 // we're not using that for anything.
284 u16 association_id = 1;
285 assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id,
286 network_info.network_id);
287 assoc_response.destination_address = address;
288 assoc_response.type = WifiPacket::PacketType::AssociationResponse;
289 }
290
291 SendPacket(assoc_response);
292}
293
294/*
295 * Handles the authentication request frame and sends the authentication response and association
296 * response frames. Once an Authentication frame with SEQ1 is received by the server, it responds
297 * with an Authentication frame containing SEQ2, and immediately sends an Association response frame
298 * containing the details of the access point and the assigned association id for the new client.
299 */
300void HandleAuthenticationFrame(const Network::WifiPacket& packet) {
301 // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
302 if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
303 using Network::WifiPacket;
304 WifiPacket auth_request;
305 {
306 std::lock_guard<std::mutex> lock(connection_status_mutex);
307 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
308 LOG_ERROR(Service_NWM,
309 "Connection sequence aborted, because connection status is %u",
310 connection_status.status);
311 return;
312 }
313
314 // Respond with an authentication response frame with SEQ2
315 auth_request.channel = network_channel;
316 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
317 auth_request.destination_address = packet.transmitter_address;
318 auth_request.type = WifiPacket::PacketType::Authentication;
319 }
320 SendPacket(auth_request);
321
322 SendAssociationResponseFrame(packet.transmitter_address);
323 }
324}
325
326static void HandleDataFrame(const Network::WifiPacket& packet) {
327 switch (GetFrameEtherType(packet.data)) {
328 case EtherType::EAPoL:
329 HandleEAPoLPacket(packet);
330 break;
331 case EtherType::SecureData:
332 // TODO(B3N30): Handle SecureData packets
333 break;
334 }
335}
336
337/// Callback to parse and handle a received wifi packet.
338void OnWifiPacketReceived(const Network::WifiPacket& packet) {
339 switch (packet.type) {
340 case Network::WifiPacket::PacketType::Beacon:
341 HandleBeaconFrame(packet);
342 break;
343 case Network::WifiPacket::PacketType::Authentication:
344 HandleAuthenticationFrame(packet);
345 break;
346 case Network::WifiPacket::PacketType::AssociationResponse:
347 HandleAssociationResponseFrame(packet);
348 break;
349 case Network::WifiPacket::PacketType::Data:
350 HandleDataFrame(packet);
351 break;
352 }
353}
354
54/** 355/**
55 * NWM_UDS::Shutdown service function 356 * NWM_UDS::Shutdown service function
56 * Inputs: 357 * Inputs:
@@ -111,11 +412,10 @@ static void RecvBeaconBroadcastData(Interface* self) {
111 u32 total_size = sizeof(BeaconDataReplyHeader); 412 u32 total_size = sizeof(BeaconDataReplyHeader);
112 413
113 // Retrieve all beacon frames that were received from the desired mac address. 414 // Retrieve all beacon frames that were received from the desired mac address.
114 std::deque<WifiPacket> beacons = 415 auto beacons = GetReceivedBeacons(mac_address);
115 GetReceivedPackets(WifiPacket::PacketType::Beacon, mac_address);
116 416
117 BeaconDataReplyHeader data_reply_header{}; 417 BeaconDataReplyHeader data_reply_header{};
118 data_reply_header.total_entries = beacons.size(); 418 data_reply_header.total_entries = static_cast<u32>(beacons.size());
119 data_reply_header.max_output_size = out_buffer_size; 419 data_reply_header.max_output_size = out_buffer_size;
120 420
121 Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader)); 421 Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader));
@@ -125,8 +425,8 @@ static void RecvBeaconBroadcastData(Interface* self) {
125 for (const auto& beacon : beacons) { 425 for (const auto& beacon : beacons) {
126 BeaconEntryHeader entry{}; 426 BeaconEntryHeader entry{};
127 // TODO(Subv): Figure out what this size is used for. 427 // TODO(Subv): Figure out what this size is used for.
128 entry.unk_size = sizeof(BeaconEntryHeader) + beacon.data.size(); 428 entry.unk_size = static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
129 entry.total_size = sizeof(BeaconEntryHeader) + beacon.data.size(); 429 entry.total_size = static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
130 entry.wifi_channel = beacon.channel; 430 entry.wifi_channel = beacon.channel;
131 entry.header_size = sizeof(BeaconEntryHeader); 431 entry.header_size = sizeof(BeaconEntryHeader);
132 entry.mac_address = beacon.transmitter_address; 432 entry.mac_address = beacon.transmitter_address;
@@ -137,9 +437,9 @@ static void RecvBeaconBroadcastData(Interface* self) {
137 current_buffer_pos += sizeof(BeaconEntryHeader); 437 current_buffer_pos += sizeof(BeaconEntryHeader);
138 438
139 Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size()); 439 Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size());
140 current_buffer_pos += beacon.data.size(); 440 current_buffer_pos += static_cast<VAddr>(beacon.data.size());
141 441
142 total_size += sizeof(BeaconEntryHeader) + beacon.data.size(); 442 total_size += static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
143 } 443 }
144 444
145 // Update the total size in the structure and write it to the buffer again. 445 // Update the total size in the structure and write it to the buffer again.
@@ -174,7 +474,7 @@ static void InitializeWithVersion(Interface* self) {
174 u32 sharedmem_size = rp.Pop<u32>(); 474 u32 sharedmem_size = rp.Pop<u32>();
175 475
176 // Update the node information with the data the game gave us. 476 // Update the node information with the data the game gave us.
177 rp.PopRaw(node_info[0]); 477 rp.PopRaw(current_node);
178 478
179 u16 version = rp.Pop<u16>(); 479 u16 version = rp.Pop<u16>();
180 480
@@ -184,15 +484,22 @@ static void InitializeWithVersion(Interface* self) {
184 484
185 ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); 485 ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size.");
186 486
187 // Reset the connection status, it contains all zeros after initialization, 487 {
188 // except for the actual status value. 488 std::lock_guard<std::mutex> lock(connection_status_mutex);
189 connection_status = {}; 489
190 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); 490 // Reset the connection status, it contains all zeros after initialization,
491 // except for the actual status value.
492 connection_status = {};
493 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
494 }
191 495
192 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); 496 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
193 rb.Push(RESULT_SUCCESS); 497 rb.Push(RESULT_SUCCESS);
194 rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap()); 498 rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap());
195 499
500 // TODO(Subv): Connect the OnWifiPacketReceived function to the wifi packet received callback of
501 // the room we're currently in.
502
196 LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X", 503 LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X",
197 sharedmem_size, version, sharedmem_handle); 504 sharedmem_size, version, sharedmem_handle);
198} 505}
@@ -214,12 +521,16 @@ static void GetConnectionStatus(Interface* self) {
214 IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); 521 IPC::RequestBuilder rb = rp.MakeBuilder(13, 0);
215 522
216 rb.Push(RESULT_SUCCESS); 523 rb.Push(RESULT_SUCCESS);
217 rb.PushRaw(connection_status); 524 {
218 525 std::lock_guard<std::mutex> lock(connection_status_mutex);
219 // Reset the bitmask of changed nodes after each call to this 526 rb.PushRaw(connection_status);
220 // function to prevent falsely informing games of outstanding 527
221 // changes in subsequent calls. 528 // Reset the bitmask of changed nodes after each call to this
222 connection_status.changed_nodes = 0; 529 // function to prevent falsely informing games of outstanding
530 // changes in subsequent calls.
531 // TODO(Subv): Find exactly where the NWM module resets this value.
532 connection_status.changed_nodes = 0;
533 }
223 534
224 LOG_DEBUG(Service_NWM, "called"); 535 LOG_DEBUG(Service_NWM, "called");
225} 536}
@@ -300,31 +611,36 @@ static void BeginHostingNetwork(Interface* self) {
300 // The real UDS module throws a fatal error if this assert fails. 611 // The real UDS module throws a fatal error if this assert fails.
301 ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); 612 ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member.");
302 613
303 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); 614 {
304 615 std::lock_guard<std::mutex> lock(connection_status_mutex);
305 // Ensure the application data size is less than the maximum value. 616 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost);
306 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big."); 617
307 618 // Ensure the application data size is less than the maximum value.
308 // Set up basic information for this network. 619 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize,
309 network_info.oui_value = NintendoOUI; 620 "Data size is too big.");
310 network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo); 621
311 622 // Set up basic information for this network.
312 connection_status.max_nodes = network_info.max_nodes; 623 network_info.oui_value = NintendoOUI;
313 624 network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo);
314 // Resize the nodes list to hold max_nodes. 625
315 node_info.resize(network_info.max_nodes); 626 connection_status.max_nodes = network_info.max_nodes;
316 627
317 // There's currently only one node in the network (the host). 628 // Resize the nodes list to hold max_nodes.
318 connection_status.total_nodes = 1; 629 node_info.resize(network_info.max_nodes);
319 network_info.total_nodes = 1; 630
320 // The host is always the first node 631 // There's currently only one node in the network (the host).
321 connection_status.network_node_id = 1; 632 connection_status.total_nodes = 1;
322 node_info[0].network_node_id = 1; 633 network_info.total_nodes = 1;
323 connection_status.nodes[0] = connection_status.network_node_id; 634 // The host is always the first node
324 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. 635 connection_status.network_node_id = 1;
325 connection_status.node_bitmask |= 1; 636 current_node.network_node_id = 1;
326 // Notify the application that the first node was set. 637 connection_status.nodes[0] = connection_status.network_node_id;
327 connection_status.changed_nodes |= 1; 638 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken.
639 connection_status.node_bitmask |= 1;
640 // Notify the application that the first node was set.
641 connection_status.changed_nodes |= 1;
642 node_info[0] = current_node;
643 }
328 644
329 // If the game has a preferred channel, use that instead. 645 // If the game has a preferred channel, use that instead.
330 if (network_info.channel != 0) 646 if (network_info.channel != 0)
@@ -361,9 +677,13 @@ static void DestroyNetwork(Interface* self) {
361 // Unschedule the beacon broadcast event. 677 // Unschedule the beacon broadcast event.
362 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); 678 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
363 679
364 // TODO(Subv): Check if connection_status is indeed reset after this call. 680 {
365 connection_status = {}; 681 std::lock_guard<std::mutex> lock(connection_status_mutex);
366 connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); 682
683 // TODO(Subv): Check if connection_status is indeed reset after this call.
684 connection_status = {};
685 connection_status.status = static_cast<u8>(NetworkStatus::NotConnected);
686 }
367 connection_status_event->Signal(); 687 connection_status_event->Signal();
368 688
369 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 689 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@@ -406,17 +726,24 @@ static void SendTo(Interface* self) {
406 726
407 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 727 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
408 728
409 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && 729 u16 network_node_id;
410 connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
411 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
412 ErrorSummary::InvalidState, ErrorLevel::Status));
413 return;
414 }
415 730
416 if (dest_node_id == connection_status.network_node_id) { 731 {
417 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, 732 std::lock_guard<std::mutex> lock(connection_status_mutex);
418 ErrorSummary::WrongArgument, ErrorLevel::Status)); 733 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) &&
419 return; 734 connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
735 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
736 ErrorSummary::InvalidState, ErrorLevel::Status));
737 return;
738 }
739
740 if (dest_node_id == connection_status.network_node_id) {
741 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS,
742 ErrorSummary::WrongArgument, ErrorLevel::Status));
743 return;
744 }
745
746 network_node_id = connection_status.network_node_id;
420 } 747 }
421 748
422 // TODO(Subv): Do something with the flags. 749 // TODO(Subv): Do something with the flags.
@@ -433,8 +760,8 @@ static void SendTo(Interface* self) {
433 760
434 // TODO(Subv): Increment the sequence number after each sent packet. 761 // TODO(Subv): Increment the sequence number after each sent packet.
435 u16 sequence_number = 0; 762 u16 sequence_number = 0;
436 std::vector<u8> data_payload = GenerateDataPayload( 763 std::vector<u8> data_payload =
437 data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); 764 GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number);
438 765
439 // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt 766 // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt
440 // and encapsulate the payload. 767 // and encapsulate the payload.
@@ -461,6 +788,7 @@ static void GetChannel(Interface* self) {
461 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); 788 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0);
462 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); 789 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
463 790
791 std::lock_guard<std::mutex> lock(connection_status_mutex);
464 bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected); 792 bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected);
465 793
466 u8 channel = is_connected ? network_channel : 0; 794 u8 channel = is_connected ? network_channel : 0;
@@ -610,37 +938,29 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
610 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) 938 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost))
611 return; 939 return;
612 940
613 // TODO(Subv): Actually send the beacon.
614 std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info); 941 std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info);
615 942
943 using Network::WifiPacket;
944 WifiPacket packet;
945 packet.type = WifiPacket::PacketType::Beacon;
946 packet.data = std::move(frame);
947 packet.destination_address = Network::BroadcastMac;
948 packet.channel = network_channel;
949
950 SendPacket(packet);
951
616 // Start broadcasting the network, send a beacon frame every 102.4ms. 952 // Start broadcasting the network, send a beacon frame every 102.4ms.
617 CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late, 953 CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late,
618 beacon_broadcast_event, 0); 954 beacon_broadcast_event, 0);
619} 955}
620 956
621/* 957/*
622 * Returns an available index in the nodes array for the
623 * currently-hosted UDS network.
624 */
625static u32 GetNextAvailableNodeId() {
626 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
627 "Can not accept clients if we're not hosting a network");
628
629 for (unsigned index = 0; index < connection_status.max_nodes; ++index) {
630 if ((connection_status.node_bitmask & (1 << index)) == 0)
631 return index;
632 }
633
634 // Any connection attempts to an already full network should have been refused.
635 ASSERT_MSG(false, "No available connection slots in the network");
636}
637
638/*
639 * Called when a client connects to an UDS network we're hosting, 958 * Called when a client connects to an UDS network we're hosting,
640 * updates the connection status and signals the update event. 959 * updates the connection status and signals the update event.
641 * @param network_node_id Network Node Id of the connecting client. 960 * @param network_node_id Network Node Id of the connecting client.
642 */ 961 */
643void OnClientConnected(u16 network_node_id) { 962void OnClientConnected(u16 network_node_id) {
963 std::lock_guard<std::mutex> lock(connection_status_mutex);
644 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), 964 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
645 "Can not accept clients if we're not hosting a network"); 965 "Can not accept clients if we're not hosting a network");
646 ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, 966 ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes,
@@ -655,7 +975,7 @@ void OnClientConnected(u16 network_node_id) {
655} 975}
656 976
657const Interface::FunctionInfo FunctionTable[] = { 977const Interface::FunctionInfo FunctionTable[] = {
658 {0x00010442, nullptr, "Initialize (deprecated)"}, 978 {0x000102C2, nullptr, "Initialize (deprecated)"},
659 {0x00020000, nullptr, "Scrap"}, 979 {0x00020000, nullptr, "Scrap"},
660 {0x00030000, Shutdown, "Shutdown"}, 980 {0x00030000, Shutdown, "Shutdown"},
661 {0x00040402, nullptr, "CreateNetwork (deprecated)"}, 981 {0x00040402, nullptr, "CreateNetwork (deprecated)"},
@@ -702,8 +1022,11 @@ NWM_UDS::~NWM_UDS() {
702 connection_status_event = nullptr; 1022 connection_status_event = nullptr;
703 recv_buffer_memory = nullptr; 1023 recv_buffer_memory = nullptr;
704 1024
705 connection_status = {}; 1025 {
706 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); 1026 std::lock_guard<std::mutex> lock(connection_status_mutex);
1027 connection_status = {};
1028 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
1029 }
707 1030
708 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); 1031 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
709} 1032}
diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h
index 141f49f9c..f1caaf974 100644
--- a/src/core/hle/service/nwm/nwm_uds.h
+++ b/src/core/hle/service/nwm/nwm_uds.h
@@ -42,6 +42,7 @@ using NodeList = std::vector<NodeInfo>;
42enum class NetworkStatus { 42enum class NetworkStatus {
43 NotConnected = 3, 43 NotConnected = 3,
44 ConnectedAsHost = 6, 44 ConnectedAsHost = 6,
45 Connecting = 7,
45 ConnectedAsClient = 9, 46 ConnectedAsClient = 9,
46 ConnectedAsSpectator = 10, 47 ConnectedAsSpectator = 10,
47}; 48};
@@ -85,6 +86,17 @@ static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wron
85static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset."); 86static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset.");
86static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); 87static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size.");
87 88
89/// Additional block tag ids in the Beacon and Association Response frames
90enum class TagId : u8 {
91 SSID = 0,
92 SupportedRates = 1,
93 DSParameterSet = 2,
94 TrafficIndicationMap = 5,
95 CountryInformation = 7,
96 ERPInformation = 42,
97 VendorSpecific = 221
98};
99
88class NWM_UDS final : public Interface { 100class NWM_UDS final : public Interface {
89public: 101public:
90 NWM_UDS(); 102 NWM_UDS();
diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp
index 6332b404c..73a80d940 100644
--- a/src/core/hle/service/nwm/uds_beacon.cpp
+++ b/src/core/hle/service/nwm/uds_beacon.cpp
@@ -243,7 +243,7 @@ std::vector<u8> GenerateNintendoFirstEncryptedDataTag(const NetworkInfo& network
243 243
244 EncryptedDataTag tag{}; 244 EncryptedDataTag tag{};
245 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); 245 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
246 tag.header.length = sizeof(tag) - sizeof(TagHeader) + payload_size; 246 tag.header.length = static_cast<u8>(sizeof(tag) - sizeof(TagHeader) + payload_size);
247 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData0); 247 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData0);
248 tag.oui = NintendoOUI; 248 tag.oui = NintendoOUI;
249 249
@@ -279,7 +279,7 @@ std::vector<u8> GenerateNintendoSecondEncryptedDataTag(const NetworkInfo& networ
279 279
280 EncryptedDataTag tag{}; 280 EncryptedDataTag tag{};
281 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); 281 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
282 tag.header.length = tag_length; 282 tag.header.length = static_cast<u8>(tag_length);
283 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData1); 283 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData1);
284 tag.oui = NintendoOUI; 284 tag.oui = NintendoOUI;
285 285
@@ -325,8 +325,5 @@ std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeL
325 return buffer; 325 return buffer;
326} 326}
327 327
328std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender) {
329 return {};
330}
331} // namespace NWM 328} // namespace NWM
332} // namespace Service 329} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_beacon.h b/src/core/hle/service/nwm/uds_beacon.h
index caacf4c6f..50cc76da2 100644
--- a/src/core/hle/service/nwm/uds_beacon.h
+++ b/src/core/hle/service/nwm/uds_beacon.h
@@ -17,17 +17,6 @@ namespace NWM {
17using MacAddress = std::array<u8, 6>; 17using MacAddress = std::array<u8, 6>;
18constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32}; 18constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32};
19 19
20/// Additional block tag ids in the Beacon frames
21enum class TagId : u8 {
22 SSID = 0,
23 SupportedRates = 1,
24 DSParameterSet = 2,
25 TrafficIndicationMap = 5,
26 CountryInformation = 7,
27 ERPInformation = 42,
28 VendorSpecific = 221
29};
30
31/** 20/**
32 * Internal vendor-specific tag ids as stored inside 21 * Internal vendor-specific tag ids as stored inside
33 * VendorSpecific blocks in the Beacon frames. 22 * VendorSpecific blocks in the Beacon frames.
@@ -135,20 +124,6 @@ struct BeaconData {
135 124
136static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size."); 125static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size.");
137 126
138/// Information about a received WiFi packet.
139/// Acts as our own 802.11 header.
140struct WifiPacket {
141 enum class PacketType { Beacon, Data };
142
143 PacketType type; ///< The type of 802.11 frame, Beacon / Data.
144
145 /// Raw 802.11 frame data, starting at the management frame header for management frames.
146 std::vector<u8> data;
147 MacAddress transmitter_address; ///< Mac address of the transmitter.
148 MacAddress destination_address; ///< Mac address of the receiver.
149 u8 channel; ///< WiFi channel where this frame was transmitted.
150};
151
152/** 127/**
153 * Decrypts the beacon data buffer for the network described by `network_info`. 128 * Decrypts the beacon data buffer for the network described by `network_info`.
154 */ 129 */
@@ -161,10 +136,5 @@ void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer)
161 */ 136 */
162std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes); 137std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes);
163 138
164/**
165 * Returns a list of received 802.11 frames from the specified sender
166 * matching the type since the last call.
167 */
168std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender);
169} // namespace NWM 139} // namespace NWM
170} // namespace Service 140} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp
new file mode 100644
index 000000000..c74f51253
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_connection.cpp
@@ -0,0 +1,88 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nwm/nwm_uds.h"
6#include "core/hle/service/nwm/uds_connection.h"
7#include "fmt/format.h"
8
9namespace Service {
10namespace NWM {
11
12// Note: These values were taken from a packet capture of an o3DS XL
13// broadcasting a Super Smash Bros. 4 lobby.
14constexpr u16 DefaultExtraCapabilities = 0x0431;
15
16std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq) {
17 AuthenticationFrame frame{};
18 frame.auth_seq = static_cast<u16>(seq);
19
20 std::vector<u8> data(sizeof(frame));
21 std::memcpy(data.data(), &frame, sizeof(frame));
22
23 return data;
24}
25
26AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body) {
27 AuthenticationFrame frame;
28 std::memcpy(&frame, body.data(), sizeof(frame));
29
30 return static_cast<AuthenticationSeq>(frame.auth_seq);
31}
32
33/**
34 * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte character representation of the
35 * specified network id as the SSID value.
36 * @param network_id The network id to use.
37 * @returns A buffer with the SSID tag.
38 */
39static std::vector<u8> GenerateSSIDTag(u32 network_id) {
40 constexpr u8 SSIDSize = 8;
41
42 struct {
43 u8 id = static_cast<u8>(TagId::SSID);
44 u8 size = SSIDSize;
45 } tag_header;
46
47 std::vector<u8> buffer(sizeof(tag_header) + SSIDSize);
48
49 std::memcpy(buffer.data(), &tag_header, sizeof(tag_header));
50
51 std::string network_name = fmt::format("{0:08X}", network_id);
52
53 std::memcpy(buffer.data() + sizeof(tag_header), network_name.c_str(), SSIDSize);
54
55 return buffer;
56}
57
58std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id) {
59 AssociationResponseFrame frame{};
60 frame.capabilities = DefaultExtraCapabilities;
61 frame.status_code = static_cast<u16>(status);
62 // The association id is ORed with this magic value (0xC000)
63 constexpr u16 AssociationIdMagic = 0xC000;
64 frame.assoc_id = association_id | AssociationIdMagic;
65
66 std::vector<u8> data(sizeof(frame));
67 std::memcpy(data.data(), &frame, sizeof(frame));
68
69 auto ssid_tag = GenerateSSIDTag(network_id);
70 data.insert(data.end(), ssid_tag.begin(), ssid_tag.end());
71
72 // TODO(Subv): Add the SupportedRates tag.
73 // TODO(Subv): Add the DSParameterSet tag.
74 // TODO(Subv): Add the ERPInformation tag.
75 return data;
76}
77
78std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body) {
79 AssociationResponseFrame frame;
80 memcpy(&frame, body.data(), sizeof(frame));
81
82 constexpr u16 AssociationIdMask = 0x3FFF;
83 return std::make_tuple(static_cast<AssocStatus>(frame.status_code),
84 frame.assoc_id & AssociationIdMask);
85}
86
87} // namespace NWM
88} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h
new file mode 100644
index 000000000..a664f8471
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_connection.h
@@ -0,0 +1,56 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <tuple>
8#include <vector>
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/service.h"
12
13namespace Service {
14namespace NWM {
15
16/// Sequence number of the 802.11 authentication frames.
17enum class AuthenticationSeq : u16 { SEQ1 = 1, SEQ2 = 2 };
18
19enum class AuthAlgorithm : u16 { OpenSystem = 0 };
20
21enum class AuthStatus : u16 { Successful = 0 };
22
23enum class AssocStatus : u16 { Successful = 0 };
24
25struct AuthenticationFrame {
26 u16_le auth_algorithm = static_cast<u16>(AuthAlgorithm::OpenSystem);
27 u16_le auth_seq;
28 u16_le status_code = static_cast<u16>(AuthStatus::Successful);
29};
30
31static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size");
32
33struct AssociationResponseFrame {
34 u16_le capabilities;
35 u16_le status_code;
36 u16_le assoc_id;
37};
38
39static_assert(sizeof(AssociationResponseFrame) == 6, "AssociationResponseFrame has wrong size");
40
41/// Generates an 802.11 authentication frame, starting at the frame body.
42std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq);
43
44/// Returns the sequence number from the body of an Authentication frame.
45AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body);
46
47/// Generates an 802.11 association response frame with the specified status, association id and
48/// network id, starting at the frame body.
49std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id);
50
51/// Returns a tuple of (association status, association id) from the body of an AssociationResponse
52/// frame.
53std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body);
54
55} // namespace NWM
56} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp
index 8c6742dba..4b389710f 100644
--- a/src/core/hle/service/nwm/uds_data.cpp
+++ b/src/core/hle/service/nwm/uds_data.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <cstring> 6#include <cstring>
6#include <cryptopp/aes.h> 7#include <cryptopp/aes.h>
7#include <cryptopp/ccm.h> 8#include <cryptopp/ccm.h>
@@ -197,7 +198,7 @@ static std::vector<u8> DecryptDataFrame(const std::vector<u8>& encrypted_payload
197 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); 198 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL);
198 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); 199 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
199 200
200 int size = df.MaxRetrievable(); 201 size_t size = df.MaxRetrievable();
201 202
202 std::vector<u8> pdata(size); 203 std::vector<u8> pdata(size);
203 df.Get(pdata.data(), size); 204 df.Get(pdata.data(), size);
@@ -251,7 +252,7 @@ static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload,
251 252
252 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); 253 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
253 254
254 int size = df.MaxRetrievable(); 255 size_t size = df.MaxRetrievable();
255 256
256 std::vector<u8> cipher(size); 257 std::vector<u8> cipher(size);
257 df.Get(cipher.data(), size); 258 df.Get(cipher.data(), size);
@@ -266,13 +267,107 @@ static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload,
266std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, 267std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
267 u16 src_node, u16 sequence_number) { 268 u16 src_node, u16 sequence_number) {
268 std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData); 269 std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData);
269 std::vector<u8> securedata_header = 270 std::vector<u8> securedata_header = GenerateSecureDataHeader(
270 GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, sequence_number); 271 static_cast<u16>(data.size()), channel, dest_node, src_node, sequence_number);
271 272
272 buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end()); 273 buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end());
273 buffer.insert(buffer.end(), data.begin(), data.end()); 274 buffer.insert(buffer.end(), data.begin(), data.end());
274 return buffer; 275 return buffer;
275} 276}
276 277
278std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) {
279 EAPoLStartPacket eapol_start{};
280 eapol_start.association_id = association_id;
281 eapol_start.node.friend_code_seed = node_info.friend_code_seed;
282
283 std::copy(node_info.username.begin(), node_info.username.end(),
284 eapol_start.node.username.begin());
285
286 // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module.
287 // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in
288 // EAPoL-Start packets from different 3DSs to the same host during a Super Smash Bros. 4 game.
289 // Find out what that means.
290
291 std::vector<u8> eapol_buffer(sizeof(EAPoLStartPacket));
292 std::memcpy(eapol_buffer.data(), &eapol_start, sizeof(eapol_start));
293
294 std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL);
295 buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end());
296 return buffer;
297}
298
299EtherType GetFrameEtherType(const std::vector<u8>& frame) {
300 LLCHeader header;
301 std::memcpy(&header, frame.data(), sizeof(header));
302
303 u16 ethertype = header.protocol;
304 return static_cast<EtherType>(ethertype);
305}
306
307u16 GetEAPoLFrameType(const std::vector<u8>& frame) {
308 // Ignore the LLC header
309 u16_be eapol_type;
310 std::memcpy(&eapol_type, frame.data() + sizeof(LLCHeader), sizeof(eapol_type));
311 return eapol_type;
312}
313
314NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame) {
315 EAPoLStartPacket eapol_start;
316
317 // Skip the LLC header
318 std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start));
319
320 NodeInfo node{};
321 node.friend_code_seed = eapol_start.node.friend_code_seed;
322
323 std::copy(eapol_start.node.username.begin(), eapol_start.node.username.end(),
324 node.username.begin());
325
326 return node;
327}
328
329NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) {
330 NodeInfo node_info{};
331 node_info.friend_code_seed = node.friend_code_seed;
332 node_info.network_node_id = node.network_node_id;
333
334 std::copy(node.username.begin(), node.username.end(), node_info.username.begin());
335
336 return node_info;
337}
338
339std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id,
340 const NodeList& nodes, u8 max_nodes, u8 total_nodes) {
341 EAPoLLogoffPacket eapol_logoff{};
342 eapol_logoff.assigned_node_id = network_node_id;
343 eapol_logoff.connected_nodes = total_nodes;
344 eapol_logoff.max_nodes = max_nodes;
345
346 for (size_t index = 0; index < total_nodes; ++index) {
347 const auto& node_info = nodes[index];
348 auto& node = eapol_logoff.nodes[index];
349
350 node.friend_code_seed = node_info.friend_code_seed;
351 node.network_node_id = node_info.network_node_id;
352
353 std::copy(node_info.username.begin(), node_info.username.end(), node.username.begin());
354 }
355
356 std::vector<u8> eapol_buffer(sizeof(EAPoLLogoffPacket));
357 std::memcpy(eapol_buffer.data(), &eapol_logoff, sizeof(eapol_logoff));
358
359 std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL);
360 buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end());
361 return buffer;
362}
363
364EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame) {
365 EAPoLLogoffPacket eapol_logoff;
366
367 // Skip the LLC header
368 std::memcpy(&eapol_logoff, frame.data() + sizeof(LLCHeader), sizeof(eapol_logoff));
369 return eapol_logoff;
370}
371
277} // namespace NWM 372} // namespace NWM
278} // namespace Service 373} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h
index a23520a41..76bccb1bf 100644
--- a/src/core/hle/service/nwm/uds_data.h
+++ b/src/core/hle/service/nwm/uds_data.h
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hle/service/nwm/uds_beacon.h"
11#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
12 13
13namespace Service { 14namespace Service {
@@ -67,6 +68,49 @@ struct DataFrameCryptoCTR {
67 68
68static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); 69static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size");
69 70
71struct EAPoLNodeInfo {
72 u64_be friend_code_seed;
73 std::array<u16_be, 10> username;
74 INSERT_PADDING_BYTES(4);
75 u16_be network_node_id;
76 INSERT_PADDING_BYTES(6);
77};
78
79static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size");
80
81constexpr u16 EAPoLStartMagic = 0x201;
82
83/*
84 * Nintendo EAPoLStartPacket, is used to initaliaze a connection between client and host
85 */
86struct EAPoLStartPacket {
87 u16_be magic = EAPoLStartMagic;
88 u16_be association_id;
89 // This value is hardcoded to 1 in the NWM module.
90 u16_be unknown = 1;
91 INSERT_PADDING_BYTES(2);
92 EAPoLNodeInfo node;
93};
94
95static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size");
96
97constexpr u16 EAPoLLogoffMagic = 0x202;
98
99struct EAPoLLogoffPacket {
100 u16_be magic = EAPoLLogoffMagic;
101 INSERT_PADDING_BYTES(2);
102 u16_be assigned_node_id;
103 MacAddress client_mac_address;
104 INSERT_PADDING_BYTES(6);
105 u8 connected_nodes;
106 u8 max_nodes;
107 INSERT_PADDING_BYTES(4);
108
109 std::array<EAPoLNodeInfo, UDSMaxNodes> nodes;
110};
111
112static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wrong size");
113
70/** 114/**
71 * Generates an unencrypted 802.11 data payload. 115 * Generates an unencrypted 802.11 data payload.
72 * @returns The generated frame payload. 116 * @returns The generated frame payload.
@@ -74,5 +118,47 @@ static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wron
74std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, 118std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
75 u16 src_node, u16 sequence_number); 119 u16 src_node, u16 sequence_number);
76 120
121/*
122 * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS
123 * communication.
124 * @returns The generated frame body.
125 */
126std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info);
127
128/*
129 * Returns the EtherType of the specified 802.11 frame.
130 */
131EtherType GetFrameEtherType(const std::vector<u8>& frame);
132
133/*
134 * Returns the EAPoL type (Start / Logoff) of the specified 802.11 frame.
135 * Note: The frame *must* be an EAPoL frame.
136 */
137u16 GetEAPoLFrameType(const std::vector<u8>& frame);
138
139/*
140 * Returns a deserialized NodeInfo structure from the information inside an EAPoL-Start packet
141 * encapsulated in an 802.11 data frame.
142 */
143NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame);
144
145/*
146 * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo.
147 */
148NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node);
149
150/*
151 * Generates an unencrypted 802.11 data frame body with the EAPoL-Logoff format for UDS
152 * communication.
153 * @returns The generated frame body.
154 */
155std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id,
156 const NodeList& nodes, u8 max_nodes, u8 total_nodes);
157
158/*
159 * Returns a EAPoLLogoffPacket representing the specified 802.11-encapsulated data frame.
160 */
161EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame);
162
77} // namespace NWM 163} // namespace NWM
78} // namespace Service 164} // namespace Service
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index aad950e50..f267aad74 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -38,7 +38,7 @@
38#include "core/hle/service/news/news.h" 38#include "core/hle/service/news/news.h"
39#include "core/hle/service/nfc/nfc.h" 39#include "core/hle/service/nfc/nfc.h"
40#include "core/hle/service/nim/nim.h" 40#include "core/hle/service/nim/nim.h"
41#include "core/hle/service/ns_s.h" 41#include "core/hle/service/ns/ns.h"
42#include "core/hle/service/nwm/nwm.h" 42#include "core/hle/service/nwm/nwm.h"
43#include "core/hle/service/pm_app.h" 43#include "core/hle/service/pm_app.h"
44#include "core/hle/service/ptm/ptm.h" 44#include "core/hle/service/ptm/ptm.h"
@@ -215,6 +215,8 @@ void Init() {
215 SM::g_service_manager = std::make_shared<SM::ServiceManager>(); 215 SM::g_service_manager = std::make_shared<SM::ServiceManager>();
216 SM::ServiceManager::InstallInterfaces(SM::g_service_manager); 216 SM::ServiceManager::InstallInterfaces(SM::g_service_manager);
217 217
218 NS::InstallInterfaces(*SM::g_service_manager);
219
218 AddNamedPort(new ERR::ERR_F); 220 AddNamedPort(new ERR::ERR_F);
219 221
220 FS::ArchiveInit(); 222 FS::ArchiveInit();
@@ -246,7 +248,6 @@ void Init() {
246 AddService(new HTTP::HTTP_C); 248 AddService(new HTTP::HTTP_C);
247 AddService(new LDR::LDR_RO); 249 AddService(new LDR::LDR_RO);
248 AddService(new MIC::MIC_U); 250 AddService(new MIC::MIC_U);
249 AddService(new NS::NS_S);
250 AddService(new PM::PM_APP); 251 AddService(new PM::PM_APP);
251 AddService(new SOC::SOC_U); 252 AddService(new SOC::SOC_U);
252 AddService(new SSL::SSL_C); 253 AddService(new SSL::SSL_C);
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 5e7fc68f9..854ab9a05 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -36,6 +36,10 @@ ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService
36 std::string name, unsigned int max_sessions) { 36 std::string name, unsigned int max_sessions) {
37 37
38 CASCADE_CODE(ValidateServiceName(name)); 38 CASCADE_CODE(ValidateServiceName(name));
39
40 if (registered_services.find(name) != registered_services.end())
41 return ERR_ALREADY_REGISTERED;
42
39 Kernel::SharedPtr<Kernel::ServerPort> server_port; 43 Kernel::SharedPtr<Kernel::ServerPort> server_port;
40 Kernel::SharedPtr<Kernel::ClientPort> client_port; 44 Kernel::SharedPtr<Kernel::ClientPort> client_port;
41 std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name); 45 std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name);
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 8f0dbf2db..9f60a7965 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -32,6 +32,9 @@ constexpr ResultCode ERR_ACCESS_DENIED(6, ErrorModule::SRV, ErrorSummary::Invali
32 ErrorLevel::Permanent); // 0xD8E06406 32 ErrorLevel::Permanent); // 0xD8E06406
33constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument, 33constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument,
34 ErrorLevel::Permanent); // 0xD9006407 34 ErrorLevel::Permanent); // 0xD9006407
35constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorDescription::AlreadyExists, ErrorModule::OS,
36 ErrorSummary::WrongArgument,
37 ErrorLevel::Permanent); // 0xD9001BFC
35 38
36class ServiceManager { 39class ServiceManager {
37public: 40public:
diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp
index 352941e69..fb873981c 100644
--- a/src/core/hle/service/sm/srv.cpp
+++ b/src/core/hle/service/sm/srv.cpp
@@ -13,6 +13,7 @@
13#include "core/hle/kernel/errors.h" 13#include "core/hle/kernel/errors.h"
14#include "core/hle/kernel/hle_ipc.h" 14#include "core/hle/kernel/hle_ipc.h"
15#include "core/hle/kernel/semaphore.h" 15#include "core/hle/kernel/semaphore.h"
16#include "core/hle/kernel/server_port.h"
16#include "core/hle/kernel/server_session.h" 17#include "core/hle/kernel/server_session.h"
17#include "core/hle/service/sm/sm.h" 18#include "core/hle/service/sm/sm.h"
18#include "core/hle/service/sm/srv.h" 19#include "core/hle/service/sm/srv.h"
@@ -61,7 +62,7 @@ void SRV::EnableNotification(Kernel::HLERequestContext& ctx) {
61 IPC::RequestParser rp(ctx, 0x2, 0, 0); 62 IPC::RequestParser rp(ctx, 0x2, 0, 0);
62 63
63 notification_semaphore = 64 notification_semaphore =
64 Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap(); 65 Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, 0, "SRV:Notification").Unwrap();
65 66
66 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); 67 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
67 rb.Push(RESULT_SUCCESS); 68 rb.Push(RESULT_SUCCESS);
@@ -184,12 +185,35 @@ void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) {
184 flags); 185 flags);
185} 186}
186 187
188void SRV::RegisterService(Kernel::HLERequestContext& ctx) {
189 IPC::RequestParser rp(ctx, 0x3, 4, 0);
190
191 auto name_buf = rp.PopRaw<std::array<char, 8>>();
192 size_t name_len = rp.Pop<u32>();
193 u32 max_sessions = rp.Pop<u32>();
194
195 std::string name(name_buf.data(), std::min(name_len, name_buf.size()));
196
197 auto port = service_manager->RegisterService(name, max_sessions);
198
199 if (port.Failed()) {
200 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
201 rb.Push(port.Code());
202 LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), port.Code().raw);
203 return;
204 }
205
206 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
207 rb.Push(RESULT_SUCCESS);
208 rb.PushObjects(port.Unwrap());
209}
210
187SRV::SRV(std::shared_ptr<ServiceManager> service_manager) 211SRV::SRV(std::shared_ptr<ServiceManager> service_manager)
188 : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) { 212 : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) {
189 static const FunctionInfo functions[] = { 213 static const FunctionInfo functions[] = {
190 {0x00010002, &SRV::RegisterClient, "RegisterClient"}, 214 {0x00010002, &SRV::RegisterClient, "RegisterClient"},
191 {0x00020000, &SRV::EnableNotification, "EnableNotification"}, 215 {0x00020000, &SRV::EnableNotification, "EnableNotification"},
192 {0x00030100, nullptr, "RegisterService"}, 216 {0x00030100, &SRV::RegisterService, "RegisterService"},
193 {0x000400C0, nullptr, "UnregisterService"}, 217 {0x000400C0, nullptr, "UnregisterService"},
194 {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"}, 218 {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"},
195 {0x000600C2, nullptr, "RegisterPort"}, 219 {0x000600C2, nullptr, "RegisterPort"},
diff --git a/src/core/hle/service/sm/srv.h b/src/core/hle/service/sm/srv.h
index 75cca5184..aad839563 100644
--- a/src/core/hle/service/sm/srv.h
+++ b/src/core/hle/service/sm/srv.h
@@ -28,6 +28,7 @@ private:
28 void Subscribe(Kernel::HLERequestContext& ctx); 28 void Subscribe(Kernel::HLERequestContext& ctx);
29 void Unsubscribe(Kernel::HLERequestContext& ctx); 29 void Unsubscribe(Kernel::HLERequestContext& ctx);
30 void PublishToSubscriber(Kernel::HLERequestContext& ctx); 30 void PublishToSubscriber(Kernel::HLERequestContext& ctx);
31 void RegisterService(Kernel::HLERequestContext& ctx);
31 32
32 std::shared_ptr<ServiceManager> service_manager; 33 std::shared_ptr<ServiceManager> service_manager;
33 Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore; 34 Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore;