diff options
| author | 2017-10-09 23:56:20 -0400 | |
|---|---|---|
| committer | 2017-10-09 23:56:20 -0400 | |
| commit | b1d5db1cf60344b6b081c9d03cb6ccc3264326cd (patch) | |
| tree | fde377c4ba3c0f92c032e6f5ec8627aae37270ef /src/core/hle | |
| parent | loader: Various improvements for NSO/NRO loaders. (diff) | |
| parent | Merge pull request #2996 from MerryMage/split-travis (diff) | |
| download | yuzu-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')
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 | ||
| 84 | void MiiSelector::Update() {} | 84 | void 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 { | |||
| 16 | namespace Applets { | 16 | namespace Applets { |
| 17 | 17 | ||
| 18 | struct MiiConfig { | 18 | struct 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 | |||
| 39 | static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size"); | 33 | static_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") |
| 43 | ASSERT_REG_POSITION(unk_008, 0x08); | 37 | ASSERT_REG_POSITION(title, 0x08); |
| 44 | ASSERT_REG_POSITION(unk_08C, 0x8C); | 38 | ASSERT_REG_POSITION(show_guest_miis, 0x8C); |
| 45 | ASSERT_REG_POSITION(unk_090, 0x90); | 39 | ASSERT_REG_POSITION(initially_selected_mii_index, 0x90); |
| 46 | ASSERT_REG_POSITION(unk_094, 0x94); | 40 | ASSERT_REG_POSITION(guest_mii_whitelist, 0x94); |
| 47 | ASSERT_REG_POSITION(unk_0FE, 0xFE); | ||
| 48 | #undef ASSERT_REG_POSITION | 41 | #undef ASSERT_REG_POSITION |
| 49 | 42 | ||
| 50 | struct MiiResult { | 43 | struct 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 | }; |
| 58 | static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size"); | 53 | static_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") |
| 62 | ASSERT_REG_POSITION(unk_0C, 0x0C); | 57 | ASSERT_REG_POSITION(selected_mii_data, 0x0C); |
| 63 | ASSERT_REG_POSITION(unk_6C, 0x6C); | 58 | ASSERT_REG_POSITION(guest_mii_name, 0x6C); |
| 64 | #undef ASSERT_REG_POSITION | 59 | #undef ASSERT_REG_POSITION |
| 65 | 60 | ||
| 66 | class MiiSelector final : public Applet { | 61 | class 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 | */ | ||
| 32 | static 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 | ||
| 30 | template <ResultCode func(u64)> | ||
| 31 | void Wrap() { | ||
| 32 | FuncReturn(func(PARAM(0)).raw); | ||
| 33 | } | ||
| 34 | |||
| 35 | template <ResultCode func(u32, u64, u32)> | ||
| 36 | void Wrap() { | ||
| 37 | FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); | ||
| 38 | } | ||
| 39 | |||
| 40 | template <ResultCode func(u64, u32)> | ||
| 41 | void Wrap() { | ||
| 42 | FuncReturn(func(PARAM(0), PARAM(1)).raw); | ||
| 43 | } | ||
| 44 | |||
| 45 | template <ResultCode func(u64, u64, u64)> | ||
| 46 | void Wrap() { | ||
| 47 | FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); | ||
| 48 | } | ||
| 49 | |||
| 50 | template <ResultCode func(u64*, u64, u64, u64)> | ||
| 51 | void Wrap() { | ||
| 52 | u64 param_1 = 0; | ||
| 53 | u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3)).raw; | ||
| 54 | Core::CPU().SetReg(1, param_1); | ||
| 55 | FuncReturn(retval); | ||
| 56 | } | ||
| 57 | |||
| 40 | template <ResultCode func(u32, u32, u32, u32)> | 58 | template <ResultCode func(u32, u32, u32, u32)> |
| 41 | void Wrap() { | 59 | void 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 | ||
| 61 | template <ResultCode func(s32*, u32*, s32, bool, s64)> | 79 | template <ResultCode func(s32*, VAddr, s32, bool, s64)> |
| 62 | void Wrap() { | 80 | void Wrap() { |
| 63 | s32 param_1 = 0; | 81 | s32 param_1 = 0; |
| 64 | s32 retval = func(¶m_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(¶m_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 | ||
| 72 | template <ResultCode func(s32*, u32*, s32, u32)> | 90 | template <ResultCode func(s32*, VAddr, s32, u32)> |
| 73 | void Wrap() { | 91 | void Wrap() { |
| 74 | s32 param_1 = 0; | 92 | s32 param_1 = 0; |
| 75 | u32 retval = | 93 | u32 retval = func(¶m_1, PARAM(1), (s32)PARAM(2), PARAM(3)).raw; |
| 76 | func(¶m_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 | ||
| 105 | template <ResultCode func(u32, u64*)> | ||
| 106 | void Wrap() { | ||
| 107 | u64 param_1 = 0; | ||
| 108 | u32 retval = func(PARAM(0), ¶m_1).raw; | ||
| 109 | Core::CPU().SetReg(1, param_1); | ||
| 110 | FuncReturn(retval); | ||
| 111 | } | ||
| 112 | |||
| 88 | template <ResultCode func(u32*)> | 113 | template <ResultCode func(u32*)> |
| 89 | void Wrap() { | 114 | void 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 | ||
| 103 | template <ResultCode func(MemoryInfo*, PageInfo*, u32)> | 128 | template <ResultCode func(MemoryInfo*, PageInfo*, u64)> |
| 104 | void Wrap() { | 129 | void 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 | ||
| 142 | template <ResultCode func(u32*, u32)> | 168 | template <ResultCode func(u32*, u64)> |
| 143 | void Wrap() { | 169 | void Wrap() { |
| 144 | u32 param_1 = 0; | 170 | u32 param_1 = 0; |
| 145 | u32 retval = func(¶m_1, PARAM(1)).raw; | 171 | u32 retval = func(¶m_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 | ||
| 155 | template <ResultCode func(s64*, u32, u32*, u32)> | ||
| 156 | void Wrap() { | ||
| 157 | FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), | ||
| 158 | (u32*)Memory::GetPointer(PARAM(2)), (s32)PARAM(3)) | ||
| 159 | .raw); | ||
| 160 | } | ||
| 161 | |||
| 162 | template <ResultCode func(u32*, const char*)> | ||
| 163 | void Wrap() { | ||
| 164 | u32 param_1 = 0; | ||
| 165 | u32 retval = func(¶m_1, (char*)Memory::GetPointer(PARAM(1))).raw; | ||
| 166 | Core::CPU().SetReg(1, param_1); | ||
| 167 | FuncReturn(retval); | ||
| 168 | } | ||
| 169 | |||
| 170 | template <ResultCode func(u32*, s32, s32)> | 181 | template <ResultCode func(u32*, s32, s32)> |
| 171 | void Wrap() { | 182 | void 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 | ||
| 225 | template <ResultCode func(Kernel::Handle*, Kernel::Handle*, const char*, u32)> | 236 | template <ResultCode func(Kernel::Handle*, Kernel::Handle*, VAddr, u32)> |
| 226 | void Wrap() { | 237 | void 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(¶m_1, ¶m_2, | 240 | u32 retval = func(¶m_1, ¶m_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 | ||
| 256 | template <ResultCode func(u32, u32, u32)> | ||
| 257 | void 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 | ||
| 271 | template <void func(const char*, int len)> | 285 | template <void func(VAddr, int len)> |
| 272 | void Wrap() { | 286 | void Wrap() { |
| 273 | func((char*)Memory::GetPointer(PARAM(0)), PARAM(1)); | 287 | func(PARAM(0), PARAM(1)); |
| 274 | } | 288 | } |
| 275 | 289 | ||
| 276 | template <void func(u8)> | 290 | template <void func(u64, u64, u64)> |
| 277 | void Wrap() { | 291 | void 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 | ||
| 125 | inline u32 StaticBufferDesc(u32 size, u8 buffer_id) { | 125 | inline 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 | ||
| 163 | inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { | 163 | inline 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 | ||
| 193 | inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) { | 193 | inline 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 | ||
| 198 | inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size, | 198 | inline 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 | ||
| 38 | u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { | 38 | u32 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 | ||
| 43 | void HLERequestContext::ClearIncomingObjects() { | 43 | void 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 | ||
| 13 | namespace Kernel { | 14 | namespace 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 | ||
| 89 | public: | 92 | public: |
| @@ -129,4 +132,4 @@ void Init(u32 system_mode); | |||
| 129 | /// Shutdown the kernel | 132 | /// Shutdown the kernel |
| 130 | void Shutdown(); | 133 | void 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 | ||
| 25 | namespace Kernel { | 24 | namespace Kernel { |
| 26 | 25 | ||
| 27 | static MemoryRegionInfo memory_regions[3]; | 26 | MemoryRegionInfo 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 | ||
| 99 | std::array<u8, Memory::VRAM_SIZE> vram; | ||
| 100 | std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram; | ||
| 101 | |||
| 102 | void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { | 98 | void 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 | ||
| 27 | void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); | 27 | void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); |
| 28 | void MapSharedPages(VMManager& address_space); | 28 | void MapSharedPages(VMManager& address_space); |
| 29 | |||
| 30 | extern 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) { | |||
| 25 | Mutex::Mutex() {} | 25 | Mutex::Mutex() {} |
| 26 | Mutex::~Mutex() {} | 26 | Mutex::~Mutex() {} |
| 27 | 27 | ||
| 28 | SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { | 28 | SharedPtr<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 | ||
| 135 | void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { | 136 | void 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 | ||
| 64 | s32 ResourceLimit::GetMaxResourceValue(u32 resource) const { | 64 | u32 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 { | |||
| 13 | Semaphore::Semaphore() {} | 13 | Semaphore::Semaphore() {} |
| 14 | Semaphore::~Semaphore() {} | 14 | Semaphore::~Semaphore() {} |
| 15 | 15 | ||
| 16 | ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, | 16 | ResultVal<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 | ||
| 112 | Thread* ArbitrateHighestPriorityThread(u32 address) { | 112 | Thread* 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 | ||
| 354 | ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority, | 371 | ResultVal<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 | ||
| 450 | void Thread::SetPriority(s32 priority) { | 467 | void 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 | ||
| 462 | void Thread::UpdatePriority() { | 479 | void 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 | ||
| 471 | void Thread::BoostPriority(s32 priority) { | 488 | void 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 | ||
| 480 | SharedPtr<Thread> SetupMainThread(VAddr entry_point, s32 priority) { | 497 | SharedPtr<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) { | |||
| 525 | s32 Thread::GetWaitObjectIndex(WaitObject* object) const { | 541 | s32 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 | |||
| 547 | VAddr 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 | ||
| 18 | enum ThreadPriority : s32 { | 18 | enum 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 | ||
| 44 | enum 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 | |||
| 44 | namespace Kernel { | 49 | namespace Kernel { |
| 45 | 50 | ||
| 46 | class Mutex; | 51 | class 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 | |||
| 208 | private: | 225 | private: |
| 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 | */ |
| 219 | SharedPtr<Thread> SetupMainThread(VAddr entry_point, s32 priority); | 237 | SharedPtr<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 | */ |
| 277 | const std::vector<SharedPtr<Thread>>& GetThreadList(); | 295 | const 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) { | |||
| 328 | void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { | 338 | void 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 | ||
| 14 | namespace Kernel { | 15 | namespace 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 | */ |
| 104 | class VMManager final { | 105 | class VMManager final { |
| 105 | // TODO(yuriks): Make page tables switchable to support multiple VMManagers | ||
| 106 | public: | 106 | public: |
| 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 | |||
| 187 | private: | 191 | private: |
| 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 | ||
| 35 | SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | 35 | SharedPtr<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 | |||
| 9 | namespace HLE { | ||
| 10 | std::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 | |||
| 9 | namespace 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 | */ | ||
| 17 | extern 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; | |||
| 33 | static bool shared_font_relocated = false; | 35 | static bool shared_font_relocated = false; |
| 34 | 36 | ||
| 35 | static Kernel::SharedPtr<Kernel::Mutex> lock; | 37 | static Kernel::SharedPtr<Kernel::Mutex> lock; |
| 36 | static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event | ||
| 37 | static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event | ||
| 38 | 38 | ||
| 39 | static u32 cpu_percent; ///< CPU time available to the running application | 39 | static u32 cpu_percent; ///< CPU time available to the running application |
| 40 | 40 | ||
| @@ -43,43 +43,344 @@ static u8 unknown_ns_state_field; | |||
| 43 | 43 | ||
| 44 | static ScreencapPostPermission screen_capture_post_permission; | 44 | static 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. |
| 47 | static MessageParameter next_parameter; | 47 | /// TODO(Subv): Use std::optional once we migrate to C++17. |
| 48 | static boost::optional<MessageParameter> next_parameter; | ||
| 49 | |||
| 50 | enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 }; | ||
| 51 | |||
| 52 | static constexpr size_t NumAppletSlot = 4; | ||
| 53 | |||
| 54 | enum class AppletSlot : u8 { | ||
| 55 | Application, | ||
| 56 | SystemApplet, | ||
| 57 | HomeMenu, | ||
| 58 | LibraryApplet, | ||
| 59 | |||
| 60 | // An invalid tag | ||
| 61 | Error, | ||
| 62 | }; | ||
| 63 | |||
| 64 | union 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 | |||
| 74 | struct 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. | ||
| 84 | static std::array<AppletSlotData, NumAppletSlot> applet_slots = {}; | ||
| 85 | |||
| 86 | // This overload returns nullptr if no applet with the specified id has been started. | ||
| 87 | static 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 | |||
| 147 | static 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 | ||
| 49 | void SendParameter(const MessageParameter& parameter) { | 170 | void 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 | ||
| 55 | void Initialize(Service::Interface* self) { | 183 | void 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. | 225 | static 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 | |||
| 279 | static 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 | |||
| 344 | static 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 | ||
| 74 | void GetSharedFont(Service::Interface* self) { | 362 | void 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) { | |||
| 128 | void Enable(Service::Interface* self) { | 434 | void 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 | ||
| 137 | void GetAppletManInfo(Service::Interface* self) { | 455 | void GetAppletManInfo(Service::Interface* self) { |
| @@ -149,22 +467,27 @@ void GetAppletManInfo(Service::Interface* self) { | |||
| 149 | 467 | ||
| 150 | void IsRegistered(Service::Interface* self) { | 468 | void 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 | ||
| 170 | void InquireNotification(Service::Interface* self) { | 493 | void 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 | ||
| 216 | void ReceiveParameter(Service::Interface* self) { | 544 | void 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 | ||
| 246 | void GlanceParameter(Service::Interface* self) { | 593 | void 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 | ||
| 276 | void CancelParameter(Service::Interface* self) { | 643 | void 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 | ||
| 292 | void PrepareToStartApplication(Service::Interface* self) { | 676 | void 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 | |||
| 785 | void 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 | ||
| 397 | void PreloadLibraryApplet(Service::Interface* self) { | 799 | void 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 | ||
| 412 | void StartLibraryApplet(Service::Interface* self) { | 818 | void 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 | ||
| 439 | void CancelLibraryApplet(Service::Interface* self) { | 852 | void 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 | ||
| 650 | static 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 | |||
| 704 | static 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 | |||
| 751 | static 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 | |||
| 769 | void Init() { | 1063 | void 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 | ||
| 807 | void Shutdown() { | 1106 | void 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 |
| 74 | enum class AppletId : u32 { | 74 | enum 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 | ||
| 122 | namespace ErrCodes { | ||
| 123 | enum { | ||
| 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 |
| 120 | void SendParameter(const MessageParameter& parameter); | 130 | void SendParameter(const MessageParameter& parameter); |
| 121 | 131 | ||
| @@ -410,6 +420,16 @@ void GetAppCpuTimeLimit(Service::Interface* self); | |||
| 410 | void PrepareToStartLibraryApplet(Service::Interface* self); | 420 | void 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 | */ | ||
| 430 | void 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 | ||
| 142 | void GetCountryCodeID(Service::Interface* self) { | 142 | void 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 | ||
| 171 | static u32 GetRegionValue() { | 171 | u32 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 | */ |
| 102 | void GetCountryCodeID(Service::Interface* self); | 102 | void GetCountryCodeID(Service::Interface* self); |
| 103 | 103 | ||
| 104 | u32 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 { | |||
| 8 | namespace DLP { | 8 | namespace DLP { |
| 9 | 9 | ||
| 10 | const Interface::FunctionInfo FunctionTable[] = { | 10 | const 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 | ||
| 14 | DLP_CLNT_Interface::DLP_CLNT_Interface() { | 33 | DLP_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 { | |||
| 8 | namespace DLP { | 8 | namespace DLP { |
| 9 | 9 | ||
| 10 | const Interface::FunctionInfo FunctionTable[] = { | 10 | const 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 | ||
| 14 | DLP_FKCL_Interface::DLP_FKCL_Interface() { | 30 | DLP_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 @@ | |||
| 11 | namespace Service { | 11 | namespace Service { |
| 12 | namespace DLP { | 12 | namespace DLP { |
| 13 | 13 | ||
| 14 | static void unk_0x000E0040(Interface* self) { | 14 | static 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 | ||
| 37 | DLP_SRVR_Interface::DLP_SRVR_Interface() { | 42 | DLP_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 | ||
| 53 | static constexpr Kernel::Handle INVALID_HANDLE{}; | 54 | static 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 | |||
| 573 | void 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 | ||
| 569 | void UnregisterArchiveTypes() { | 585 | void 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 |
| 22 | static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"}; | 22 | static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"}; |
| 23 | 23 | ||
| 24 | namespace Loader { | ||
| 25 | class AppLoader; | ||
| 26 | } | ||
| 27 | |||
| 24 | namespace Service { | 28 | namespace Service { |
| 25 | namespace FS { | 29 | namespace FS { |
| 26 | 30 | ||
| @@ -259,6 +263,9 @@ void ArchiveInit(); | |||
| 259 | /// Shutdown archives | 263 | /// Shutdown archives |
| 260 | void ArchiveShutdown(); | 264 | void ArchiveShutdown(); |
| 261 | 265 | ||
| 266 | /// Registers a new NCCH file with the SelfNCCH archive factory | ||
| 267 | void RegisterSelfNCCH(Loader::AppLoader& app_loader); | ||
| 268 | |||
| 262 | /// Register all archive types | 269 | /// Register all archive types |
| 263 | void RegisterArchiveTypes(); | 270 | void 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 | ||
| 23 | namespace Service { | 23 | namespace Service { |
| 24 | namespace HID { | 24 | namespace HID { |
| @@ -50,10 +50,15 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234; | |||
| 50 | constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; | 50 | constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; |
| 51 | constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; | 51 | constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; |
| 52 | 52 | ||
| 53 | constexpr float accelerometer_coef = 512.0f; // measured from hw test result | ||
| 54 | constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call | ||
| 55 | |||
| 53 | static std::atomic<bool> is_device_reload_pending; | 56 | static std::atomic<bool> is_device_reload_pending; |
| 54 | static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> | 57 | static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> |
| 55 | buttons; | 58 | buttons; |
| 56 | static std::unique_ptr<Input::AnalogDevice> circle_pad; | 59 | static std::unique_ptr<Input::AnalogDevice> circle_pad; |
| 60 | static std::unique_ptr<Input::MotionDevice> motion_device; | ||
| 61 | static std::unique_ptr<Input::TouchDevice> touch_device; | ||
| 57 | 62 | ||
| 58 | DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { | 63 | DirectionState 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 | ||
| 95 | static void UnloadInputDevices() { | 102 | static 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 | ||
| 102 | static void UpdatePadCallback(u64 userdata, int cycles_late) { | 111 | static 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 | */ |
| 25 | struct PadState { | 25 | struct 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 { | |||
| 18 | namespace IR { | 18 | namespace IR { |
| 19 | 19 | ||
| 20 | union PadState { | 20 | union 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 @@ | |||
| 14 | namespace Service { | 16 | namespace Service { |
| 15 | namespace NIM { | 17 | namespace NIM { |
| 16 | 18 | ||
| 19 | static Kernel::SharedPtr<Kernel::Event> nim_system_update_event; | ||
| 20 | |||
| 21 | void 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 | |||
| 17 | void CheckSysUpdateAvailable(Service::Interface* self) { | 29 | void 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 | ||
| 34 | void Shutdown() {} | 48 | void 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; | |||
| 11 | namespace NIM { | 11 | namespace 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 | */ | ||
| 22 | void 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 | |||
| 8 | namespace Service { | ||
| 9 | namespace NS { | ||
| 10 | |||
| 11 | void 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 | |||
| 9 | namespace Service { | ||
| 10 | namespace NS { | ||
| 11 | |||
| 12 | /// Registers all NS services with the specified service manager. | ||
| 13 | void 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 | |||
| 7 | namespace Service { | ||
| 8 | namespace NS { | ||
| 9 | |||
| 10 | NS_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 | |||
| 31 | NS_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 | ||
| 9 | namespace Service { | 10 | namespace Service { |
| 10 | namespace NS { | 11 | namespace NS { |
| 11 | 12 | ||
| 12 | class NS_S final : public Interface { | 13 | /// Interface to "ns:s" service |
| 14 | class NS_S final : public ServiceFramework<NS_S> { | ||
| 13 | public: | 15 | public: |
| 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 | |||
| 7 | namespace Service { | ||
| 8 | namespace NS { | ||
| 9 | |||
| 10 | const 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 | |||
| 28 | NS_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 | ||
| 21 | namespace Service { | 27 | namespace Service { |
| 22 | namespace NWM { | 28 | namespace 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 | */ |
| 39 | static NodeList node_info(1); | 45 | static NodeList node_info; |
| 46 | |||
| 47 | // Node information about our own system. | ||
| 48 | static 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. |
| 42 | static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; | 51 | static 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. |
| 52 | static int beacon_broadcast_event; | 61 | static int beacon_broadcast_event; |
| 53 | 62 | ||
| 63 | // Mutex to synchronize access to the connection status between the emulation thread and the | ||
| 64 | // network thread. | ||
| 65 | static 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. | ||
| 69 | static 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. | ||
| 73 | constexpr size_t MaxBeaconFrames = 15; | ||
| 74 | |||
| 75 | // List of the last <MaxBeaconFrames> beacons received from the network. | ||
| 76 | static 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 | */ | ||
| 81 | std::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. | ||
| 100 | void 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 | */ | ||
| 108 | static 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. | ||
| 120 | void 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 | |||
| 139 | void 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 | |||
| 161 | static 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 | */ | ||
| 249 | void 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 | ||
| 269 | void 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 | */ | ||
| 300 | void 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 | |||
| 326 | static 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. | ||
| 338 | void 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 | */ | ||
| 625 | static 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 | */ |
| 643 | void OnClientConnected(u16 network_node_id) { | 962 | void 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 | ||
| 657 | const Interface::FunctionInfo FunctionTable[] = { | 977 | const 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>; | |||
| 42 | enum class NetworkStatus { | 42 | enum 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 | |||
| 85 | static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset."); | 86 | static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset."); |
| 86 | static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); | 87 | static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); |
| 87 | 88 | ||
| 89 | /// Additional block tag ids in the Beacon and Association Response frames | ||
| 90 | enum 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 | |||
| 88 | class NWM_UDS final : public Interface { | 100 | class NWM_UDS final : public Interface { |
| 89 | public: | 101 | public: |
| 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 | ||
| 328 | std::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 { | |||
| 17 | using MacAddress = std::array<u8, 6>; | 17 | using MacAddress = std::array<u8, 6>; |
| 18 | constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32}; | 18 | constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32}; |
| 19 | 19 | ||
| 20 | /// Additional block tag ids in the Beacon frames | ||
| 21 | enum 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 | ||
| 136 | static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size."); | 125 | static_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. | ||
| 140 | struct 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 | */ |
| 162 | std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes); | 137 | std::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 | */ | ||
| 168 | std::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 | |||
| 9 | namespace Service { | ||
| 10 | namespace NWM { | ||
| 11 | |||
| 12 | // Note: These values were taken from a packet capture of an o3DS XL | ||
| 13 | // broadcasting a Super Smash Bros. 4 lobby. | ||
| 14 | constexpr u16 DefaultExtraCapabilities = 0x0431; | ||
| 15 | |||
| 16 | std::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 | |||
| 26 | AuthenticationSeq 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 | */ | ||
| 39 | static 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 | |||
| 58 | std::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 | |||
| 78 | std::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 | |||
| 13 | namespace Service { | ||
| 14 | namespace NWM { | ||
| 15 | |||
| 16 | /// Sequence number of the 802.11 authentication frames. | ||
| 17 | enum class AuthenticationSeq : u16 { SEQ1 = 1, SEQ2 = 2 }; | ||
| 18 | |||
| 19 | enum class AuthAlgorithm : u16 { OpenSystem = 0 }; | ||
| 20 | |||
| 21 | enum class AuthStatus : u16 { Successful = 0 }; | ||
| 22 | |||
| 23 | enum class AssocStatus : u16 { Successful = 0 }; | ||
| 24 | |||
| 25 | struct 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 | |||
| 31 | static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size"); | ||
| 32 | |||
| 33 | struct AssociationResponseFrame { | ||
| 34 | u16_le capabilities; | ||
| 35 | u16_le status_code; | ||
| 36 | u16_le assoc_id; | ||
| 37 | }; | ||
| 38 | |||
| 39 | static_assert(sizeof(AssociationResponseFrame) == 6, "AssociationResponseFrame has wrong size"); | ||
| 40 | |||
| 41 | /// Generates an 802.11 authentication frame, starting at the frame body. | ||
| 42 | std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq); | ||
| 43 | |||
| 44 | /// Returns the sequence number from the body of an Authentication frame. | ||
| 45 | AuthenticationSeq 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. | ||
| 49 | std::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. | ||
| 53 | std::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, | |||
| 266 | std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, | 267 | std::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 | ||
| 278 | std::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 | |||
| 299 | EtherType 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 | |||
| 307 | u16 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 | |||
| 314 | NodeInfo 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 | |||
| 329 | NodeInfo 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 | |||
| 339 | std::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 | |||
| 364 | EAPoLLogoffPacket 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 | ||
| 13 | namespace Service { | 14 | namespace Service { |
| @@ -67,6 +68,49 @@ struct DataFrameCryptoCTR { | |||
| 67 | 68 | ||
| 68 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); | 69 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); |
| 69 | 70 | ||
| 71 | struct 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 | |||
| 79 | static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size"); | ||
| 80 | |||
| 81 | constexpr u16 EAPoLStartMagic = 0x201; | ||
| 82 | |||
| 83 | /* | ||
| 84 | * Nintendo EAPoLStartPacket, is used to initaliaze a connection between client and host | ||
| 85 | */ | ||
| 86 | struct 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 | |||
| 95 | static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); | ||
| 96 | |||
| 97 | constexpr u16 EAPoLLogoffMagic = 0x202; | ||
| 98 | |||
| 99 | struct 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 | |||
| 112 | static_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 | |||
| 74 | std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, | 118 | std::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 | */ | ||
| 126 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); | ||
| 127 | |||
| 128 | /* | ||
| 129 | * Returns the EtherType of the specified 802.11 frame. | ||
| 130 | */ | ||
| 131 | EtherType 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 | */ | ||
| 137 | u16 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 | */ | ||
| 143 | NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame); | ||
| 144 | |||
| 145 | /* | ||
| 146 | * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo. | ||
| 147 | */ | ||
| 148 | NodeInfo 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 | */ | ||
| 155 | std::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 | */ | ||
| 161 | EAPoLLogoffPacket 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 |
| 33 | constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument, | 33 | constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument, |
| 34 | ErrorLevel::Permanent); // 0xD9006407 | 34 | ErrorLevel::Permanent); // 0xD9006407 |
| 35 | constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorDescription::AlreadyExists, ErrorModule::OS, | ||
| 36 | ErrorSummary::WrongArgument, | ||
| 37 | ErrorLevel::Permanent); // 0xD9001BFC | ||
| 35 | 38 | ||
| 36 | class ServiceManager { | 39 | class ServiceManager { |
| 37 | public: | 40 | public: |
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 | ||
| 188 | void 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 | |||
| 187 | SRV::SRV(std::shared_ptr<ServiceManager> service_manager) | 211 | SRV::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; |