diff options
| author | 2014-12-29 19:47:41 -0800 | |
|---|---|---|
| committer | 2014-12-29 19:47:41 -0800 | |
| commit | 8ba9ac0f74abb0408a26207a76a0c1808bad8de0 (patch) | |
| tree | f1c7c3393fa726435b5b90bf335567c93e528ef1 /src/core/hle/kernel | |
| parent | Add comment regarding __WIN32__ in SkyEye code (diff) | |
| parent | Merge pull request #367 from bunnei/usat_ssat (diff) | |
| download | yuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.tar.gz yuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.tar.xz yuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.zip | |
Fix merge conflicts
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/address_arbiter.cpp | 19 | ||||
| -rw-r--r-- | src/core/hle/kernel/address_arbiter.h | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/archive.cpp | 429 | ||||
| -rw-r--r-- | src/core/hle/kernel/archive.h | 85 | ||||
| -rw-r--r-- | src/core/hle/kernel/event.cpp | 17 | ||||
| -rw-r--r-- | src/core/hle/kernel/event.h | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 139 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 217 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 108 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.h | 8 | ||||
| -rw-r--r-- | src/core/hle/kernel/semaphore.cpp | 95 | ||||
| -rw-r--r-- | src/core/hle/kernel/semaphore.h | 32 | ||||
| -rw-r--r-- | src/core/hle/kernel/session.h | 58 | ||||
| -rw-r--r-- | src/core/hle/kernel/shared_memory.cpp | 23 | ||||
| -rw-r--r-- | src/core/hle/kernel/shared_memory.h | 16 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 136 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 22 |
17 files changed, 604 insertions, 804 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index db571b895..38705e3cd 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 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 "common/common_types.h" | 5 | #include "common/common_types.h" |
| @@ -20,16 +20,10 @@ public: | |||
| 20 | std::string GetTypeName() const override { return "Arbiter"; } | 20 | std::string GetTypeName() const override { return "Arbiter"; } |
| 21 | std::string GetName() const override { return name; } | 21 | std::string GetName() const override { return name; } |
| 22 | 22 | ||
| 23 | static Kernel::HandleType GetStaticHandleType() { return HandleType::AddressArbiter; } | 23 | static const HandleType HANDLE_TYPE = HandleType::AddressArbiter; |
| 24 | Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; } | 24 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 25 | 25 | ||
| 26 | std::string name; ///< Name of address arbiter object (optional) | 26 | std::string name; ///< Name of address arbiter object (optional) |
| 27 | |||
| 28 | ResultVal<bool> WaitSynchronization() override { | ||
| 29 | // TODO(bunnei): ImplementMe | ||
| 30 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 31 | return UnimplementedFunction(ErrorModule::OS); | ||
| 32 | } | ||
| 33 | }; | 27 | }; |
| 34 | 28 | ||
| 35 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 29 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -53,13 +47,13 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 | |||
| 53 | // Wait current thread (acquire the arbiter)... | 47 | // Wait current thread (acquire the arbiter)... |
| 54 | case ArbitrationType::WaitIfLessThan: | 48 | case ArbitrationType::WaitIfLessThan: |
| 55 | if ((s32)Memory::Read32(address) <= value) { | 49 | if ((s32)Memory::Read32(address) <= value) { |
| 56 | Kernel::WaitCurrentThread(WAITTYPE_ARB, handle); | 50 | Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); |
| 57 | HLE::Reschedule(__func__); | 51 | HLE::Reschedule(__func__); |
| 58 | } | 52 | } |
| 59 | break; | 53 | break; |
| 60 | 54 | ||
| 61 | default: | 55 | default: |
| 62 | ERROR_LOG(KERNEL, "unknown type=%d", type); | 56 | LOG_ERROR(Kernel, "unknown type=%d", type); |
| 63 | return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage); | 57 | return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage); |
| 64 | } | 58 | } |
| 65 | return RESULT_SUCCESS; | 59 | return RESULT_SUCCESS; |
| @@ -68,7 +62,8 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 | |||
| 68 | /// Create an address arbiter | 62 | /// Create an address arbiter |
| 69 | AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) { | 63 | AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) { |
| 70 | AddressArbiter* address_arbiter = new AddressArbiter; | 64 | AddressArbiter* address_arbiter = new AddressArbiter; |
| 71 | handle = Kernel::g_object_pool.Create(address_arbiter); | 65 | // TOOD(yuriks): Fix error reporting |
| 66 | handle = Kernel::g_handle_table.Create(address_arbiter).ValueOr(INVALID_HANDLE); | ||
| 72 | address_arbiter->name = name; | 67 | address_arbiter->name = name; |
| 73 | return address_arbiter; | 68 | return address_arbiter; |
| 74 | } | 69 | } |
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 8a5fb10b4..030e7ad7b 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 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 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp deleted file mode 100644 index e273444c9..000000000 --- a/src/core/hle/kernel/archive.cpp +++ /dev/null | |||
| @@ -1,429 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "common/file_util.h" | ||
| 7 | #include "common/math_util.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/archive.h" | ||
| 10 | #include "core/file_sys/archive_sdmc.h" | ||
| 11 | #include "core/file_sys/directory.h" | ||
| 12 | #include "core/hle/kernel/archive.h" | ||
| 13 | #include "core/hle/result.h" | ||
| 14 | #include "core/hle/service/service.h" | ||
| 15 | |||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 17 | // Kernel namespace | ||
| 18 | |||
| 19 | namespace Kernel { | ||
| 20 | |||
| 21 | // Command to access archive file | ||
| 22 | enum class FileCommand : u32 { | ||
| 23 | Dummy1 = 0x000100C6, | ||
| 24 | Control = 0x040100C4, | ||
| 25 | OpenSubFile = 0x08010100, | ||
| 26 | Read = 0x080200C2, | ||
| 27 | Write = 0x08030102, | ||
| 28 | GetSize = 0x08040000, | ||
| 29 | SetSize = 0x08050080, | ||
| 30 | GetAttributes = 0x08060000, | ||
| 31 | SetAttributes = 0x08070040, | ||
| 32 | Close = 0x08080000, | ||
| 33 | Flush = 0x08090000, | ||
| 34 | }; | ||
| 35 | |||
| 36 | // Command to access directory | ||
| 37 | enum class DirectoryCommand : u32 { | ||
| 38 | Dummy1 = 0x000100C6, | ||
| 39 | Control = 0x040100C4, | ||
| 40 | Read = 0x08010042, | ||
| 41 | Close = 0x08020000, | ||
| 42 | }; | ||
| 43 | |||
| 44 | class Archive : public Object { | ||
| 45 | public: | ||
| 46 | std::string GetTypeName() const override { return "Archive"; } | ||
| 47 | std::string GetName() const override { return name; } | ||
| 48 | |||
| 49 | static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; } | ||
| 50 | Kernel::HandleType GetHandleType() const override { return HandleType::Archive; } | ||
| 51 | |||
| 52 | std::string name; ///< Name of archive (optional) | ||
| 53 | FileSys::Archive* backend; ///< Archive backend interface | ||
| 54 | |||
| 55 | ResultVal<bool> SyncRequest() override { | ||
| 56 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 57 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 58 | |||
| 59 | switch (cmd) { | ||
| 60 | // Read from archive... | ||
| 61 | case FileCommand::Read: | ||
| 62 | { | ||
| 63 | u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 64 | u32 length = cmd_buff[3]; | ||
| 65 | u32 address = cmd_buff[5]; | ||
| 66 | |||
| 67 | // Number of bytes read | ||
| 68 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | ||
| 69 | break; | ||
| 70 | } | ||
| 71 | // Write to archive... | ||
| 72 | case FileCommand::Write: | ||
| 73 | { | ||
| 74 | u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 75 | u32 length = cmd_buff[3]; | ||
| 76 | u32 flush = cmd_buff[4]; | ||
| 77 | u32 address = cmd_buff[6]; | ||
| 78 | |||
| 79 | // Number of bytes written | ||
| 80 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | case FileCommand::GetSize: | ||
| 84 | { | ||
| 85 | u64 filesize = (u64) backend->GetSize(); | ||
| 86 | cmd_buff[2] = (u32) filesize; // Lower word | ||
| 87 | cmd_buff[3] = (u32) (filesize >> 32); // Upper word | ||
| 88 | break; | ||
| 89 | } | ||
| 90 | case FileCommand::SetSize: | ||
| 91 | { | ||
| 92 | backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32)); | ||
| 93 | break; | ||
| 94 | } | ||
| 95 | case FileCommand::Close: | ||
| 96 | { | ||
| 97 | DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 98 | CloseArchive(backend->GetIdCode()); | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | // Unknown command... | ||
| 102 | default: | ||
| 103 | { | ||
| 104 | ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); | ||
| 105 | return UnimplementedFunction(ErrorModule::FS); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | cmd_buff[1] = 0; // No error | ||
| 109 | return MakeResult<bool>(false); | ||
| 110 | } | ||
| 111 | |||
| 112 | ResultVal<bool> WaitSynchronization() override { | ||
| 113 | // TODO(bunnei): ImplementMe | ||
| 114 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 115 | return UnimplementedFunction(ErrorModule::FS); | ||
| 116 | } | ||
| 117 | }; | ||
| 118 | |||
| 119 | class File : public Object { | ||
| 120 | public: | ||
| 121 | std::string GetTypeName() const override { return "File"; } | ||
| 122 | std::string GetName() const override { return path.DebugStr(); } | ||
| 123 | |||
| 124 | static Kernel::HandleType GetStaticHandleType() { return HandleType::File; } | ||
| 125 | Kernel::HandleType GetHandleType() const override { return HandleType::File; } | ||
| 126 | |||
| 127 | FileSys::Path path; ///< Path of the file | ||
| 128 | std::unique_ptr<FileSys::File> backend; ///< File backend interface | ||
| 129 | |||
| 130 | ResultVal<bool> SyncRequest() override { | ||
| 131 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 132 | FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||
| 133 | switch (cmd) { | ||
| 134 | |||
| 135 | // Read from file... | ||
| 136 | case FileCommand::Read: | ||
| 137 | { | ||
| 138 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 139 | u32 length = cmd_buff[3]; | ||
| 140 | u32 address = cmd_buff[5]; | ||
| 141 | DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x", | ||
| 142 | GetTypeName().c_str(), GetName().c_str(), offset, length, address); | ||
| 143 | cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | |||
| 147 | // Write to file... | ||
| 148 | case FileCommand::Write: | ||
| 149 | { | ||
| 150 | u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; | ||
| 151 | u32 length = cmd_buff[3]; | ||
| 152 | u32 flush = cmd_buff[4]; | ||
| 153 | u32 address = cmd_buff[6]; | ||
| 154 | DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", | ||
| 155 | GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); | ||
| 156 | cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); | ||
| 157 | break; | ||
| 158 | } | ||
| 159 | |||
| 160 | case FileCommand::GetSize: | ||
| 161 | { | ||
| 162 | DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 163 | u64 size = backend->GetSize(); | ||
| 164 | cmd_buff[2] = (u32)size; | ||
| 165 | cmd_buff[3] = size >> 32; | ||
| 166 | break; | ||
| 167 | } | ||
| 168 | |||
| 169 | case FileCommand::SetSize: | ||
| 170 | { | ||
| 171 | u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); | ||
| 172 | DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu", | ||
| 173 | GetTypeName().c_str(), GetName().c_str(), size); | ||
| 174 | backend->SetSize(size); | ||
| 175 | break; | ||
| 176 | } | ||
| 177 | |||
| 178 | case FileCommand::Close: | ||
| 179 | { | ||
| 180 | DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 181 | Kernel::g_object_pool.Destroy<File>(GetHandle()); | ||
| 182 | break; | ||
| 183 | } | ||
| 184 | |||
| 185 | // Unknown command... | ||
| 186 | default: | ||
| 187 | ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); | ||
| 188 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 189 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 190 | return error; | ||
| 191 | } | ||
| 192 | cmd_buff[1] = 0; // No error | ||
| 193 | return MakeResult<bool>(false); | ||
| 194 | } | ||
| 195 | |||
| 196 | ResultVal<bool> WaitSynchronization() override { | ||
| 197 | // TODO(bunnei): ImplementMe | ||
| 198 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 199 | return UnimplementedFunction(ErrorModule::FS); | ||
| 200 | } | ||
| 201 | }; | ||
| 202 | |||
| 203 | class Directory : public Object { | ||
| 204 | public: | ||
| 205 | std::string GetTypeName() const override { return "Directory"; } | ||
| 206 | std::string GetName() const override { return path.DebugStr(); } | ||
| 207 | |||
| 208 | static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; } | ||
| 209 | Kernel::HandleType GetHandleType() const override { return HandleType::Directory; } | ||
| 210 | |||
| 211 | FileSys::Path path; ///< Path of the directory | ||
| 212 | std::unique_ptr<FileSys::Directory> backend; ///< File backend interface | ||
| 213 | |||
| 214 | ResultVal<bool> SyncRequest() override { | ||
| 215 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 216 | DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); | ||
| 217 | switch (cmd) { | ||
| 218 | |||
| 219 | // Read from directory... | ||
| 220 | case DirectoryCommand::Read: | ||
| 221 | { | ||
| 222 | u32 count = cmd_buff[1]; | ||
| 223 | u32 address = cmd_buff[3]; | ||
| 224 | auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address)); | ||
| 225 | DEBUG_LOG(KERNEL, "Read %s %s: count=%d", | ||
| 226 | GetTypeName().c_str(), GetName().c_str(), count); | ||
| 227 | |||
| 228 | // Number of entries actually read | ||
| 229 | cmd_buff[2] = backend->Read(count, entries); | ||
| 230 | break; | ||
| 231 | } | ||
| 232 | |||
| 233 | case DirectoryCommand::Close: | ||
| 234 | { | ||
| 235 | DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); | ||
| 236 | Kernel::g_object_pool.Destroy<Directory>(GetHandle()); | ||
| 237 | break; | ||
| 238 | } | ||
| 239 | |||
| 240 | // Unknown command... | ||
| 241 | default: | ||
| 242 | ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); | ||
| 243 | ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||
| 244 | cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. | ||
| 245 | return error; | ||
| 246 | } | ||
| 247 | cmd_buff[1] = 0; // No error | ||
| 248 | return MakeResult<bool>(false); | ||
| 249 | } | ||
| 250 | |||
| 251 | ResultVal<bool> WaitSynchronization() override { | ||
| 252 | // TODO(bunnei): ImplementMe | ||
| 253 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 254 | return UnimplementedFunction(ErrorModule::FS); | ||
| 255 | } | ||
| 256 | }; | ||
| 257 | |||
| 258 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 259 | |||
| 260 | std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode | ||
| 261 | |||
| 262 | ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code) { | ||
| 263 | auto itr = g_archive_map.find(id_code); | ||
| 264 | if (itr == g_archive_map.end()) { | ||
| 265 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 266 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 267 | } | ||
| 268 | |||
| 269 | return MakeResult<Handle>(itr->second); | ||
| 270 | } | ||
| 271 | |||
| 272 | ResultCode CloseArchive(FileSys::Archive::IdCode id_code) { | ||
| 273 | auto itr = g_archive_map.find(id_code); | ||
| 274 | if (itr == g_archive_map.end()) { | ||
| 275 | ERROR_LOG(KERNEL, "Cannot close archive %d, does not exist!", (int)id_code); | ||
| 276 | return InvalidHandle(ErrorModule::FS); | ||
| 277 | } | ||
| 278 | |||
| 279 | INFO_LOG(KERNEL, "Closed archive %d", (int) id_code); | ||
| 280 | return RESULT_SUCCESS; | ||
| 281 | } | ||
| 282 | |||
| 283 | /** | ||
| 284 | * Mounts an archive | ||
| 285 | * @param archive Pointer to the archive to mount | ||
| 286 | */ | ||
| 287 | ResultCode MountArchive(Archive* archive) { | ||
| 288 | FileSys::Archive::IdCode id_code = archive->backend->GetIdCode(); | ||
| 289 | ResultVal<Handle> archive_handle = OpenArchive(id_code); | ||
| 290 | if (archive_handle.Succeeded()) { | ||
| 291 | ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code); | ||
| 292 | return archive_handle.Code(); | ||
| 293 | } | ||
| 294 | g_archive_map[id_code] = archive->GetHandle(); | ||
| 295 | INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str()); | ||
| 296 | return RESULT_SUCCESS; | ||
| 297 | } | ||
| 298 | |||
| 299 | ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name) { | ||
| 300 | Archive* archive = new Archive; | ||
| 301 | Handle handle = Kernel::g_object_pool.Create(archive); | ||
| 302 | archive->name = name; | ||
| 303 | archive->backend = backend; | ||
| 304 | |||
| 305 | ResultCode result = MountArchive(archive); | ||
| 306 | if (result.IsError()) { | ||
| 307 | return result; | ||
| 308 | } | ||
| 309 | |||
| 310 | return RESULT_SUCCESS; | ||
| 311 | } | ||
| 312 | |||
| 313 | ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) { | ||
| 314 | // TODO(bunnei): Binary type files get a raw file pointer to the archive. Currently, we create | ||
| 315 | // the archive file handles at app loading, and then keep them persistent throughout execution. | ||
| 316 | // Archives file handles are just reused and not actually freed until emulation shut down. | ||
| 317 | // Verify if real hardware works this way, or if new handles are created each time | ||
| 318 | if (path.GetType() == FileSys::Binary) | ||
| 319 | // TODO(bunnei): FixMe - this is a hack to compensate for an incorrect FileSys backend | ||
| 320 | // design. While the functionally of this is OK, our implementation decision to separate | ||
| 321 | // normal files from archive file pointers is very likely wrong. | ||
| 322 | // See https://github.com/citra-emu/citra/issues/205 | ||
| 323 | return MakeResult<Handle>(archive_handle); | ||
| 324 | |||
| 325 | File* file = new File; | ||
| 326 | Handle handle = Kernel::g_object_pool.Create(file); | ||
| 327 | |||
| 328 | Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle); | ||
| 329 | if (archive == nullptr) { | ||
| 330 | return InvalidHandle(ErrorModule::FS); | ||
| 331 | } | ||
| 332 | file->path = path; | ||
| 333 | file->backend = archive->backend->OpenFile(path, mode); | ||
| 334 | |||
| 335 | if (!file->backend) { | ||
| 336 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||
| 337 | ErrorSummary::NotFound, ErrorLevel::Permanent); | ||
| 338 | } | ||
| 339 | |||
| 340 | return MakeResult<Handle>(handle); | ||
| 341 | } | ||
| 342 | |||
| 343 | /** | ||
| 344 | * Delete a File from an Archive | ||
| 345 | * @param archive_handle Handle to an open Archive object | ||
| 346 | * @param path Path to the File inside of the Archive | ||
| 347 | * @return Whether deletion succeeded | ||
| 348 | */ | ||
| 349 | Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 350 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 351 | if (archive == nullptr) | ||
| 352 | return -1; | ||
| 353 | if (archive->backend->DeleteFile(path)) | ||
| 354 | return 0; | ||
| 355 | return -1; | ||
| 356 | } | ||
| 357 | |||
| 358 | /** | ||
| 359 | * Delete a Directory from an Archive | ||
| 360 | * @param archive_handle Handle to an open Archive object | ||
| 361 | * @param path Path to the Directory inside of the Archive | ||
| 362 | * @return Whether deletion succeeded | ||
| 363 | */ | ||
| 364 | Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 365 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 366 | if (archive == nullptr) | ||
| 367 | return -1; | ||
| 368 | if (archive->backend->DeleteDirectory(path)) | ||
| 369 | return 0; | ||
| 370 | return -1; | ||
| 371 | } | ||
| 372 | |||
| 373 | /** | ||
| 374 | * Create a Directory from an Archive | ||
| 375 | * @param archive_handle Handle to an open Archive object | ||
| 376 | * @param path Path to the Directory inside of the Archive | ||
| 377 | * @return Whether creation succeeded | ||
| 378 | */ | ||
| 379 | Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 380 | Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); | ||
| 381 | if (archive == nullptr) | ||
| 382 | return -1; | ||
| 383 | if (archive->backend->CreateDirectory(path)) | ||
| 384 | return 0; | ||
| 385 | return -1; | ||
| 386 | } | ||
| 387 | |||
| 388 | /** | ||
| 389 | * Open a Directory from an Archive | ||
| 390 | * @param archive_handle Handle to an open Archive object | ||
| 391 | * @param path Path to the Directory inside of the Archive | ||
| 392 | * @return Opened Directory object | ||
| 393 | */ | ||
| 394 | ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { | ||
| 395 | Directory* directory = new Directory; | ||
| 396 | Handle handle = Kernel::g_object_pool.Create(directory); | ||
| 397 | |||
| 398 | Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle); | ||
| 399 | if (archive == nullptr) { | ||
| 400 | return InvalidHandle(ErrorModule::FS); | ||
| 401 | } | ||
| 402 | directory->path = path; | ||
| 403 | directory->backend = archive->backend->OpenDirectory(path); | ||
| 404 | |||
| 405 | return MakeResult<Handle>(handle); | ||
| 406 | } | ||
| 407 | |||
| 408 | /// Initialize archives | ||
| 409 | void ArchiveInit() { | ||
| 410 | g_archive_map.clear(); | ||
| 411 | |||
| 412 | // TODO(Link Mauve): Add the other archive types (see here for the known types: | ||
| 413 | // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished | ||
| 414 | // archive type is SDMC, so it is the only one getting exposed. | ||
| 415 | |||
| 416 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); | ||
| 417 | auto archive = new FileSys::Archive_SDMC(sdmc_directory); | ||
| 418 | if (archive->Initialize()) | ||
| 419 | CreateArchive(archive, "SDMC"); | ||
| 420 | else | ||
| 421 | ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | ||
| 422 | } | ||
| 423 | |||
| 424 | /// Shutdown archives | ||
| 425 | void ArchiveShutdown() { | ||
| 426 | g_archive_map.clear(); | ||
| 427 | } | ||
| 428 | |||
| 429 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h deleted file mode 100644 index 6fc4f0f25..000000000 --- a/src/core/hle/kernel/archive.h +++ /dev/null | |||
| @@ -1,85 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/archive.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 14 | // Kernel namespace | ||
| 15 | |||
| 16 | namespace Kernel { | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Opens an archive | ||
| 20 | * @param id_code IdCode of the archive to open | ||
| 21 | * @return Handle to the opened archive | ||
| 22 | */ | ||
| 23 | ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code); | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Closes an archive | ||
| 27 | * @param id_code IdCode of the archive to open | ||
| 28 | */ | ||
| 29 | ResultCode CloseArchive(FileSys::Archive::IdCode id_code); | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Creates an Archive | ||
| 33 | * @param backend File system backend interface to the archive | ||
| 34 | * @param name Name of Archive | ||
| 35 | */ | ||
| 36 | ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name); | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Open a File from an Archive | ||
| 40 | * @param archive_handle Handle to an open Archive object | ||
| 41 | * @param path Path to the File inside of the Archive | ||
| 42 | * @param mode Mode under which to open the File | ||
| 43 | * @return Handle to the opened File object | ||
| 44 | */ | ||
| 45 | ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Delete a File from an Archive | ||
| 49 | * @param archive_handle Handle to an open Archive object | ||
| 50 | * @param path Path to the File inside of the Archive | ||
| 51 | * @return Whether deletion succeeded | ||
| 52 | */ | ||
| 53 | Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path); | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Delete a Directory from an Archive | ||
| 57 | * @param archive_handle Handle to an open Archive object | ||
| 58 | * @param path Path to the Directory inside of the Archive | ||
| 59 | * @return Whether deletion succeeded | ||
| 60 | */ | ||
| 61 | Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Create a Directory from an Archive | ||
| 65 | * @param archive_handle Handle to an open Archive object | ||
| 66 | * @param path Path to the Directory inside of the Archive | ||
| 67 | * @return Whether creation of directory succeeded | ||
| 68 | */ | ||
| 69 | Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); | ||
| 70 | |||
| 71 | /** | ||
| 72 | * Open a Directory from an Archive | ||
| 73 | * @param archive_handle Handle to an open Archive object | ||
| 74 | * @param path Path to the Directory inside of the Archive | ||
| 75 | * @return Handle to the opened File object | ||
| 76 | */ | ||
| 77 | ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); | ||
| 78 | |||
| 79 | /// Initialize archives | ||
| 80 | void ArchiveInit(); | ||
| 81 | |||
| 82 | /// Shutdown archives | ||
| 83 | void ArchiveShutdown(); | ||
| 84 | |||
| 85 | } // namespace FileSys | ||
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 288080209..e43c3ee4e 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 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 <map> | 5 | #include <map> |
| @@ -19,8 +19,8 @@ public: | |||
| 19 | std::string GetTypeName() const override { return "Event"; } | 19 | std::string GetTypeName() const override { return "Event"; } |
| 20 | std::string GetName() const override { return name; } | 20 | std::string GetName() const override { return name; } |
| 21 | 21 | ||
| 22 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; } | 22 | static const HandleType HANDLE_TYPE = HandleType::Event; |
| 23 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Event; } | 23 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 24 | 24 | ||
| 25 | ResetType intitial_reset_type; ///< ResetType specified at Event initialization | 25 | ResetType intitial_reset_type; ///< ResetType specified at Event initialization |
| 26 | ResetType reset_type; ///< Current ResetType | 26 | ResetType reset_type; ///< Current ResetType |
| @@ -53,7 +53,7 @@ public: | |||
| 53 | * @return Result of operation, 0 on success, otherwise error code | 53 | * @return Result of operation, 0 on success, otherwise error code |
| 54 | */ | 54 | */ |
| 55 | ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { | 55 | ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { |
| 56 | Event* evt = g_object_pool.Get<Event>(handle); | 56 | Event* evt = g_handle_table.Get<Event>(handle); |
| 57 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 57 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 58 | 58 | ||
| 59 | evt->permanent_locked = permanent_locked; | 59 | evt->permanent_locked = permanent_locked; |
| @@ -67,7 +67,7 @@ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { | |||
| 67 | * @return Result of operation, 0 on success, otherwise error code | 67 | * @return Result of operation, 0 on success, otherwise error code |
| 68 | */ | 68 | */ |
| 69 | ResultCode SetEventLocked(const Handle handle, const bool locked) { | 69 | ResultCode SetEventLocked(const Handle handle, const bool locked) { |
| 70 | Event* evt = g_object_pool.Get<Event>(handle); | 70 | Event* evt = g_handle_table.Get<Event>(handle); |
| 71 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 71 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 72 | 72 | ||
| 73 | if (!evt->permanent_locked) { | 73 | if (!evt->permanent_locked) { |
| @@ -82,7 +82,7 @@ ResultCode SetEventLocked(const Handle handle, const bool locked) { | |||
| 82 | * @return Result of operation, 0 on success, otherwise error code | 82 | * @return Result of operation, 0 on success, otherwise error code |
| 83 | */ | 83 | */ |
| 84 | ResultCode SignalEvent(const Handle handle) { | 84 | ResultCode SignalEvent(const Handle handle) { |
| 85 | Event* evt = g_object_pool.Get<Event>(handle); | 85 | Event* evt = g_handle_table.Get<Event>(handle); |
| 86 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 86 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 87 | 87 | ||
| 88 | // Resume threads waiting for event to signal | 88 | // Resume threads waiting for event to signal |
| @@ -110,7 +110,7 @@ ResultCode SignalEvent(const Handle handle) { | |||
| 110 | * @return Result of operation, 0 on success, otherwise error code | 110 | * @return Result of operation, 0 on success, otherwise error code |
| 111 | */ | 111 | */ |
| 112 | ResultCode ClearEvent(Handle handle) { | 112 | ResultCode ClearEvent(Handle handle) { |
| 113 | Event* evt = g_object_pool.Get<Event>(handle); | 113 | Event* evt = g_handle_table.Get<Event>(handle); |
| 114 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 114 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 115 | 115 | ||
| 116 | if (!evt->permanent_locked) { | 116 | if (!evt->permanent_locked) { |
| @@ -129,7 +129,8 @@ ResultCode ClearEvent(Handle handle) { | |||
| 129 | Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) { | 129 | Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) { |
| 130 | Event* evt = new Event; | 130 | Event* evt = new Event; |
| 131 | 131 | ||
| 132 | handle = Kernel::g_object_pool.Create(evt); | 132 | // TOOD(yuriks): Fix error reporting |
| 133 | handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE); | ||
| 133 | 134 | ||
| 134 | evt->locked = true; | 135 | evt->locked = true; |
| 135 | evt->permanent_locked = false; | 136 | evt->permanent_locked = false; |
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index 73aec4e79..da793df1a 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 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 | #pragma once | 5 | #pragma once |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 018000abd..e59ed1b57 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -1,106 +1,117 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 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> | ||
| 6 | |||
| 5 | #include "common/common.h" | 7 | #include "common/common.h" |
| 6 | 8 | ||
| 7 | #include "core/core.h" | 9 | #include "core/core.h" |
| 8 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 9 | #include "core/hle/kernel/thread.h" | 11 | #include "core/hle/kernel/thread.h" |
| 10 | #include "core/hle/kernel/archive.h" | ||
| 11 | 12 | ||
| 12 | namespace Kernel { | 13 | namespace Kernel { |
| 13 | 14 | ||
| 14 | Handle g_main_thread = 0; | 15 | Handle g_main_thread = 0; |
| 15 | ObjectPool g_object_pool; | 16 | HandleTable g_handle_table; |
| 17 | u64 g_program_id = 0; | ||
| 16 | 18 | ||
| 17 | ObjectPool::ObjectPool() { | 19 | HandleTable::HandleTable() { |
| 18 | next_id = INITIAL_NEXT_ID; | 20 | next_generation = 1; |
| 21 | Clear(); | ||
| 19 | } | 22 | } |
| 20 | 23 | ||
| 21 | Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) { | 24 | ResultVal<Handle> HandleTable::Create(Object* obj) { |
| 22 | if (range_top > MAX_COUNT) { | 25 | _dbg_assert_(Kernel, obj != nullptr); |
| 23 | range_top = MAX_COUNT; | 26 | |
| 24 | } | 27 | u16 slot = next_free_slot; |
| 25 | if (next_id >= range_bottom && next_id < range_top) { | 28 | if (slot >= generations.size()) { |
| 26 | range_bottom = next_id++; | 29 | LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); |
| 27 | } | 30 | return ERR_OUT_OF_HANDLES; |
| 28 | for (int i = range_bottom; i < range_top; i++) { | ||
| 29 | if (!occupied[i]) { | ||
| 30 | occupied[i] = true; | ||
| 31 | pool[i] = obj; | ||
| 32 | pool[i]->handle = i + HANDLE_OFFSET; | ||
| 33 | return i + HANDLE_OFFSET; | ||
| 34 | } | ||
| 35 | } | 31 | } |
| 36 | ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use."); | 32 | next_free_slot = generations[slot]; |
| 37 | return 0; | 33 | |
| 38 | } | 34 | u16 generation = next_generation++; |
| 39 | 35 | ||
| 40 | bool ObjectPool::IsValid(Handle handle) { | 36 | // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. |
| 41 | int index = handle - HANDLE_OFFSET; | 37 | // CTR-OS doesn't use generation 0, so skip straight to 1. |
| 42 | if (index < 0) | 38 | if (next_generation >= (1 << 15)) next_generation = 1; |
| 43 | return false; | ||
| 44 | if (index >= MAX_COUNT) | ||
| 45 | return false; | ||
| 46 | 39 | ||
| 47 | return occupied[index]; | 40 | generations[slot] = generation; |
| 41 | intrusive_ptr_add_ref(obj); | ||
| 42 | objects[slot] = obj; | ||
| 43 | |||
| 44 | Handle handle = generation | (slot << 15); | ||
| 45 | obj->handle = handle; | ||
| 46 | return MakeResult<Handle>(handle); | ||
| 48 | } | 47 | } |
| 49 | 48 | ||
| 50 | void ObjectPool::Clear() { | 49 | ResultVal<Handle> HandleTable::Duplicate(Handle handle) { |
| 51 | for (int i = 0; i < MAX_COUNT; i++) { | 50 | Object* object = GetGeneric(handle); |
| 52 | //brutally clear everything, no validation | 51 | if (object == nullptr) { |
| 53 | if (occupied[i]) | 52 | LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); |
| 54 | delete pool[i]; | 53 | return ERR_INVALID_HANDLE; |
| 55 | occupied[i] = false; | ||
| 56 | } | 54 | } |
| 57 | pool.fill(nullptr); | 55 | return Create(object); |
| 58 | next_id = INITIAL_NEXT_ID; | 56 | } |
| 57 | |||
| 58 | ResultCode HandleTable::Close(Handle handle) { | ||
| 59 | if (!IsValid(handle)) | ||
| 60 | return ERR_INVALID_HANDLE; | ||
| 61 | |||
| 62 | size_t slot = GetSlot(handle); | ||
| 63 | u16 generation = GetGeneration(handle); | ||
| 64 | |||
| 65 | intrusive_ptr_release(objects[slot]); | ||
| 66 | objects[slot] = nullptr; | ||
| 67 | |||
| 68 | generations[generation] = next_free_slot; | ||
| 69 | next_free_slot = slot; | ||
| 70 | return RESULT_SUCCESS; | ||
| 59 | } | 71 | } |
| 60 | 72 | ||
| 61 | Object* &ObjectPool::operator [](Handle handle) | 73 | bool HandleTable::IsValid(Handle handle) const { |
| 62 | { | 74 | size_t slot = GetSlot(handle); |
| 63 | _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); | 75 | u16 generation = GetGeneration(handle); |
| 64 | return pool[handle - HANDLE_OFFSET]; | 76 | |
| 77 | return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; | ||
| 65 | } | 78 | } |
| 66 | 79 | ||
| 67 | void ObjectPool::List() { | 80 | Object* HandleTable::GetGeneric(Handle handle) const { |
| 68 | for (int i = 0; i < MAX_COUNT; i++) { | 81 | if (handle == CurrentThread) { |
| 69 | if (occupied[i]) { | 82 | // TODO(yuriks) Directly return the pointer once this is possible. |
| 70 | if (pool[i]) { | 83 | handle = GetCurrentThreadHandle(); |
| 71 | INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(), | 84 | } else if (handle == CurrentProcess) { |
| 72 | pool[i]->GetName().c_str()); | 85 | LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess); |
| 73 | } | 86 | return nullptr; |
| 74 | } | ||
| 75 | } | 87 | } |
| 76 | } | ||
| 77 | 88 | ||
| 78 | int ObjectPool::GetCount() { | 89 | if (!IsValid(handle)) { |
| 79 | int count = 0; | 90 | return nullptr; |
| 80 | for (int i = 0; i < MAX_COUNT; i++) { | ||
| 81 | if (occupied[i]) | ||
| 82 | count++; | ||
| 83 | } | 91 | } |
| 84 | return count; | 92 | return objects[GetSlot(handle)]; |
| 85 | } | 93 | } |
| 86 | 94 | ||
| 87 | Object* ObjectPool::CreateByIDType(int type) { | 95 | void HandleTable::Clear() { |
| 88 | ERROR_LOG(COMMON, "Unimplemented: %d.", type); | 96 | for (size_t i = 0; i < MAX_COUNT; ++i) { |
| 89 | return nullptr; | 97 | generations[i] = i + 1; |
| 98 | if (objects[i] != nullptr) | ||
| 99 | intrusive_ptr_release(objects[i]); | ||
| 100 | objects[i] = nullptr; | ||
| 101 | } | ||
| 102 | next_free_slot = 0; | ||
| 90 | } | 103 | } |
| 91 | 104 | ||
| 92 | /// Initialize the kernel | 105 | /// Initialize the kernel |
| 93 | void Init() { | 106 | void Init() { |
| 94 | Kernel::ThreadingInit(); | 107 | Kernel::ThreadingInit(); |
| 95 | Kernel::ArchiveInit(); | ||
| 96 | } | 108 | } |
| 97 | 109 | ||
| 98 | /// Shutdown the kernel | 110 | /// Shutdown the kernel |
| 99 | void Shutdown() { | 111 | void Shutdown() { |
| 100 | Kernel::ThreadingShutdown(); | 112 | Kernel::ThreadingShutdown(); |
| 101 | Kernel::ArchiveShutdown(); | ||
| 102 | 113 | ||
| 103 | g_object_pool.Clear(); // Free all kernel objects | 114 | g_handle_table.Clear(); // Free all kernel objects |
| 104 | } | 115 | } |
| 105 | 116 | ||
| 106 | /** | 117 | /** |
| @@ -109,8 +120,6 @@ void Shutdown() { | |||
| 109 | * @return True on success, otherwise false | 120 | * @return True on success, otherwise false |
| 110 | */ | 121 | */ |
| 111 | bool LoadExec(u32 entry_point) { | 122 | bool LoadExec(u32 entry_point) { |
| 112 | Init(); | ||
| 113 | |||
| 114 | Core::g_app_core->SetPC(entry_point); | 123 | Core::g_app_core->SetPC(entry_point); |
| 115 | 124 | ||
| 116 | // 0x30 is the typical main thread priority I've seen used so far | 125 | // 0x30 is the typical main thread priority I've seen used so far |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 8d3937ce8..7f86fd07d 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 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 | #pragma once | 5 | #pragma once |
| @@ -12,9 +12,17 @@ | |||
| 12 | typedef u32 Handle; | 12 | typedef u32 Handle; |
| 13 | typedef s32 Result; | 13 | typedef s32 Result; |
| 14 | 14 | ||
| 15 | const Handle INVALID_HANDLE = 0; | ||
| 16 | |||
| 15 | namespace Kernel { | 17 | namespace Kernel { |
| 16 | 18 | ||
| 17 | enum KernelHandle { | 19 | // TODO: Verify code |
| 20 | const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel, | ||
| 21 | ErrorSummary::OutOfResource, ErrorLevel::Temporary); | ||
| 22 | // TOOD: Verify code | ||
| 23 | const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel); | ||
| 24 | |||
| 25 | enum KernelHandle : Handle { | ||
| 18 | CurrentThread = 0xFFFF8000, | 26 | CurrentThread = 0xFFFF8000, |
| 19 | CurrentProcess = 0xFFFF8001, | 27 | CurrentProcess = 0xFFFF8001, |
| 20 | }; | 28 | }; |
| @@ -22,7 +30,7 @@ enum KernelHandle { | |||
| 22 | enum class HandleType : u32 { | 30 | enum class HandleType : u32 { |
| 23 | Unknown = 0, | 31 | Unknown = 0, |
| 24 | Port = 1, | 32 | Port = 1, |
| 25 | Service = 2, | 33 | Session = 2, |
| 26 | Event = 3, | 34 | Event = 3, |
| 27 | Mutex = 4, | 35 | Mutex = 4, |
| 28 | SharedMemory = 5, | 36 | SharedMemory = 5, |
| @@ -30,20 +38,17 @@ enum class HandleType : u32 { | |||
| 30 | Thread = 7, | 38 | Thread = 7, |
| 31 | Process = 8, | 39 | Process = 8, |
| 32 | AddressArbiter = 9, | 40 | AddressArbiter = 9, |
| 33 | File = 10, | 41 | Semaphore = 10, |
| 34 | Semaphore = 11, | ||
| 35 | Archive = 12, | ||
| 36 | Directory = 13, | ||
| 37 | }; | 42 | }; |
| 38 | 43 | ||
| 39 | enum { | 44 | enum { |
| 40 | DEFAULT_STACK_SIZE = 0x4000, | 45 | DEFAULT_STACK_SIZE = 0x4000, |
| 41 | }; | 46 | }; |
| 42 | 47 | ||
| 43 | class ObjectPool; | 48 | class HandleTable; |
| 44 | 49 | ||
| 45 | class Object : NonCopyable { | 50 | class Object : NonCopyable { |
| 46 | friend class ObjectPool; | 51 | friend class HandleTable; |
| 47 | u32 handle; | 52 | u32 handle; |
| 48 | public: | 53 | public: |
| 49 | virtual ~Object() {} | 54 | virtual ~Object() {} |
| @@ -53,113 +58,145 @@ public: | |||
| 53 | virtual Kernel::HandleType GetHandleType() const = 0; | 58 | virtual Kernel::HandleType GetHandleType() const = 0; |
| 54 | 59 | ||
| 55 | /** | 60 | /** |
| 56 | * Synchronize kernel object. | 61 | * Wait for kernel object to synchronize. |
| 57 | * @return True if the current thread should wait as a result of the sync | 62 | * @return True if the current thread should wait as a result of the wait |
| 58 | */ | 63 | */ |
| 59 | virtual ResultVal<bool> SyncRequest() { | 64 | virtual ResultVal<bool> WaitSynchronization() { |
| 60 | ERROR_LOG(KERNEL, "(UNIMPLEMENTED)"); | 65 | LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); |
| 61 | return UnimplementedFunction(ErrorModule::Kernel); | 66 | return UnimplementedFunction(ErrorModule::Kernel); |
| 62 | } | 67 | } |
| 63 | 68 | ||
| 64 | /** | 69 | private: |
| 65 | * Wait for kernel object to synchronize. | 70 | friend void intrusive_ptr_add_ref(Object*); |
| 66 | * @return True if the current thread should wait as a result of the wait | 71 | friend void intrusive_ptr_release(Object*); |
| 67 | */ | 72 | |
| 68 | virtual ResultVal<bool> WaitSynchronization() = 0; | 73 | unsigned int ref_count = 0; |
| 69 | }; | 74 | }; |
| 70 | 75 | ||
| 71 | class ObjectPool : NonCopyable { | 76 | // Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting |
| 72 | public: | 77 | inline void intrusive_ptr_add_ref(Object* object) { |
| 73 | ObjectPool(); | 78 | ++object->ref_count; |
| 74 | ~ObjectPool() {} | 79 | } |
| 75 | 80 | ||
| 76 | // Allocates a handle within the range and inserts the object into the map. | 81 | inline void intrusive_ptr_release(Object* object) { |
| 77 | Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); | 82 | if (--object->ref_count == 0) { |
| 83 | delete object; | ||
| 84 | } | ||
| 85 | } | ||
| 78 | 86 | ||
| 79 | static Object* CreateByIDType(int type); | 87 | /** |
| 88 | * This class allows the creation of Handles, which are references to objects that can be tested | ||
| 89 | * for validity and looked up. Here they are used to pass references to kernel objects to/from the | ||
| 90 | * emulated process. it has been designed so that it follows the same handle format and has | ||
| 91 | * approximately the same restrictions as the handle manager in the CTR-OS. | ||
| 92 | * | ||
| 93 | * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). | ||
| 94 | * The slot index is used to index into the arrays in this class to access the data corresponding | ||
| 95 | * to the Handle. | ||
| 96 | * | ||
| 97 | * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter | ||
| 98 | * is kept and incremented every time a Handle is created. This is the Handle's "generation". The | ||
| 99 | * value of the counter is stored into the Handle as well as in the handle table (in the | ||
| 100 | * "generations" array). When looking up a handle, the Handle's generation must match with the | ||
| 101 | * value stored on the class, otherwise the Handle is considered invalid. | ||
| 102 | * | ||
| 103 | * To find free slots when allocating a Handle without needing to scan the entire object array, the | ||
| 104 | * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. | ||
| 105 | * When a Handle is created, an index is popped off the list and used for the new Handle. When it | ||
| 106 | * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is | ||
| 107 | * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been | ||
| 108 | * verified and isn't likely to cause any problems. | ||
| 109 | */ | ||
| 110 | class HandleTable final : NonCopyable { | ||
| 111 | public: | ||
| 112 | HandleTable(); | ||
| 80 | 113 | ||
| 81 | template <class T> | 114 | /** |
| 82 | void Destroy(Handle handle) { | 115 | * Allocates a handle for the given object. |
| 83 | if (Get<T>(handle)) { | 116 | * @return The created Handle or one of the following errors: |
| 84 | occupied[handle - HANDLE_OFFSET] = false; | 117 | * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. |
| 85 | delete pool[handle - HANDLE_OFFSET]; | 118 | */ |
| 86 | } | 119 | ResultVal<Handle> Create(Object* obj); |
| 87 | } | ||
| 88 | 120 | ||
| 89 | bool IsValid(Handle handle); | 121 | /** |
| 122 | * Returns a new handle that points to the same object as the passed in handle. | ||
| 123 | * @return The duplicated Handle or one of the following errors: | ||
| 124 | * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. | ||
| 125 | * - Any errors returned by `Create()`. | ||
| 126 | */ | ||
| 127 | ResultVal<Handle> Duplicate(Handle handle); | ||
| 90 | 128 | ||
| 91 | template <class T> | 129 | /** |
| 92 | T* Get(Handle handle) { | 130 | * Closes a handle, removing it from the table and decreasing the object's ref-count. |
| 93 | if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { | 131 | * @return `RESULT_SUCCESS` or one of the following errors: |
| 94 | if (handle != 0) { | 132 | * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. |
| 95 | WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); | 133 | */ |
| 96 | } | 134 | ResultCode Close(Handle handle); |
| 97 | return nullptr; | ||
| 98 | } else { | ||
| 99 | Object* t = pool[handle - HANDLE_OFFSET]; | ||
| 100 | if (t->GetHandleType() != T::GetStaticHandleType()) { | ||
| 101 | WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle); | ||
| 102 | return nullptr; | ||
| 103 | } | ||
| 104 | return static_cast<T*>(t); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | 135 | ||
| 108 | // ONLY use this when you know the handle is valid. | 136 | /// Checks if a handle is valid and points to an existing object. |
| 109 | template <class T> | 137 | bool IsValid(Handle handle) const; |
| 110 | T *GetFast(Handle handle) { | ||
| 111 | const Handle realHandle = handle - HANDLE_OFFSET; | ||
| 112 | _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); | ||
| 113 | return static_cast<T*>(pool[realHandle]); | ||
| 114 | } | ||
| 115 | 138 | ||
| 116 | template <class T, typename ArgT> | 139 | /** |
| 117 | void Iterate(bool func(T*, ArgT), ArgT arg) { | 140 | * Looks up a handle. |
| 118 | int type = T::GetStaticIDType(); | 141 | * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid. |
| 119 | for (int i = 0; i < MAX_COUNT; i++) | 142 | */ |
| 120 | { | 143 | Object* GetGeneric(Handle handle) const; |
| 121 | if (!occupied[i]) | ||
| 122 | continue; | ||
| 123 | T* t = static_cast<T*>(pool[i]); | ||
| 124 | if (t->GetIDType() == type) { | ||
| 125 | if (!func(t, arg)) | ||
| 126 | break; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | 144 | ||
| 131 | bool GetIDType(Handle handle, HandleType* type) const { | 145 | /** |
| 132 | if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || | 146 | * Looks up a handle while verifying its type. |
| 133 | !occupied[handle - HANDLE_OFFSET]) { | 147 | * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid or its |
| 134 | ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); | 148 | * type differs from the handle type `T::HANDLE_TYPE`. |
| 135 | return false; | 149 | */ |
| 150 | template <class T> | ||
| 151 | T* Get(Handle handle) const { | ||
| 152 | Object* object = GetGeneric(handle); | ||
| 153 | if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { | ||
| 154 | return static_cast<T*>(object); | ||
| 136 | } | 155 | } |
| 137 | Object* t = pool[handle - HANDLE_OFFSET]; | 156 | return nullptr; |
| 138 | *type = t->GetHandleType(); | ||
| 139 | return true; | ||
| 140 | } | 157 | } |
| 141 | 158 | ||
| 142 | Object* &operator [](Handle handle); | 159 | /// Closes all handles held in this table. |
| 143 | void List(); | ||
| 144 | void Clear(); | 160 | void Clear(); |
| 145 | int GetCount(); | ||
| 146 | 161 | ||
| 147 | private: | 162 | private: |
| 163 | /** | ||
| 164 | * This is the maximum limit of handles allowed per process in CTR-OS. It can be further | ||
| 165 | * reduced by ExHeader values, but this is not emulated here. | ||
| 166 | */ | ||
| 167 | static const size_t MAX_COUNT = 4096; | ||
| 148 | 168 | ||
| 149 | enum { | 169 | static size_t GetSlot(Handle handle) { return handle >> 15; } |
| 150 | MAX_COUNT = 0x1000, | 170 | static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; } |
| 151 | HANDLE_OFFSET = 0x100, | ||
| 152 | INITIAL_NEXT_ID = 0x10, | ||
| 153 | }; | ||
| 154 | 171 | ||
| 155 | std::array<Object*, MAX_COUNT> pool; | 172 | /// Stores the Object referenced by the handle or null if the slot is empty. |
| 156 | std::array<bool, MAX_COUNT> occupied; | 173 | std::array<Object*, MAX_COUNT> objects; |
| 157 | int next_id; | 174 | |
| 175 | /** | ||
| 176 | * The value of `next_generation` when the handle was created, used to check for validity. For | ||
| 177 | * empty slots, contains the index of the next free slot in the list. | ||
| 178 | */ | ||
| 179 | std::array<u16, MAX_COUNT> generations; | ||
| 180 | |||
| 181 | /** | ||
| 182 | * Global counter of the number of created handles. Stored in `generations` when a handle is | ||
| 183 | * created, and wraps around to 1 when it hits 0x8000. | ||
| 184 | */ | ||
| 185 | u16 next_generation; | ||
| 186 | |||
| 187 | /// Head of the free slots linked list. | ||
| 188 | u16 next_free_slot; | ||
| 158 | }; | 189 | }; |
| 159 | 190 | ||
| 160 | extern ObjectPool g_object_pool; | 191 | extern HandleTable g_handle_table; |
| 161 | extern Handle g_main_thread; | 192 | extern Handle g_main_thread; |
| 162 | 193 | ||
| 194 | /// The ID code of the currently running game | ||
| 195 | /// TODO(Subv): This variable should not be here, | ||
| 196 | /// we need a way to store information about the currently loaded application | ||
| 197 | /// for later query during runtime, maybe using the LDR service? | ||
| 198 | extern u64 g_program_id; | ||
| 199 | |||
| 163 | /// Initialize the kernel | 200 | /// Initialize the kernel |
| 164 | void Init(); | 201 | void Init(); |
| 165 | 202 | ||
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index b303ba128..558068c79 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 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 <map> | 5 | #include <map> |
| @@ -18,8 +18,8 @@ public: | |||
| 18 | std::string GetTypeName() const override { return "Mutex"; } | 18 | std::string GetTypeName() const override { return "Mutex"; } |
| 19 | std::string GetName() const override { return name; } | 19 | std::string GetName() const override { return name; } |
| 20 | 20 | ||
| 21 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; } | 21 | static const HandleType HANDLE_TYPE = HandleType::Mutex; |
| 22 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Mutex; } | 22 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 23 | 23 | ||
| 24 | bool initial_locked; ///< Initial lock state when mutex was created | 24 | bool initial_locked; ///< Initial lock state when mutex was created |
| 25 | bool locked; ///< Current locked state | 25 | bool locked; ///< Current locked state |
| @@ -27,21 +27,7 @@ public: | |||
| 27 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex | 27 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex |
| 28 | std::string name; ///< Name of mutex (optional) | 28 | std::string name; ///< Name of mutex (optional) |
| 29 | 29 | ||
| 30 | ResultVal<bool> SyncRequest() override { | 30 | ResultVal<bool> WaitSynchronization() override; |
| 31 | // TODO(bunnei): ImplementMe | ||
| 32 | locked = true; | ||
| 33 | return MakeResult<bool>(false); | ||
| 34 | } | ||
| 35 | |||
| 36 | ResultVal<bool> WaitSynchronization() override { | ||
| 37 | // TODO(bunnei): ImplementMe | ||
| 38 | bool wait = locked; | ||
| 39 | if (locked) { | ||
| 40 | Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); | ||
| 41 | } | ||
| 42 | |||
| 43 | return MakeResult<bool>(wait); | ||
| 44 | } | ||
| 45 | }; | 31 | }; |
| 46 | 32 | ||
| 47 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 33 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -49,21 +35,46 @@ public: | |||
| 49 | typedef std::multimap<Handle, Handle> MutexMap; | 35 | typedef std::multimap<Handle, Handle> MutexMap; |
| 50 | static MutexMap g_mutex_held_locks; | 36 | static MutexMap g_mutex_held_locks; |
| 51 | 37 | ||
| 52 | void MutexAcquireLock(Mutex* mutex, Handle thread) { | 38 | /** |
| 39 | * Acquires the specified mutex for the specified thread | ||
| 40 | * @param mutex Mutex that is to be acquired | ||
| 41 | * @param thread Thread that will acquired | ||
| 42 | */ | ||
| 43 | void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) { | ||
| 53 | g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); | 44 | g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); |
| 54 | mutex->lock_thread = thread; | 45 | mutex->lock_thread = thread; |
| 55 | } | 46 | } |
| 56 | 47 | ||
| 57 | void MutexAcquireLock(Mutex* mutex) { | 48 | bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { |
| 58 | Handle thread = GetCurrentThreadHandle(); | ||
| 59 | MutexAcquireLock(mutex, thread); | 49 | MutexAcquireLock(mutex, thread); |
| 50 | Kernel::ResumeThreadFromWait(thread); | ||
| 51 | return true; | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Resumes a thread waiting for the specified mutex | ||
| 56 | * @param mutex The mutex that some thread is waiting on | ||
| 57 | */ | ||
| 58 | void ResumeWaitingThread(Mutex* mutex) { | ||
| 59 | // Find the next waiting thread for the mutex... | ||
| 60 | if (mutex->waiting_threads.empty()) { | ||
| 61 | // Reset mutex lock thread handle, nothing is waiting | ||
| 62 | mutex->locked = false; | ||
| 63 | mutex->lock_thread = -1; | ||
| 64 | } | ||
| 65 | else { | ||
| 66 | // Resume the next waiting thread and re-lock the mutex | ||
| 67 | std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); | ||
| 68 | ReleaseMutexForThread(mutex, *iter); | ||
| 69 | mutex->waiting_threads.erase(iter); | ||
| 70 | } | ||
| 60 | } | 71 | } |
| 61 | 72 | ||
| 62 | void MutexEraseLock(Mutex* mutex) { | 73 | void MutexEraseLock(Mutex* mutex) { |
| 63 | Handle handle = mutex->GetHandle(); | 74 | Handle handle = mutex->GetHandle(); |
| 64 | auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); | 75 | auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); |
| 65 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { | 76 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { |
| 66 | if ((*iter).second == handle) { | 77 | if (iter->second == handle) { |
| 67 | g_mutex_held_locks.erase(iter); | 78 | g_mutex_held_locks.erase(iter); |
| 68 | break; | 79 | break; |
| 69 | } | 80 | } |
| @@ -71,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) { | |||
| 71 | mutex->lock_thread = -1; | 82 | mutex->lock_thread = -1; |
| 72 | } | 83 | } |
| 73 | 84 | ||
| 85 | void ReleaseThreadMutexes(Handle thread) { | ||
| 86 | auto locked = g_mutex_held_locks.equal_range(thread); | ||
| 87 | |||
| 88 | // Release every mutex that the thread holds, and resume execution on the waiting threads | ||
| 89 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { | ||
| 90 | Mutex* mutex = g_handle_table.Get<Mutex>(iter->second); | ||
| 91 | ResumeWaitingThread(mutex); | ||
| 92 | } | ||
| 93 | |||
| 94 | // Erase all the locks that this thread holds | ||
| 95 | g_mutex_held_locks.erase(thread); | ||
| 96 | } | ||
| 97 | |||
| 74 | bool LockMutex(Mutex* mutex) { | 98 | bool LockMutex(Mutex* mutex) { |
| 75 | // Mutex alread locked? | 99 | // Mutex alread locked? |
| 76 | if (mutex->locked) { | 100 | if (mutex->locked) { |
| @@ -80,28 +104,10 @@ bool LockMutex(Mutex* mutex) { | |||
| 80 | return true; | 104 | return true; |
| 81 | } | 105 | } |
| 82 | 106 | ||
| 83 | bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { | ||
| 84 | MutexAcquireLock(mutex, thread); | ||
| 85 | Kernel::ResumeThreadFromWait(thread); | ||
| 86 | return true; | ||
| 87 | } | ||
| 88 | |||
| 89 | bool ReleaseMutex(Mutex* mutex) { | 107 | bool ReleaseMutex(Mutex* mutex) { |
| 90 | MutexEraseLock(mutex); | 108 | MutexEraseLock(mutex); |
| 91 | bool woke_threads = false; | 109 | ResumeWaitingThread(mutex); |
| 92 | 110 | return true; | |
| 93 | // Find the next waiting thread for the mutex... | ||
| 94 | while (!woke_threads && !mutex->waiting_threads.empty()) { | ||
| 95 | std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); | ||
| 96 | woke_threads |= ReleaseMutexForThread(mutex, *iter); | ||
| 97 | mutex->waiting_threads.erase(iter); | ||
| 98 | } | ||
| 99 | // Reset mutex lock thread handle, nothing is waiting | ||
| 100 | if (!woke_threads) { | ||
| 101 | mutex->locked = false; | ||
| 102 | mutex->lock_thread = -1; | ||
| 103 | } | ||
| 104 | return woke_threads; | ||
| 105 | } | 111 | } |
| 106 | 112 | ||
| 107 | /** | 113 | /** |
| @@ -109,7 +115,7 @@ bool ReleaseMutex(Mutex* mutex) { | |||
| 109 | * @param handle Handle to mutex to release | 115 | * @param handle Handle to mutex to release |
| 110 | */ | 116 | */ |
| 111 | ResultCode ReleaseMutex(Handle handle) { | 117 | ResultCode ReleaseMutex(Handle handle) { |
| 112 | Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle); | 118 | Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle); |
| 113 | if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); | 119 | if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 114 | 120 | ||
| 115 | if (!ReleaseMutex(mutex)) { | 121 | if (!ReleaseMutex(mutex)) { |
| @@ -130,7 +136,8 @@ ResultCode ReleaseMutex(Handle handle) { | |||
| 130 | */ | 136 | */ |
| 131 | Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) { | 137 | Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) { |
| 132 | Mutex* mutex = new Mutex; | 138 | Mutex* mutex = new Mutex; |
| 133 | handle = Kernel::g_object_pool.Create(mutex); | 139 | // TODO(yuriks): Fix error reporting |
| 140 | handle = Kernel::g_handle_table.Create(mutex).ValueOr(INVALID_HANDLE); | ||
| 134 | 141 | ||
| 135 | mutex->locked = mutex->initial_locked = initial_locked; | 142 | mutex->locked = mutex->initial_locked = initial_locked; |
| 136 | mutex->name = name; | 143 | mutex->name = name; |
| @@ -158,4 +165,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { | |||
| 158 | return handle; | 165 | return handle; |
| 159 | } | 166 | } |
| 160 | 167 | ||
| 168 | ResultVal<bool> Mutex::WaitSynchronization() { | ||
| 169 | bool wait = locked; | ||
| 170 | if (locked) { | ||
| 171 | Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); | ||
| 172 | } | ||
| 173 | else { | ||
| 174 | // Lock the mutex when the first thread accesses it | ||
| 175 | locked = true; | ||
| 176 | MutexAcquireLock(this); | ||
| 177 | } | ||
| 178 | |||
| 179 | return MakeResult<bool>(wait); | ||
| 180 | } | ||
| 161 | } // namespace | 181 | } // namespace |
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 155449f95..a8ca97014 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 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 | #pragma once | 5 | #pragma once |
| @@ -24,4 +24,10 @@ ResultCode ReleaseMutex(Handle handle); | |||
| 24 | */ | 24 | */ |
| 25 | Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); | 25 | Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); |
| 26 | 26 | ||
| 27 | /** | ||
| 28 | * Releases all the mutexes held by the specified thread | ||
| 29 | * @param thread Thread that is holding the mutexes | ||
| 30 | */ | ||
| 31 | void ReleaseThreadMutexes(Handle thread); | ||
| 32 | |||
| 27 | } // namespace | 33 | } // namespace |
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp new file mode 100644 index 000000000..6bc8066a6 --- /dev/null +++ b/src/core/hle/kernel/semaphore.cpp | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <queue> | ||
| 6 | |||
| 7 | #include "common/common.h" | ||
| 8 | |||
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 10 | #include "core/hle/kernel/semaphore.h" | ||
| 11 | #include "core/hle/kernel/thread.h" | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | |||
| 15 | class Semaphore : public Object { | ||
| 16 | public: | ||
| 17 | std::string GetTypeName() const override { return "Semaphore"; } | ||
| 18 | std::string GetName() const override { return name; } | ||
| 19 | |||
| 20 | static const HandleType HANDLE_TYPE = HandleType::Semaphore; | ||
| 21 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | ||
| 22 | |||
| 23 | s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have | ||
| 24 | s32 available_count; ///< Number of free slots left in the semaphore | ||
| 25 | std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore | ||
| 26 | std::string name; ///< Name of semaphore (optional) | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Tests whether a semaphore still has free slots | ||
| 30 | * @return Whether the semaphore is available | ||
| 31 | */ | ||
| 32 | bool IsAvailable() const { | ||
| 33 | return available_count > 0; | ||
| 34 | } | ||
| 35 | |||
| 36 | ResultVal<bool> WaitSynchronization() override { | ||
| 37 | bool wait = !IsAvailable(); | ||
| 38 | |||
| 39 | if (wait) { | ||
| 40 | Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle()); | ||
| 41 | waiting_threads.push(GetCurrentThreadHandle()); | ||
| 42 | } else { | ||
| 43 | --available_count; | ||
| 44 | } | ||
| 45 | |||
| 46 | return MakeResult<bool>(wait); | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | |||
| 50 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 51 | |||
| 52 | ResultCode CreateSemaphore(Handle* handle, s32 initial_count, | ||
| 53 | s32 max_count, const std::string& name) { | ||
| 54 | |||
| 55 | if (initial_count > max_count) | ||
| 56 | return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, | ||
| 57 | ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 58 | |||
| 59 | Semaphore* semaphore = new Semaphore; | ||
| 60 | // TOOD(yuriks): Fix error reporting | ||
| 61 | *handle = g_handle_table.Create(semaphore).ValueOr(INVALID_HANDLE); | ||
| 62 | |||
| 63 | // When the semaphore is created, some slots are reserved for other threads, | ||
| 64 | // and the rest is reserved for the caller thread | ||
| 65 | semaphore->max_count = max_count; | ||
| 66 | semaphore->available_count = initial_count; | ||
| 67 | semaphore->name = name; | ||
| 68 | |||
| 69 | return RESULT_SUCCESS; | ||
| 70 | } | ||
| 71 | |||
| 72 | ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { | ||
| 73 | Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle); | ||
| 74 | if (semaphore == nullptr) | ||
| 75 | return InvalidHandle(ErrorModule::Kernel); | ||
| 76 | |||
| 77 | if (semaphore->max_count - semaphore->available_count < release_count) | ||
| 78 | return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, | ||
| 79 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 80 | |||
| 81 | *count = semaphore->available_count; | ||
| 82 | semaphore->available_count += release_count; | ||
| 83 | |||
| 84 | // Notify some of the threads that the semaphore has been released | ||
| 85 | // stop once the semaphore is full again or there are no more waiting threads | ||
| 86 | while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { | ||
| 87 | Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front()); | ||
| 88 | semaphore->waiting_threads.pop(); | ||
| 89 | --semaphore->available_count; | ||
| 90 | } | ||
| 91 | |||
| 92 | return RESULT_SUCCESS; | ||
| 93 | } | ||
| 94 | |||
| 95 | } // namespace | ||
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h new file mode 100644 index 000000000..8644ecf0c --- /dev/null +++ b/src/core/hle/kernel/semaphore.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // Copyright 2014 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 "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 10 | |||
| 11 | namespace Kernel { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Creates a semaphore. | ||
| 15 | * @param handle Pointer to the handle of the newly created object | ||
| 16 | * @param initial_count Number of slots reserved for other threads | ||
| 17 | * @param max_count Maximum number of slots the semaphore can have | ||
| 18 | * @param name Optional name of semaphore | ||
| 19 | * @return ResultCode of the error | ||
| 20 | */ | ||
| 21 | ResultCode CreateSemaphore(Handle* handle, s32 initial_count, s32 max_count, const std::string& name = "Unknown"); | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Releases a certain number of slots from a semaphore. | ||
| 25 | * @param count The number of free slots the semaphore had before this call | ||
| 26 | * @param handle The handle of the semaphore to release | ||
| 27 | * @param release_count The number of slots to release | ||
| 28 | * @return ResultCode of the error | ||
| 29 | */ | ||
| 30 | ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count); | ||
| 31 | |||
| 32 | } // namespace | ||
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h new file mode 100644 index 000000000..91f3ffc2c --- /dev/null +++ b/src/core/hle/kernel/session.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | // Copyright 2014 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/kernel/kernel.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | |||
| 11 | static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Returns a pointer to the command buffer in kernel memory | ||
| 15 | * @param offset Optional offset into command buffer | ||
| 16 | * @return Pointer to command buffer | ||
| 17 | */ | ||
| 18 | inline static u32* GetCommandBuffer(const int offset=0) { | ||
| 19 | return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset); | ||
| 20 | } | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS | ||
| 24 | * primitive for communication between different processes, and are used to implement service calls | ||
| 25 | * to the various system services. | ||
| 26 | * | ||
| 27 | * To make a service call, the client must write the command header and parameters to the buffer | ||
| 28 | * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest | ||
| 29 | * SVC call with its Session handle. The kernel will read the command header, using it to marshall | ||
| 30 | * the parameters to the process at the server endpoint of the session. After the server replies to | ||
| 31 | * the request, the response is marshalled back to the caller's TLS buffer and control is | ||
| 32 | * transferred back to it. | ||
| 33 | * | ||
| 34 | * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC | ||
| 35 | * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called | ||
| 36 | * with the session handle, this class's SyncRequest method is called, which should read the TLS | ||
| 37 | * buffer and emulate the call accordingly. Since the code can directly read the emulated memory, | ||
| 38 | * no parameter marshalling is done. | ||
| 39 | * | ||
| 40 | * In the long term, this should be turned into the full-fledged IPC mechanism implemented by | ||
| 41 | * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as | ||
| 42 | * opposed to HLE simulations. | ||
| 43 | */ | ||
| 44 | class Session : public Object { | ||
| 45 | public: | ||
| 46 | std::string GetTypeName() const override { return "Session"; } | ||
| 47 | |||
| 48 | static const HandleType HANDLE_TYPE = HandleType::Session; | ||
| 49 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls | ||
| 53 | * aren't supported yet. | ||
| 54 | */ | ||
| 55 | virtual ResultVal<bool> SyncRequest() = 0; | ||
| 56 | }; | ||
| 57 | |||
| 58 | } | ||
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index cfcc0e0b7..cea1f6fa1 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 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 "common/common.h" | 5 | #include "common/common.h" |
| @@ -13,14 +13,8 @@ class SharedMemory : public Object { | |||
| 13 | public: | 13 | public: |
| 14 | std::string GetTypeName() const override { return "SharedMemory"; } | 14 | std::string GetTypeName() const override { return "SharedMemory"; } |
| 15 | 15 | ||
| 16 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; } | 16 | static const HandleType HANDLE_TYPE = HandleType::SharedMemory; |
| 17 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; } | 17 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 18 | |||
| 19 | ResultVal<bool> WaitSynchronization() override { | ||
| 20 | // TODO(bunnei): ImplementMe | ||
| 21 | ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); | ||
| 22 | return UnimplementedFunction(ErrorModule::OS); | ||
| 23 | } | ||
| 24 | 18 | ||
| 25 | u32 base_address; ///< Address of shared memory block in RAM | 19 | u32 base_address; ///< Address of shared memory block in RAM |
| 26 | MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) | 20 | MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) |
| @@ -38,7 +32,8 @@ public: | |||
| 38 | */ | 32 | */ |
| 39 | SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) { | 33 | SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) { |
| 40 | SharedMemory* shared_memory = new SharedMemory; | 34 | SharedMemory* shared_memory = new SharedMemory; |
| 41 | handle = Kernel::g_object_pool.Create(shared_memory); | 35 | // TOOD(yuriks): Fix error reporting |
| 36 | handle = Kernel::g_handle_table.Create(shared_memory).ValueOr(INVALID_HANDLE); | ||
| 42 | shared_memory->name = name; | 37 | shared_memory->name = name; |
| 43 | return shared_memory; | 38 | return shared_memory; |
| 44 | } | 39 | } |
| @@ -61,12 +56,12 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions | |||
| 61 | MemoryPermission other_permissions) { | 56 | MemoryPermission other_permissions) { |
| 62 | 57 | ||
| 63 | if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { | 58 | if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { |
| 64 | ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", | 59 | LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", |
| 65 | handle, address); | 60 | handle, address); |
| 66 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | 61 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, |
| 67 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | 62 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); |
| 68 | } | 63 | } |
| 69 | SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); | 64 | SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); |
| 70 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); | 65 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 71 | 66 | ||
| 72 | shared_memory->base_address = address; | 67 | shared_memory->base_address = address; |
| @@ -77,13 +72,13 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions | |||
| 77 | } | 72 | } |
| 78 | 73 | ||
| 79 | ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { | 74 | ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { |
| 80 | SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); | 75 | SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); |
| 81 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); | 76 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 82 | 77 | ||
| 83 | if (0 != shared_memory->base_address) | 78 | if (0 != shared_memory->base_address) |
| 84 | return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset)); | 79 | return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset)); |
| 85 | 80 | ||
| 86 | ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle); | 81 | LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle); |
| 87 | // TODO(yuriks): Verify error code. | 82 | // TODO(yuriks): Verify error code. |
| 88 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | 83 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, |
| 89 | ErrorSummary::InvalidState, ErrorLevel::Permanent); | 84 | ErrorSummary::InvalidState, ErrorLevel::Permanent); |
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 304cf5b67..bb65c7ccd 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 | 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 | #pragma once | 5 | #pragma once |
| @@ -12,11 +12,15 @@ namespace Kernel { | |||
| 12 | 12 | ||
| 13 | /// Permissions for mapped shared memory blocks | 13 | /// Permissions for mapped shared memory blocks |
| 14 | enum class MemoryPermission : u32 { | 14 | enum class MemoryPermission : u32 { |
| 15 | None = 0, | 15 | None = 0, |
| 16 | Read = (1u << 0), | 16 | Read = (1u << 0), |
| 17 | Write = (1u << 1), | 17 | Write = (1u << 1), |
| 18 | ReadWrite = (Read | Write), | 18 | ReadWrite = (Read | Write), |
| 19 | DontCare = (1u << 28) | 19 | Execute = (1u << 2), |
| 20 | ReadExecute = (Read | Execute), | ||
| 21 | WriteExecute = (Write | Execute), | ||
| 22 | ReadWriteExecute = (Read | Write | Execute), | ||
| 23 | DontCare = (1u << 28) | ||
| 20 | }; | 24 | }; |
| 21 | 25 | ||
| 22 | /** | 26 | /** |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index f3f54a4e9..872df2d14 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 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 <algorithm> |
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/hle/hle.h" | 14 | #include "core/hle/hle.h" |
| 15 | #include "core/hle/kernel/kernel.h" | 15 | #include "core/hle/kernel/kernel.h" |
| 16 | #include "core/hle/kernel/thread.h" | 16 | #include "core/hle/kernel/thread.h" |
| 17 | #include "core/hle/kernel/mutex.h" | ||
| 17 | #include "core/hle/result.h" | 18 | #include "core/hle/result.h" |
| 18 | #include "core/mem_map.h" | 19 | #include "core/mem_map.h" |
| 19 | 20 | ||
| @@ -25,8 +26,8 @@ public: | |||
| 25 | std::string GetName() const override { return name; } | 26 | std::string GetName() const override { return name; } |
| 26 | std::string GetTypeName() const override { return "Thread"; } | 27 | std::string GetTypeName() const override { return "Thread"; } |
| 27 | 28 | ||
| 28 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } | 29 | static const HandleType HANDLE_TYPE = HandleType::Thread; |
| 29 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Thread; } | 30 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 30 | 31 | ||
| 31 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } | 32 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } |
| 32 | inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } | 33 | inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } |
| @@ -49,6 +50,8 @@ public: | |||
| 49 | 50 | ||
| 50 | ThreadContext context; | 51 | ThreadContext context; |
| 51 | 52 | ||
| 53 | u32 thread_id; | ||
| 54 | |||
| 52 | u32 status; | 55 | u32 status; |
| 53 | u32 entry_point; | 56 | u32 entry_point; |
| 54 | u32 stack_top; | 57 | u32 stack_top; |
| @@ -61,6 +64,7 @@ public: | |||
| 61 | 64 | ||
| 62 | WaitType wait_type; | 65 | WaitType wait_type; |
| 63 | Handle wait_handle; | 66 | Handle wait_handle; |
| 67 | VAddr wait_address; | ||
| 64 | 68 | ||
| 65 | std::vector<Handle> waiting_threads; | 69 | std::vector<Handle> waiting_threads; |
| 66 | 70 | ||
| @@ -76,8 +80,10 @@ static Common::ThreadQueueList<Handle> thread_ready_queue; | |||
| 76 | static Handle current_thread_handle; | 80 | static Handle current_thread_handle; |
| 77 | static Thread* current_thread; | 81 | static Thread* current_thread; |
| 78 | 82 | ||
| 79 | /// Gets the current thread | 83 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup |
| 80 | inline Thread* GetCurrentThread() { | 84 | static u32 next_thread_id; ///< The next available thread id |
| 85 | |||
| 86 | Thread* GetCurrentThread() { | ||
| 81 | return current_thread; | 87 | return current_thread; |
| 82 | } | 88 | } |
| 83 | 89 | ||
| @@ -121,6 +127,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | |||
| 121 | } | 127 | } |
| 122 | t->wait_type = WAITTYPE_NONE; | 128 | t->wait_type = WAITTYPE_NONE; |
| 123 | t->wait_handle = 0; | 129 | t->wait_handle = 0; |
| 130 | t->wait_address = 0; | ||
| 124 | } | 131 | } |
| 125 | 132 | ||
| 126 | /// Change a thread to "ready" state | 133 | /// Change a thread to "ready" state |
| @@ -140,30 +147,43 @@ void ChangeReadyState(Thread* t, bool ready) { | |||
| 140 | } | 147 | } |
| 141 | } | 148 | } |
| 142 | 149 | ||
| 143 | /// Verify that a thread has not been released from waiting | 150 | /// Check if a thread is blocking on a specified wait type |
| 144 | inline bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { | 151 | static bool CheckWaitType(const Thread* thread, WaitType type) { |
| 145 | _dbg_assert_(KERNEL, thread != nullptr); | 152 | return (type == thread->wait_type) && (thread->IsWaiting()); |
| 146 | return type == thread->wait_type && wait_handle == thread->wait_handle; | 153 | } |
| 154 | |||
| 155 | /// Check if a thread is blocking on a specified wait type with a specified handle | ||
| 156 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) { | ||
| 157 | return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle); | ||
| 158 | } | ||
| 159 | |||
| 160 | /// Check if a thread is blocking on a specified wait type with a specified handle and address | ||
| 161 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { | ||
| 162 | return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address); | ||
| 147 | } | 163 | } |
| 148 | 164 | ||
| 149 | /// Stops the current thread | 165 | /// Stops the current thread |
| 150 | ResultCode StopThread(Handle handle, const char* reason) { | 166 | ResultCode StopThread(Handle handle, const char* reason) { |
| 151 | Thread* thread = g_object_pool.Get<Thread>(handle); | 167 | Thread* thread = g_handle_table.Get<Thread>(handle); |
| 152 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); | 168 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 153 | 169 | ||
| 170 | // Release all the mutexes that this thread holds | ||
| 171 | ReleaseThreadMutexes(handle); | ||
| 172 | |||
| 154 | ChangeReadyState(thread, false); | 173 | ChangeReadyState(thread, false); |
| 155 | thread->status = THREADSTATUS_DORMANT; | 174 | thread->status = THREADSTATUS_DORMANT; |
| 156 | for (Handle waiting_handle : thread->waiting_threads) { | 175 | for (Handle waiting_handle : thread->waiting_threads) { |
| 157 | Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); | 176 | Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle); |
| 158 | if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { | 177 | |
| 178 | if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle)) | ||
| 159 | ResumeThreadFromWait(waiting_handle); | 179 | ResumeThreadFromWait(waiting_handle); |
| 160 | } | ||
| 161 | } | 180 | } |
| 162 | thread->waiting_threads.clear(); | 181 | thread->waiting_threads.clear(); |
| 163 | 182 | ||
| 164 | // Stopped threads are never waiting. | 183 | // Stopped threads are never waiting. |
| 165 | thread->wait_type = WAITTYPE_NONE; | 184 | thread->wait_type = WAITTYPE_NONE; |
| 166 | thread->wait_handle = 0; | 185 | thread->wait_handle = 0; |
| 186 | thread->wait_address = 0; | ||
| 167 | 187 | ||
| 168 | return RESULT_SUCCESS; | 188 | return RESULT_SUCCESS; |
| 169 | } | 189 | } |
| @@ -178,7 +198,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) { | |||
| 178 | 198 | ||
| 179 | if (new_status == THREADSTATUS_WAIT) { | 199 | if (new_status == THREADSTATUS_WAIT) { |
| 180 | if (t->wait_type == WAITTYPE_NONE) { | 200 | if (t->wait_type == WAITTYPE_NONE) { |
| 181 | ERROR_LOG(KERNEL, "Waittype none not allowed"); | 201 | LOG_ERROR(Kernel, "Waittype none not allowed"); |
| 182 | } | 202 | } |
| 183 | } | 203 | } |
| 184 | } | 204 | } |
| @@ -190,14 +210,14 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { | |||
| 190 | 210 | ||
| 191 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 211 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 192 | for (Handle handle : thread_queue) { | 212 | for (Handle handle : thread_queue) { |
| 193 | Thread* thread = g_object_pool.Get<Thread>(handle); | 213 | Thread* thread = g_handle_table.Get<Thread>(handle); |
| 194 | 214 | ||
| 195 | // TODO(bunnei): Verify arbiter address... | 215 | if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) |
| 196 | if (!VerifyWait(thread, WAITTYPE_ARB, arbiter)) | ||
| 197 | continue; | 216 | continue; |
| 198 | 217 | ||
| 199 | if (thread == nullptr) | 218 | if (thread == nullptr) |
| 200 | continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. | 219 | continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. |
| 220 | |||
| 201 | if(thread->current_priority <= priority) { | 221 | if(thread->current_priority <= priority) { |
| 202 | highest_priority_thread = handle; | 222 | highest_priority_thread = handle; |
| 203 | priority = thread->current_priority; | 223 | priority = thread->current_priority; |
| @@ -215,10 +235,9 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) { | |||
| 215 | 235 | ||
| 216 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 236 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 217 | for (Handle handle : thread_queue) { | 237 | for (Handle handle : thread_queue) { |
| 218 | Thread* thread = g_object_pool.Get<Thread>(handle); | 238 | Thread* thread = g_handle_table.Get<Thread>(handle); |
| 219 | 239 | ||
| 220 | // TODO(bunnei): Verify arbiter address... | 240 | if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) |
| 221 | if (VerifyWait(thread, WAITTYPE_ARB, arbiter)) | ||
| 222 | ResumeThreadFromWait(handle); | 241 | ResumeThreadFromWait(handle); |
| 223 | } | 242 | } |
| 224 | } | 243 | } |
| @@ -269,14 +288,9 @@ Thread* NextThread() { | |||
| 269 | if (next == 0) { | 288 | if (next == 0) { |
| 270 | return nullptr; | 289 | return nullptr; |
| 271 | } | 290 | } |
| 272 | return Kernel::g_object_pool.Get<Thread>(next); | 291 | return Kernel::g_handle_table.Get<Thread>(next); |
| 273 | } | 292 | } |
| 274 | 293 | ||
| 275 | /** | ||
| 276 | * Puts the current thread in the wait state for the given type | ||
| 277 | * @param wait_type Type of wait | ||
| 278 | * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread | ||
| 279 | */ | ||
| 280 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { | 294 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { |
| 281 | Thread* thread = GetCurrentThread(); | 295 | Thread* thread = GetCurrentThread(); |
| 282 | thread->wait_type = wait_type; | 296 | thread->wait_type = wait_type; |
| @@ -284,11 +298,18 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { | |||
| 284 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | 298 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); |
| 285 | } | 299 | } |
| 286 | 300 | ||
| 301 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) { | ||
| 302 | WaitCurrentThread(wait_type, wait_handle); | ||
| 303 | GetCurrentThread()->wait_address = wait_address; | ||
| 304 | } | ||
| 305 | |||
| 287 | /// Resumes a thread from waiting by marking it as "ready" | 306 | /// Resumes a thread from waiting by marking it as "ready" |
| 288 | void ResumeThreadFromWait(Handle handle) { | 307 | void ResumeThreadFromWait(Handle handle) { |
| 289 | Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); | 308 | Thread* thread = Kernel::g_handle_table.Get<Thread>(handle); |
| 290 | if (thread) { | 309 | if (thread) { |
| 291 | thread->status &= ~THREADSTATUS_WAIT; | 310 | thread->status &= ~THREADSTATUS_WAIT; |
| 311 | thread->wait_handle = 0; | ||
| 312 | thread->wait_type = WAITTYPE_NONE; | ||
| 292 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | 313 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { |
| 293 | ChangeReadyState(thread, true); | 314 | ChangeReadyState(thread, true); |
| 294 | } | 315 | } |
| @@ -301,12 +322,12 @@ void DebugThreadQueue() { | |||
| 301 | if (!thread) { | 322 | if (!thread) { |
| 302 | return; | 323 | return; |
| 303 | } | 324 | } |
| 304 | INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); | 325 | LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); |
| 305 | for (u32 i = 0; i < thread_queue.size(); i++) { | 326 | for (u32 i = 0; i < thread_queue.size(); i++) { |
| 306 | Handle handle = thread_queue[i]; | 327 | Handle handle = thread_queue[i]; |
| 307 | s32 priority = thread_ready_queue.contains(handle); | 328 | s32 priority = thread_ready_queue.contains(handle); |
| 308 | if (priority != -1) { | 329 | if (priority != -1) { |
| 309 | INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle); | 330 | LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle); |
| 310 | } | 331 | } |
| 311 | } | 332 | } |
| 312 | } | 333 | } |
| @@ -316,15 +337,17 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio | |||
| 316 | s32 processor_id, u32 stack_top, int stack_size) { | 337 | s32 processor_id, u32 stack_top, int stack_size) { |
| 317 | 338 | ||
| 318 | _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), | 339 | _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), |
| 319 | "CreateThread priority=%d, outside of allowable range!", priority) | 340 | "priority=%d, outside of allowable range!", priority) |
| 320 | 341 | ||
| 321 | Thread* thread = new Thread; | 342 | Thread* thread = new Thread; |
| 322 | 343 | ||
| 323 | handle = Kernel::g_object_pool.Create(thread); | 344 | // TOOD(yuriks): Fix error reporting |
| 345 | handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE); | ||
| 324 | 346 | ||
| 325 | thread_queue.push_back(handle); | 347 | thread_queue.push_back(handle); |
| 326 | thread_ready_queue.prepare(priority); | 348 | thread_ready_queue.prepare(priority); |
| 327 | 349 | ||
| 350 | thread->thread_id = next_thread_id++; | ||
| 328 | thread->status = THREADSTATUS_DORMANT; | 351 | thread->status = THREADSTATUS_DORMANT; |
| 329 | thread->entry_point = entry_point; | 352 | thread->entry_point = entry_point; |
| 330 | thread->stack_top = stack_top; | 353 | thread->stack_top = stack_top; |
| @@ -333,6 +356,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio | |||
| 333 | thread->processor_id = processor_id; | 356 | thread->processor_id = processor_id; |
| 334 | thread->wait_type = WAITTYPE_NONE; | 357 | thread->wait_type = WAITTYPE_NONE; |
| 335 | thread->wait_handle = 0; | 358 | thread->wait_handle = 0; |
| 359 | thread->wait_address = 0; | ||
| 336 | thread->name = name; | 360 | thread->name = name; |
| 337 | 361 | ||
| 338 | return thread; | 362 | return thread; |
| @@ -343,24 +367,24 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 | |||
| 343 | u32 stack_top, int stack_size) { | 367 | u32 stack_top, int stack_size) { |
| 344 | 368 | ||
| 345 | if (name == nullptr) { | 369 | if (name == nullptr) { |
| 346 | ERROR_LOG(KERNEL, "CreateThread(): nullptr name"); | 370 | LOG_ERROR(Kernel_SVC, "nullptr name"); |
| 347 | return -1; | 371 | return -1; |
| 348 | } | 372 | } |
| 349 | if ((u32)stack_size < 0x200) { | 373 | if ((u32)stack_size < 0x200) { |
| 350 | ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name, | 374 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name, |
| 351 | stack_size); | 375 | stack_size); |
| 352 | return -1; | 376 | return -1; |
| 353 | } | 377 | } |
| 354 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | 378 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { |
| 355 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 379 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); |
| 356 | WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", | 380 | LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", |
| 357 | name, priority, new_priority); | 381 | name, priority, new_priority); |
| 358 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | 382 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm |
| 359 | // validity of this | 383 | // validity of this |
| 360 | priority = new_priority; | 384 | priority = new_priority; |
| 361 | } | 385 | } |
| 362 | if (!Memory::GetPointer(entry_point)) { | 386 | if (!Memory::GetPointer(entry_point)) { |
| 363 | ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); | 387 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point); |
| 364 | return -1; | 388 | return -1; |
| 365 | } | 389 | } |
| 366 | Handle handle; | 390 | Handle handle; |
| @@ -375,7 +399,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 | |||
| 375 | 399 | ||
| 376 | /// Get the priority of the thread specified by handle | 400 | /// Get the priority of the thread specified by handle |
| 377 | ResultVal<u32> GetThreadPriority(const Handle handle) { | 401 | ResultVal<u32> GetThreadPriority(const Handle handle) { |
| 378 | Thread* thread = g_object_pool.Get<Thread>(handle); | 402 | Thread* thread = g_handle_table.Get<Thread>(handle); |
| 379 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); | 403 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 380 | 404 | ||
| 381 | return MakeResult<u32>(thread->current_priority); | 405 | return MakeResult<u32>(thread->current_priority); |
| @@ -387,7 +411,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) { | |||
| 387 | if (!handle) { | 411 | if (!handle) { |
| 388 | thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? | 412 | thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? |
| 389 | } else { | 413 | } else { |
| 390 | thread = g_object_pool.Get<Thread>(handle); | 414 | thread = g_handle_table.Get<Thread>(handle); |
| 391 | if (thread == nullptr) { | 415 | if (thread == nullptr) { |
| 392 | return InvalidHandle(ErrorModule::Kernel); | 416 | return InvalidHandle(ErrorModule::Kernel); |
| 393 | } | 417 | } |
| @@ -397,7 +421,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) { | |||
| 397 | // If priority is invalid, clamp to valid range | 421 | // If priority is invalid, clamp to valid range |
| 398 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | 422 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { |
| 399 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 423 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); |
| 400 | WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority); | 424 | LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority); |
| 401 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | 425 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm |
| 402 | // validity of this | 426 | // validity of this |
| 403 | priority = new_priority; | 427 | priority = new_priority; |
| @@ -450,24 +474,44 @@ void Reschedule() { | |||
| 450 | Thread* prev = GetCurrentThread(); | 474 | Thread* prev = GetCurrentThread(); |
| 451 | Thread* next = NextThread(); | 475 | Thread* next = NextThread(); |
| 452 | HLE::g_reschedule = false; | 476 | HLE::g_reschedule = false; |
| 453 | if (next > 0) { | ||
| 454 | INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); | ||
| 455 | 477 | ||
| 478 | if (next != nullptr) { | ||
| 479 | LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); | ||
| 456 | SwitchContext(next); | 480 | SwitchContext(next); |
| 481 | } else { | ||
| 482 | LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); | ||
| 457 | 483 | ||
| 458 | // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep | 484 | for (Handle handle : thread_queue) { |
| 459 | // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. | 485 | Thread* thread = g_handle_table.Get<Thread>(handle); |
| 460 | // This results in the current thread yielding on a VBLANK once, and then it will be | 486 | LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", |
| 461 | // immediately placed back in the queue for execution. | 487 | thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle); |
| 462 | if (prev->wait_type == WAITTYPE_VBLANK) { | ||
| 463 | ResumeThreadFromWait(prev->GetHandle()); | ||
| 464 | } | 488 | } |
| 465 | } | 489 | } |
| 490 | |||
| 491 | // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put | ||
| 492 | // to sleep. So, we'll just immediately set it to "ready" again after an attempted context | ||
| 493 | // switch has occurred. This results in the current thread yielding on a sleep once, and then it | ||
| 494 | // will immediately be placed back in the queue for execution. | ||
| 495 | |||
| 496 | if (CheckWaitType(prev, WAITTYPE_SLEEP)) | ||
| 497 | ResumeThreadFromWait(prev->GetHandle()); | ||
| 498 | } | ||
| 499 | |||
| 500 | ResultCode GetThreadId(u32* thread_id, Handle handle) { | ||
| 501 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 502 | if (thread == nullptr) | ||
| 503 | return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, | ||
| 504 | ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 505 | |||
| 506 | *thread_id = thread->thread_id; | ||
| 507 | |||
| 508 | return RESULT_SUCCESS; | ||
| 466 | } | 509 | } |
| 467 | 510 | ||
| 468 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 511 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 469 | 512 | ||
| 470 | void ThreadingInit() { | 513 | void ThreadingInit() { |
| 514 | next_thread_id = INITIAL_THREAD_ID; | ||
| 471 | } | 515 | } |
| 472 | 516 | ||
| 473 | void ThreadingShutdown() { | 517 | void ThreadingShutdown() { |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index ce63a70d3..0e1397cd9 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -1,10 +1,13 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project |
| 2 | // Licensed under GPLv2 | 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 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | |||
| 9 | #include "core/mem_map.h" | ||
| 10 | |||
| 8 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 9 | #include "core/hle/result.h" | 12 | #include "core/hle/result.h" |
| 10 | 13 | ||
| @@ -37,7 +40,6 @@ enum WaitType { | |||
| 37 | WAITTYPE_SEMA, | 40 | WAITTYPE_SEMA, |
| 38 | WAITTYPE_EVENT, | 41 | WAITTYPE_EVENT, |
| 39 | WAITTYPE_THREADEND, | 42 | WAITTYPE_THREADEND, |
| 40 | WAITTYPE_VBLANK, | ||
| 41 | WAITTYPE_MUTEX, | 43 | WAITTYPE_MUTEX, |
| 42 | WAITTYPE_SYNCH, | 44 | WAITTYPE_SYNCH, |
| 43 | WAITTYPE_ARB, | 45 | WAITTYPE_ARB, |
| @@ -58,6 +60,14 @@ void Reschedule(); | |||
| 58 | /// Stops the current thread | 60 | /// Stops the current thread |
| 59 | ResultCode StopThread(Handle thread, const char* reason); | 61 | ResultCode StopThread(Handle thread, const char* reason); |
| 60 | 62 | ||
| 63 | /** | ||
| 64 | * Retrieves the ID of the specified thread handle | ||
| 65 | * @param thread_id Will contain the output thread id | ||
| 66 | * @param handle Handle to the thread we want | ||
| 67 | * @return Whether the function was successful or not | ||
| 68 | */ | ||
| 69 | ResultCode GetThreadId(u32* thread_id, Handle handle); | ||
| 70 | |||
| 61 | /// Resumes a thread from waiting by marking it as "ready" | 71 | /// Resumes a thread from waiting by marking it as "ready" |
| 62 | void ResumeThreadFromWait(Handle handle); | 72 | void ResumeThreadFromWait(Handle handle); |
| 63 | 73 | ||
| @@ -77,6 +87,14 @@ Handle GetCurrentThreadHandle(); | |||
| 77 | */ | 87 | */ |
| 78 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); | 88 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); |
| 79 | 89 | ||
| 90 | /** | ||
| 91 | * Puts the current thread in the wait state for the given type | ||
| 92 | * @param wait_type Type of wait | ||
| 93 | * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread | ||
| 94 | * @param wait_address Arbitration address used to resume from wait | ||
| 95 | */ | ||
| 96 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address); | ||
| 97 | |||
| 80 | /// Put current thread in a wait state - on WaitSynchronization | 98 | /// Put current thread in a wait state - on WaitSynchronization |
| 81 | void WaitThread_Synchronization(); | 99 | void WaitThread_Synchronization(); |
| 82 | 100 | ||