aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/config_mem.h6
-rw-r--r--src/core/hle/function_wrappers.h11
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp87
-rw-r--r--src/core/hle/kernel/address_arbiter.h36
-rw-r--r--src/core/hle/kernel/archive.cpp157
-rw-r--r--src/core/hle/kernel/archive.h38
-rw-r--r--src/core/hle/kernel/kernel.cpp3
-rw-r--r--src/core/hle/kernel/kernel.h3
-rw-r--r--src/core/hle/kernel/shared_memory.cpp105
-rw-r--r--src/core/hle/kernel/shared_memory.h48
-rw-r--r--src/core/hle/kernel/thread.cpp37
-rw-r--r--src/core/hle/kernel/thread.h7
-rw-r--r--src/core/hle/service/apt.cpp164
-rw-r--r--src/core/hle/service/fs.cpp148
-rw-r--r--src/core/hle/service/fs.h31
-rw-r--r--src/core/hle/service/gsp.cpp184
-rw-r--r--src/core/hle/service/gsp.h60
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/svc.cpp44
19 files changed, 1004 insertions, 167 deletions
diff --git a/src/core/hle/config_mem.h b/src/core/hle/config_mem.h
index da396a3e6..fa01b5cdb 100644
--- a/src/core/hle/config_mem.h
+++ b/src/core/hle/config_mem.h
@@ -1,10 +1,10 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
-// Configuration memory stores various hardware/kernel configuration settings. This memory page is
+// Configuration memory stores various hardware/kernel configuration settings. This memory page is
// read-only for ARM11 processes. I'm guessing this would normally be written to by the firmware/
// bootrom. Because we're not emulating this, and essentially just "stubbing" the functionality, I'm
// putting this as a subset of HLE for now.
@@ -16,6 +16,6 @@
namespace ConfigMem {
template <typename T>
-inline void Read(T &var, const u32 addr);
+void Read(T &var, const u32 addr);
} // namespace
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 0bed78653..ea603a1bb 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -39,9 +39,16 @@ template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() {
RETURN(retval);
}
-// TODO(bunnei): Is this correct? Probably not
+// TODO(bunnei): Is this correct? Probably not - Last parameter looks wrong for ArbitrateAddress
template<s32 func(u32, u32, u32, u32, s64)> void Wrap() {
- RETURN(func(PARAM(5), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(4) << 32) | PARAM(0))));
+ RETURN(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))));
+}
+
+template<s32 func(u32*)> void Wrap(){
+ u32 param_1 = 0;
+ u32 retval = func(&param_1);
+ Core::g_app_core->SetReg(1, param_1);
+ RETURN(retval);
}
template<s32 func(u32, s64)> void Wrap() {
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
new file mode 100644
index 000000000..61717bbe4
--- /dev/null
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -0,0 +1,87 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+
+#include "core/mem_map.h"
+
+#include "core/hle/hle.h"
+#include "core/hle/kernel/address_arbiter.h"
+#include "core/hle/kernel/thread.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Kernel namespace
+
+namespace Kernel {
+
+class AddressArbiter : public Object {
+public:
+ const char* GetTypeName() const { return "Arbiter"; }
+ const char* GetName() const { return name.c_str(); }
+
+ static Kernel::HandleType GetStaticHandleType() { return HandleType::AddressArbiter; }
+ Kernel::HandleType GetHandleType() const { return HandleType::AddressArbiter; }
+
+ std::string name; ///< Name of address arbiter object (optional)
+
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result WaitSynchronization(bool* wait) {
+ // TODO(bunnei): ImplementMe
+ ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
+ return 0;
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// Arbitrate an address
+Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
+ switch (type) {
+
+ // Signal thread(s) waiting for arbitrate address...
+ case ArbitrationType::Signal:
+ // Negative value means resume all threads
+ if (value < 0) {
+ ArbitrateAllThreads(handle, address);
+ } else {
+ // Resume first N threads
+ for(int i = 0; i < value; i++)
+ ArbitrateHighestPriorityThread(handle, address);
+ }
+ HLE::Reschedule(__func__);
+
+ // Wait current thread (acquire the arbiter)...
+ case ArbitrationType::WaitIfLessThan:
+ if ((s32)Memory::Read32(address) <= value) {
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, handle);
+ HLE::Reschedule(__func__);
+ }
+
+ default:
+ ERROR_LOG(KERNEL, "unknown type=%d", type);
+ return -1;
+ }
+ return 0;
+}
+
+/// Create an address arbiter
+AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) {
+ AddressArbiter* address_arbiter = new AddressArbiter;
+ handle = Kernel::g_object_pool.Create(address_arbiter);
+ address_arbiter->name = name;
+ return address_arbiter;
+}
+
+/// Create an address arbiter
+Handle CreateAddressArbiter(const std::string& name) {
+ Handle handle;
+ CreateAddressArbiter(handle, name);
+ return handle;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
new file mode 100644
index 000000000..a483fe466
--- /dev/null
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -0,0 +1,36 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+
+// Address arbiters are an underlying kernel synchronization object that can be created/used via
+// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
+// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
+// semphores.
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Kernel namespace
+
+namespace Kernel {
+
+/// Address arbitration types
+enum class ArbitrationType : u32 {
+ Signal,
+ WaitIfLessThan,
+ DecrementAndWaitIfLessThan,
+ WaitIfLessThanWithTimeout,
+ DecrementAndWaitIfLessThanWithTimeout,
+};
+
+/// Arbitrate an address
+Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
+
+/// Create an address arbiter
+Handle CreateAddressArbiter(const std::string& name = "Unknown");
+
+} // namespace FileSys
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp
new file mode 100644
index 000000000..76b2520da
--- /dev/null
+++ b/src/core/hle/kernel/archive.cpp
@@ -0,0 +1,157 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+
+#include "core/file_sys/archive.h"
+#include "core/hle/service/service.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/archive.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Kernel namespace
+
+namespace Kernel {
+
+// Command to access archive file
+enum class FileCommand : u32 {
+ Dummy1 = 0x000100C6,
+ Control = 0x040100C4,
+ OpenSubFile = 0x08010100,
+ Read = 0x080200C2,
+ Write = 0x08030102,
+ GetSize = 0x08040000,
+ SetSize = 0x08050080,
+ GetAttributes = 0x08060000,
+ SetAttributes = 0x08070040,
+ Close = 0x08080000,
+ Flush = 0x08090000,
+};
+
+class Archive : public Object {
+public:
+ const char* GetTypeName() const { return "Archive"; }
+ const char* GetName() const { return name.c_str(); }
+
+ static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
+ Kernel::HandleType GetHandleType() const { return HandleType::Archive; }
+
+ std::string name; ///< Name of archive (optional)
+ FileSys::Archive* backend; ///< Archive backend interface
+
+ /**
+ * Synchronize kernel object
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result SyncRequest(bool* wait) {
+ u32* cmd_buff = Service::GetCommandBuffer();
+ FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
+ switch (cmd) {
+
+ // Read from archive...
+ case FileCommand::Read:
+ {
+ u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
+ u32 length = cmd_buff[3];
+ u32 address = cmd_buff[5];
+ cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
+ break;
+ }
+
+ // Unknown command...
+ default:
+ ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
+ return -1;
+ }
+ cmd_buff[1] = 0; // No error
+ return 0;
+ }
+
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result WaitSynchronization(bool* wait) {
+ // TODO(bunnei): ImplementMe
+ ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
+ return 0;
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
+
+/**
+ * Opens an archive
+ * @param id_code IdCode of the archive to open
+ * @return Handle to archive if it exists, otherwise a null handle (0)
+ */
+Handle OpenArchive(FileSys::Archive::IdCode id_code) {
+ auto itr = g_archive_map.find(id_code);
+ if (itr == g_archive_map.end()) {
+ return 0;
+ }
+ return itr->second;
+}
+
+/**
+ * Mounts an archive
+ * @param archive Pointer to the archive to mount
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result MountArchive(Archive* archive) {
+ FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
+ if (0 != OpenArchive(id_code)) {
+ ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
+ return -1;
+ }
+ g_archive_map[id_code] = archive->GetHandle();
+ INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName());
+ return 0;
+}
+
+/**
+ * Creates an Archive
+ * @param handle Handle to newly created archive object
+ * @param backend File system backend interface to the archive
+ * @param name Optional name of Archive
+ * @return Newly created Archive object
+ */
+Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
+ Archive* archive = new Archive;
+ handle = Kernel::g_object_pool.Create(archive);
+ archive->name = name;
+ archive->backend = backend;
+
+ MountArchive(archive);
+
+ return archive;
+}
+
+/**
+ * Creates an Archive
+ * @param backend File system backend interface to the archive
+ * @param name Optional name of Archive
+ * @return Handle to newly created Archive object
+ */
+Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
+ Handle handle;
+ Archive* archive = CreateArchive(handle, backend, name);
+ return handle;
+}
+
+/// Initialize archives
+void ArchiveInit() {
+ g_archive_map.clear();
+}
+
+/// Shutdown archives
+void ArchiveShutdown() {
+ g_archive_map.clear();
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h
new file mode 100644
index 000000000..3758e7061
--- /dev/null
+++ b/src/core/hle/kernel/archive.h
@@ -0,0 +1,38 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/file_sys/archive.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Kernel namespace
+
+namespace Kernel {
+
+/**
+ * Opens an archive
+ * @param id_code IdCode of the archive to open
+ * @return Handle to archive if it exists, otherwise a null handle (0)
+ */
+Handle OpenArchive(FileSys::Archive::IdCode id_code);
+
+/**
+ * Creates an Archive
+ * @param backend File system backend interface to the archive
+ * @param name Optional name of Archive
+ * @return Handle to newly created Archive object
+ */
+Handle CreateArchive(FileSys::Archive* backend, const std::string& name);
+
+/// Initialize archives
+void ArchiveInit();
+
+/// Shutdown archives
+void ArchiveShutdown();
+
+} // namespace FileSys
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index cda183add..7d9bd261e 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -9,6 +9,7 @@
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/archive.h"
namespace Kernel {
@@ -133,11 +134,13 @@ Object* ObjectPool::CreateByIDType(int type) {
/// Initialize the kernel
void Init() {
Kernel::ThreadingInit();
+ Kernel::ArchiveInit();
}
/// Shutdown the kernel
void Shutdown() {
Kernel::ThreadingShutdown();
+ Kernel::ArchiveShutdown();
g_object_pool.Clear(); // Free all kernel objects
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 3f15da0ac..d9afcdd25 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -26,9 +26,10 @@ enum class HandleType : u32 {
Redirection = 6,
Thread = 7,
Process = 8,
- Arbiter = 9,
+ AddressArbiter = 9,
File = 10,
Semaphore = 11,
+ Archive = 12,
};
enum {
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
new file mode 100644
index 000000000..52823048f
--- /dev/null
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -0,0 +1,105 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/common.h"
+
+#include "core/mem_map.h"
+#include "core/hle/kernel/shared_memory.h"
+
+namespace Kernel {
+
+class SharedMemory : public Object {
+public:
+ const char* GetTypeName() const { return "SharedMemory"; }
+
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; }
+ Kernel::HandleType GetHandleType() const { return Kernel::HandleType::SharedMemory; }
+
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result WaitSynchronization(bool* wait) {
+ // TODO(bunnei): ImplementMe
+ ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
+ return 0;
+ }
+
+ u32 base_address; ///< Address of shared memory block in RAM
+ MemoryPermission permissions; ///< Permissions of shared memory block (SVC field)
+ MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field)
+ std::string name; ///< Name of shared memory object (optional)
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Creates a shared memory object
+ * @param handle Handle of newly created shared memory object
+ * @param name Name of shared memory object
+ * @return Pointer to newly created shared memory object
+ */
+SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
+ SharedMemory* shared_memory = new SharedMemory;
+ handle = Kernel::g_object_pool.Create(shared_memory);
+ shared_memory->name = name;
+ return shared_memory;
+}
+
+/**
+ * Creates a shared memory object
+ * @param name Optional name of shared memory object
+ * @return Handle of newly created shared memory object
+ */
+Handle CreateSharedMemory(const std::string& name) {
+ Handle handle;
+ CreateSharedMemory(handle, name);
+ return handle;
+}
+
+/**
+ * Maps a shared memory block to an address in system memory
+ * @param handle Shared memory block handle
+ * @param address Address in system memory to map shared memory block to
+ * @param permissions Memory block map permissions (specified by SVC field)
+ * @param other_permissions Memory block map other permissions (specified by SVC field)
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
+ MemoryPermission other_permissions) {
+
+ if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
+ ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
+ handle);
+ return -1;
+ }
+ SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
+ _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
+
+ shared_memory->base_address = address;
+ shared_memory->permissions = permissions;
+ shared_memory->other_permissions = other_permissions;
+
+ return 0;
+}
+
+/**
+ * Gets a pointer to the shared memory block
+ * @param handle Shared memory block handle
+ * @param offset Offset from the start of the shared memory block to get pointer
+ * @return Pointer to the shared memory block from the specified offset
+ */
+u8* GetSharedMemoryPointer(Handle handle, u32 offset) {
+ SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
+ _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
+
+ if (0 != shared_memory->base_address)
+ return Memory::GetPointer(shared_memory->base_address + offset);
+
+ ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle);
+ return nullptr;
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
new file mode 100644
index 000000000..5312b8854
--- /dev/null
+++ b/src/core/hle/kernel/shared_memory.h
@@ -0,0 +1,48 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+/// Permissions for mapped shared memory blocks
+enum class MemoryPermission : u32 {
+ None = 0,
+ Read = (1u << 0),
+ Write = (1u << 1),
+ ReadWrite = (Read | Write),
+ DontCare = (1u << 28)
+};
+
+/**
+ * Creates a shared memory object
+ * @param name Optional name of shared memory object
+ * @return Handle of newly created shared memory object
+ */
+Handle CreateSharedMemory(const std::string& name="Unknown");
+
+/**
+ * Maps a shared memory block to an address in system memory
+ * @param handle Shared memory block handle
+ * @param address Address in system memory to map shared memory block to
+ * @param permissions Memory block map permissions (specified by SVC field)
+ * @param other_permissions Memory block map other permissions (specified by SVC field)
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
+ MemoryPermission other_permissions);
+
+/**
+ * Gets a pointer to the shared memory block
+ * @param handle Shared memory block handle
+ * @param offset Offset from the start of the shared memory block to get pointer
+ * @return Pointer to the shared memory block from the specified offset
+ */
+u8* GetSharedMemoryPointer(Handle handle, u32 offset);
+
+} // namespace
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index ab5a5559e..86bbf29d0 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -188,6 +188,43 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
}
}
+/// Arbitrate the highest priority thread that is waiting
+Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
+ Handle highest_priority_thread = 0;
+ s32 priority = THREADPRIO_LOWEST;
+
+ // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
+ for (const auto& handle : g_thread_queue) {
+
+ // TODO(bunnei): Verify arbiter address...
+ if (!VerifyWait(handle, WAITTYPE_ARB, arbiter))
+ continue;
+
+ Thread* thread = g_object_pool.GetFast<Thread>(handle);
+ if(thread->current_priority <= priority) {
+ highest_priority_thread = handle;
+ priority = thread->current_priority;
+ }
+ }
+ // If a thread was arbitrated, resume it
+ if (0 != highest_priority_thread)
+ ResumeThreadFromWait(highest_priority_thread);
+
+ return highest_priority_thread;
+}
+
+/// Arbitrate all threads currently waiting
+void ArbitrateAllThreads(u32 arbiter, u32 address) {
+
+ // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
+ for (const auto& handle : g_thread_queue) {
+
+ // TODO(bunnei): Verify arbiter address...
+ if (VerifyWait(handle, WAITTYPE_ARB, arbiter))
+ ResumeThreadFromWait(handle);
+ }
+}
+
/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields)
void CallThread(Thread* t) {
// Stop waiting
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 04914ba90..f2bfdfa1a 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -39,6 +39,7 @@ enum WaitType {
WAITTYPE_VBLANK,
WAITTYPE_MUTEX,
WAITTYPE_SYNCH,
+ WAITTYPE_ARB,
};
namespace Kernel {
@@ -59,6 +60,12 @@ void StopThread(Handle thread, const char* reason);
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle);
+/// Arbitrate the highest priority thread that is waiting
+Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address);
+
+/// Arbitrate all threads currently waiting...
+void ArbitrateAllThreads(u32 arbiter, u32 address);
+
/// Gets the current thread handle
Handle GetCurrentThreadHandle();
diff --git a/src/core/hle/service/apt.cpp b/src/core/hle/service/apt.cpp
index a0012b5dd..e97e7dbf7 100644
--- a/src/core/hle/service/apt.cpp
+++ b/src/core/hle/service/apt.cpp
@@ -15,9 +15,16 @@
namespace APT_U {
+/// Signals used by APT functions
+enum class SignalType : u32 {
+ None = 0x0,
+ AppJustStarted = 0x1,
+ ReturningToApp = 0xB,
+ ExitingApp = 0xC,
+};
+
void Initialize(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
- DEBUG_LOG(KERNEL, "called");
cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
@@ -26,6 +33,7 @@ void Initialize(Service::Interface* self) {
Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event
cmd_buff[1] = 0; // No error
+ DEBUG_LOG(KERNEL, "called");
}
void GetLockHandle(Service::Interface* self) {
@@ -40,15 +48,29 @@ void Enable(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
cmd_buff[1] = 0; // No error
- ERROR_LOG(KERNEL, "(UNIMPEMENTED) called unk=0x%08X", unk);
+ WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X", unk);
}
void InquireNotification(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
u32 app_id = cmd_buff[2];
cmd_buff[1] = 0; // No error
- cmd_buff[3] = 0; // Signal type
- ERROR_LOG(KERNEL, "(UNIMPEMENTED) called app_id=0x%08X", app_id);
+ cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
+ WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X", app_id);
+}
+
+void ReceiveParameter(Service::Interface* self) {
+ u32* cmd_buff = Service::GetCommandBuffer();
+ u32 app_id = cmd_buff[1];
+ u32 buffer_size = cmd_buff[2];
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = 0;
+ cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
+ cmd_buff[4] = 0x10;
+ cmd_buff[5] = 0;
+ cmd_buff[6] = 0;
+ cmd_buff[7] = 0;
+ WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
}
const Interface::FunctionInfo FunctionTable[] = {
@@ -63,73 +85,73 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00090040, nullptr, "IsRegistered"},
{0x000A0040, nullptr, "GetAttribute"},
{0x000B0040, InquireNotification, "InquireNotification"},
- {0x000C0104, nullptr, "SendParameter"},
- {0x000D0080, nullptr, "ReceiveParameter"},
- {0x000E0080, nullptr, "GlanceParameter"},
- {0x000F0100, nullptr, "CancelParameter"},
- {0x001000C2, nullptr, "DebugFunc"},
- {0x001100C0, nullptr, "MapProgramIdForDebug"},
- {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
- {0x00130000, nullptr, "GetPreparationState"},
- {0x00140040, nullptr, "SetPreparationState"},
- {0x00150140, nullptr, "PrepareToStartApplication"},
- {0x00160040, nullptr, "PreloadLibraryApplet"},
- {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
- {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
- {0x00190040, nullptr, "PrepareToStartSystemApplet"},
- {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
- {0x001B00C4, nullptr, "StartApplication"},
- {0x001C0000, nullptr, "WakeupApplication"},
- {0x001D0000, nullptr, "CancelApplication"},
- {0x001E0084, nullptr, "StartLibraryApplet"},
- {0x001F0084, nullptr, "StartSystemApplet"},
- {0x00200044, nullptr, "StartNewestHomeMenu"},
- {0x00210000, nullptr, "OrderToCloseApplication"},
- {0x00220040, nullptr, "PrepareToCloseApplication"},
- {0x00230040, nullptr, "PrepareToJumpToApplication"},
- {0x00240044, nullptr, "JumpToApplication"},
- {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
- {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
- {0x00270044, nullptr, "CloseApplication"},
- {0x00280044, nullptr, "CloseLibraryApplet"},
- {0x00290044, nullptr, "CloseSystemApplet"},
- {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
- {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
- {0x002C0044, nullptr, "JumpToHomeMenu"},
- {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
- {0x002E0044, nullptr, "LeaveHomeMenu"},
- {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
- {0x00300044, nullptr, "LeaveResidentApplet"},
- {0x00310100, nullptr, "PrepareToDoApplicationJump"},
- {0x00320084, nullptr, "DoApplicationJump"},
- {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
- {0x00340084, nullptr, "SendDeliverArg"},
- {0x00350080, nullptr, "ReceiveDeliverArg"},
- {0x00360040, nullptr, "LoadSysMenuArg"},
- {0x00370042, nullptr, "StoreSysMenuArg"},
- {0x00380040, nullptr, "PreloadResidentApplet"},
- {0x00390040, nullptr, "PrepareToStartResidentApplet"},
- {0x003A0044, nullptr, "StartResidentApplet"},
- {0x003B0040, nullptr, "CancelLibraryApplet"},
- {0x003C0042, nullptr, "SendDspSleep"},
- {0x003D0042, nullptr, "SendDspWakeUp"},
- {0x003E0080, nullptr, "ReplySleepQuery"},
- {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
- {0x00400042, nullptr, "SendCaptureBufferInfo"},
- {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
- {0x00420080, nullptr, "SleepSystem"},
- {0x00430040, nullptr, "NotifyToWait"},
- {0x00440000, nullptr, "GetSharedFont"},
- {0x00450040, nullptr, "GetWirelessRebootInfo"},
- {0x00460104, nullptr, "Wrap"},
- {0x00470104, nullptr, "Unwrap"},
- {0x00480100, nullptr, "GetProgramInfo"},
- {0x00490180, nullptr, "Reboot"},
- {0x004A0040, nullptr, "GetCaptureInfo"},
- {0x004B00C2, nullptr, "AppletUtility"},
- {0x004C0000, nullptr, "SetFatalErrDispMode"},
- {0x004D0080, nullptr, "GetAppletProgramInfo"},
- {0x004E0000, nullptr, "HardwareResetAsync"},
+ {0x000C0104, nullptr, "SendParameter"},
+ {0x000D0080, ReceiveParameter, "ReceiveParameter"},
+ {0x000E0080, nullptr, "GlanceParameter"},
+ {0x000F0100, nullptr, "CancelParameter"},
+ {0x001000C2, nullptr, "DebugFunc"},
+ {0x001100C0, nullptr, "MapProgramIdForDebug"},
+ {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
+ {0x00130000, nullptr, "GetPreparationState"},
+ {0x00140040, nullptr, "SetPreparationState"},
+ {0x00150140, nullptr, "PrepareToStartApplication"},
+ {0x00160040, nullptr, "PreloadLibraryApplet"},
+ {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
+ {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
+ {0x00190040, nullptr, "PrepareToStartSystemApplet"},
+ {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
+ {0x001B00C4, nullptr, "StartApplication"},
+ {0x001C0000, nullptr, "WakeupApplication"},
+ {0x001D0000, nullptr, "CancelApplication"},
+ {0x001E0084, nullptr, "StartLibraryApplet"},
+ {0x001F0084, nullptr, "StartSystemApplet"},
+ {0x00200044, nullptr, "StartNewestHomeMenu"},
+ {0x00210000, nullptr, "OrderToCloseApplication"},
+ {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00230040, nullptr, "PrepareToJumpToApplication"},
+ {0x00240044, nullptr, "JumpToApplication"},
+ {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
+ {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
+ {0x00270044, nullptr, "CloseApplication"},
+ {0x00280044, nullptr, "CloseLibraryApplet"},
+ {0x00290044, nullptr, "CloseSystemApplet"},
+ {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
+ {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, nullptr, "JumpToHomeMenu"},
+ {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
+ {0x00300044, nullptr, "LeaveResidentApplet"},
+ {0x00310100, nullptr, "PrepareToDoApplicationJump"},
+ {0x00320084, nullptr, "DoApplicationJump"},
+ {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
+ {0x00340084, nullptr, "SendDeliverArg"},
+ {0x00350080, nullptr, "ReceiveDeliverArg"},
+ {0x00360040, nullptr, "LoadSysMenuArg"},
+ {0x00370042, nullptr, "StoreSysMenuArg"},
+ {0x00380040, nullptr, "PreloadResidentApplet"},
+ {0x00390040, nullptr, "PrepareToStartResidentApplet"},
+ {0x003A0044, nullptr, "StartResidentApplet"},
+ {0x003B0040, nullptr, "CancelLibraryApplet"},
+ {0x003C0042, nullptr, "SendDspSleep"},
+ {0x003D0042, nullptr, "SendDspWakeUp"},
+ {0x003E0080, nullptr, "ReplySleepQuery"},
+ {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
+ {0x00400042, nullptr, "SendCaptureBufferInfo"},
+ {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
+ {0x00420080, nullptr, "SleepSystem"},
+ {0x00430040, nullptr, "NotifyToWait"},
+ {0x00440000, nullptr, "GetSharedFont"},
+ {0x00450040, nullptr, "GetWirelessRebootInfo"},
+ {0x00460104, nullptr, "Wrap"},
+ {0x00470104, nullptr, "Unwrap"},
+ {0x00480100, nullptr, "GetProgramInfo"},
+ {0x00490180, nullptr, "Reboot"},
+ {0x004A0040, nullptr, "GetCaptureInfo"},
+ {0x004B00C2, nullptr, "AppletUtility"},
+ {0x004C0000, nullptr, "SetFatalErrDispMode"},
+ {0x004D0080, nullptr, "GetAppletProgramInfo"},
+ {0x004E0000, nullptr, "HardwareResetAsync"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/fs.cpp b/src/core/hle/service/fs.cpp
new file mode 100644
index 000000000..5eabf36ad
--- /dev/null
+++ b/src/core/hle/service/fs.cpp
@@ -0,0 +1,148 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/common.h"
+
+#include "core/loader/loader.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/fs.h"
+#include "core/hle/kernel/archive.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FS_User
+
+namespace FS_User {
+
+void Initialize(Service::Interface* self) {
+ u32* cmd_buff = Service::GetCommandBuffer();
+ cmd_buff[1] = 0; // No error
+ DEBUG_LOG(KERNEL, "called");
+}
+
+void OpenFileDirectly(Service::Interface* self) {
+ u32* cmd_buff = Service::GetCommandBuffer();
+
+ FileSys::Archive::IdCode arch_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]);
+
+ // TODO(bunnei): Properly implement use of these...
+ //u32 transaction = cmd_buff[1];
+ //u32 arch_lowpath_type = cmd_buff[3];
+ //u32 arch_lowpath_sz = cmd_buff[4];
+ //u32 file_lowpath_type = cmd_buff[5];
+ //u32 file_lowpath_sz = cmd_buff[6];
+ //u32 flags = cmd_buff[7];
+ //u32 attr = cmd_buff[8];
+ //u32 arch_lowpath_desc = cmd_buff[9];
+ //u32 arch_lowpath_ptr = cmd_buff[10];
+ //u32 file_lowpath_desc = cmd_buff[11];
+ //u32 file_lowpath_ptr = cmd_buff[12];
+
+ Handle handle = Kernel::OpenArchive(arch_id);
+ if (0 != handle) {
+ cmd_buff[1] = 0; // No error
+ cmd_buff[3] = handle;
+ }
+ DEBUG_LOG(KERNEL, "called");
+}
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000100C6, nullptr, "Dummy1"},
+ {0x040100C4, nullptr, "Control"},
+ {0x08010002, Initialize, "Initialize"},
+ {0x080201C2, nullptr, "OpenFile"},
+ {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
+ {0x08040142, nullptr, "DeleteFile"},
+ {0x08050244, nullptr, "RenameFile"},
+ {0x08060142, nullptr, "DeleteDirectory"},
+ {0x08070142, nullptr, "DeleteDirectoryRecursively"},
+ {0x08080202, nullptr, "CreateFile"},
+ {0x08090182, nullptr, "CreateDirectory"},
+ {0x080A0244, nullptr, "RenameDirectory"},
+ {0x080B0102, nullptr, "OpenDirectory"},
+ {0x080C00C2, nullptr, "OpenArchive"},
+ {0x080D0144, nullptr, "ControlArchive"},
+ {0x080E0080, nullptr, "CloseArchive"},
+ {0x080F0180, nullptr, "FormatThisUserSaveData"},
+ {0x08100200, nullptr, "CreateSystemSaveData"},
+ {0x08110040, nullptr, "DeleteSystemSaveData"},
+ {0x08120080, nullptr, "GetFreeBytes"},
+ {0x08130000, nullptr, "GetCardType"},
+ {0x08140000, nullptr, "GetSdmcArchiveResource"},
+ {0x08150000, nullptr, "GetNandArchiveResource"},
+ {0x08160000, nullptr, "GetSdmcFatfsErro"},
+ {0x08170000, nullptr, "IsSdmcDetected"},
+ {0x08180000, nullptr, "IsSdmcWritable"},
+ {0x08190042, nullptr, "GetSdmcCid"},
+ {0x081A0042, nullptr, "GetNandCid"},
+ {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
+ {0x081C0000, nullptr, "GetNandSpeedInfo"},
+ {0x081D0042, nullptr, "GetSdmcLog"},
+ {0x081E0042, nullptr, "GetNandLog"},
+ {0x081F0000, nullptr, "ClearSdmcLog"},
+ {0x08200000, nullptr, "ClearNandLog"},
+ {0x08210000, nullptr, "CardSlotIsInserted"},
+ {0x08220000, nullptr, "CardSlotPowerOn"},
+ {0x08230000, nullptr, "CardSlotPowerOff"},
+ {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
+ {0x08250040, nullptr, "CardNorDirectCommand"},
+ {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
+ {0x08270082, nullptr, "CardNorDirectRead"},
+ {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
+ {0x08290082, nullptr, "CardNorDirectWrite"},
+ {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
+ {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
+ {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
+ {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
+ {0x082E0040, nullptr, "GetProductInfo"},
+ {0x082F0040, nullptr, "GetProgramLaunchInfo"},
+ {0x08300182, nullptr, "CreateExtSaveData"},
+ {0x08310180, nullptr, "CreateSharedExtSaveData"},
+ {0x08320102, nullptr, "ReadExtSaveDataIcon"},
+ {0x08330082, nullptr, "EnumerateExtSaveData"},
+ {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
+ {0x08350080, nullptr, "DeleteExtSaveData"},
+ {0x08360080, nullptr, "DeleteSharedExtSaveData"},
+ {0x08370040, nullptr, "SetCardSpiBaudRate"},
+ {0x08380040, nullptr, "SetCardSpiBusMode"},
+ {0x08390000, nullptr, "SendInitializeInfoTo9"},
+ {0x083A0100, nullptr, "GetSpecialContentIndex"},
+ {0x083B00C2, nullptr, "GetLegacyRomHeader"},
+ {0x083C00C2, nullptr, "GetLegacyBannerData"},
+ {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
+ {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
+ {0x083F00C0, nullptr, "GetExtDataBlockSize"},
+ {0x08400040, nullptr, "AbnegateAccessRight"},
+ {0x08410000, nullptr, "DeleteSdmcRoot"},
+ {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
+ {0x08430000, nullptr, "InitializeCtrFileSystem"},
+ {0x08440000, nullptr, "CreateSeed"},
+ {0x084500C2, nullptr, "GetFormatInfo"},
+ {0x08460102, nullptr, "GetLegacyRomHeader2"},
+ {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
+ {0x08480042, nullptr, "GetSdmcCtrRootPath"},
+ {0x08490040, nullptr, "GetArchiveResource"},
+ {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
+ {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
+ {0x084C0242, nullptr, "FormatSaveData"},
+ {0x084D0102, nullptr, "GetLegacySubBannerData"},
+ {0x084E0342, nullptr, "UpdateSha256Context"},
+ {0x084F0102, nullptr, "ReadSpecialFile"},
+ {0x08500040, nullptr, "GetSpecialFileSize"},
+ {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
+ {0x08610042, nullptr, "InitializeWithSdkVersion"},
+ {0x08620040, nullptr, "SetPriority"},
+ {0x08630000, nullptr, "GetPriority"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/fs.h b/src/core/hle/service/fs.h
new file mode 100644
index 000000000..34b0610ad
--- /dev/null
+++ b/src/core/hle/service/fs.h
@@ -0,0 +1,31 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FS_User
+
+namespace FS_User {
+
+/// Interface to "fs:USER" service
+class Interface : public Service::Interface {
+public:
+
+ Interface();
+
+ ~Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ const char *GetPortName() const {
+ return "Ufs:";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp
index f75ba75c2..b20203e27 100644
--- a/src/core/hle/service/gsp.cpp
+++ b/src/core/hle/service/gsp.cpp
@@ -9,6 +9,7 @@
#include "core/mem_map.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/gsp.h"
#include "core/hw/gpu.h"
@@ -36,14 +37,19 @@ union GX_CmdBufferHeader {
BitField<8,8,u32> number_commands;
};
-/// Gets the address of the start (header) of a command buffer in GSP shared memory
-static inline u32 GX_GetCmdBufferAddress(u32 thread_id) {
- return (0x10002000 + 0x800 + (thread_id * 0x200));
-}
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace GSP_GPU
+
+namespace GSP_GPU {
+
+Handle g_event = 0;
+Handle g_shared_memory = 0;
+
+u32 g_thread_id = 0;
/// Gets a pointer to the start (header) of a command buffer in GSP shared memory
static inline u8* GX_GetCmdBufferPointer(u32 thread_id, u32 offset=0) {
- return Memory::GetPointer(GX_GetCmdBufferAddress(thread_id) + offset);
+ return Kernel::GetSharedMemoryPointer(g_shared_memory, 0x800 + (thread_id * 0x200) + offset);
}
/// Finishes execution of a GSP command
@@ -56,122 +62,183 @@ void GX_FinishCommand(u32 thread_id) {
// TODO: Increment header->index?
}
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace GSP_GPU
+/// Write a GSP GPU hardware register
+void WriteHWRegs(Service::Interface* self) {
+ u32* cmd_buff = Service::GetCommandBuffer();
+ u32 reg_addr = cmd_buff[1];
+ u32 size = cmd_buff[2];
-namespace GSP_GPU {
+ // TODO: Return proper error codes
+ if (reg_addr + size >= 0x420000) {
+ ERROR_LOG(GPU, "Write address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
+ return;
+ }
-Handle g_event_handle = 0;
-u32 g_thread_id = 0;
+ // size should be word-aligned
+ if ((size % 4) != 0) {
+ ERROR_LOG(GPU, "Invalid size 0x%08x", size);
+ return;
+ }
-enum {
- REG_FRAMEBUFFER_1 = 0x00400468,
- REG_FRAMEBUFFER_2 = 0x00400494,
-};
+ u32* src = (u32*)Memory::GetPointer(cmd_buff[0x4]);
+
+ while (size > 0) {
+ GPU::Write<u32>(reg_addr + 0x1EB00000, *src);
+
+ size -= 4;
+ ++src;
+ reg_addr += 4;
+ }
+}
/// Read a GSP GPU hardware register
void ReadHWRegs(Service::Interface* self) {
- static const u32 framebuffer_1[] = {GPU::PADDR_VRAM_TOP_LEFT_FRAME1, GPU::PADDR_VRAM_TOP_RIGHT_FRAME1};
- static const u32 framebuffer_2[] = {GPU::PADDR_VRAM_TOP_LEFT_FRAME2, GPU::PADDR_VRAM_TOP_RIGHT_FRAME2};
-
u32* cmd_buff = Service::GetCommandBuffer();
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
- u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]);
- switch (reg_addr) {
+ // TODO: Return proper error codes
+ if (reg_addr + size >= 0x420000) {
+ ERROR_LOG(GPU, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
+ return;
+ }
- // NOTE: Calling SetFramebufferLocation here is a hack... Not sure the correct way yet to set
- // whether the framebuffers should be in VRAM or GSP heap, but from what I understand, if the
- // user application is reading from either of these registers, then its going to be in VRAM.
+ // size should be word-aligned
+ if ((size % 4) != 0) {
+ ERROR_LOG(GPU, "Invalid size 0x%08x", size);
+ return;
+ }
- // Top framebuffer 1 addresses
- case REG_FRAMEBUFFER_1:
- GPU::SetFramebufferLocation(GPU::FRAMEBUFFER_LOCATION_VRAM);
- memcpy(dst, framebuffer_1, size);
- break;
+ u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]);
- // Top framebuffer 2 addresses
- case REG_FRAMEBUFFER_2:
- GPU::SetFramebufferLocation(GPU::FRAMEBUFFER_LOCATION_VRAM);
- memcpy(dst, framebuffer_2, size);
- break;
+ while (size > 0) {
+ GPU::Read<u32>(*dst, reg_addr + 0x1EB00000);
- default:
- ERROR_LOG(GSP, "unknown register read at address %08X", reg_addr);
+ size -= 4;
+ ++dst;
+ reg_addr += 4;
}
-
}
+/**
+ * GSP_GPU::RegisterInterruptRelayQueue service function
+ * Inputs:
+ * 1 : "Flags" field, purpose is unknown
+ * 3 : Handle to GSP synchronization event
+ * Outputs:
+ * 0 : Result of function, 0 on success, otherwise error code
+ * 2 : Thread index into GSP command buffer
+ * 4 : Handle to GSP shared memory
+ */
void RegisterInterruptRelayQueue(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
u32 flags = cmd_buff[1];
- u32 event_handle = cmd_buff[3];
-
- _assert_msg_(GSP, (event_handle != 0), "called, but event is nullptr!");
+ g_event = cmd_buff[3];
- g_event_handle = event_handle;
+ _assert_msg_(GSP, (g_event != 0), "handle is not valid!");
- Kernel::SetEventLocked(event_handle, false);
+ Kernel::SetEventLocked(g_event, false);
- // Hack - This function will permanently set the state of the GSP event such that GPU command
- // synchronization barriers always passthrough. Correct solution would be to set this after the
+ // Hack - This function will permanently set the state of the GSP event such that GPU command
+ // synchronization barriers always passthrough. Correct solution would be to set this after the
// GPU as processed all queued up commands, but due to the emulator being single-threaded they
// will always be ready.
- Kernel::SetPermanentLock(event_handle, true);
+ Kernel::SetPermanentLock(g_event, true);
- cmd_buff[2] = g_thread_id; // ThreadID
+ cmd_buff[0] = 0; // Result - no error
+ cmd_buff[2] = g_thread_id; // ThreadID
+ cmd_buff[4] = g_shared_memory; // GSP shared memory
}
/// This triggers handling of the GX command written to the command buffer in shared memory.
void TriggerCmdReqQueue(Service::Interface* self) {
+
+ // Utility function to convert register ID to address
+ auto WriteGPURegister = [](u32 id, u32 data) {
+ GPU::Write<u32>(0x1EF00000 + 4 * id, data);
+ };
+
GX_CmdBufferHeader* header = (GX_CmdBufferHeader*)GX_GetCmdBufferPointer(g_thread_id);
- u32* cmd_buff = (u32*)GX_GetCmdBufferPointer(g_thread_id, 0x20 + (header->index * 0x20));
+ auto& command = *(const GXCommand*)GX_GetCmdBufferPointer(g_thread_id, 0x20 + (header->index * 0x20));
- switch (static_cast<GXCommandId>(cmd_buff[0])) {
+ switch (command.id) {
// GX request DMA - typically used for copying memory from GSP heap to VRAM
case GXCommandId::REQUEST_DMA:
- memcpy(Memory::GetPointer(cmd_buff[2]), Memory::GetPointer(cmd_buff[1]), cmd_buff[3]);
+ memcpy(Memory::GetPointer(command.dma_request.dest_address),
+ Memory::GetPointer(command.dma_request.source_address),
+ command.dma_request.size);
break;
+ // ctrulib homebrew sends all relevant command list data with this command,
+ // hence we do all "interesting" stuff here and do nothing in SET_COMMAND_LIST_FIRST.
+ // TODO: This will need some rework in the future.
case GXCommandId::SET_COMMAND_LIST_LAST:
- GPU::Write<u32>(GPU::Registers::CommandListAddress, cmd_buff[1] >> 3);
- GPU::Write<u32>(GPU::Registers::CommandListSize, cmd_buff[2] >> 3);
- GPU::Write<u32>(GPU::Registers::ProcessCommandList, 1); // TODO: Not sure if we are supposed to always write this
+ {
+ auto& params = command.set_command_list_last;
+ WriteGPURegister(GPU::Regs::CommandProcessor + 2, params.address >> 3);
+ WriteGPURegister(GPU::Regs::CommandProcessor, params.size >> 3);
+ WriteGPURegister(GPU::Regs::CommandProcessor + 4, 1); // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though
// TODO: Move this to GPU
// TODO: Not sure what units the size is measured in
- g_debugger.CommandListCalled(cmd_buff[1], (u32*)Memory::GetPointer(cmd_buff[1]), cmd_buff[2]);
+ g_debugger.CommandListCalled(params.address,
+ (u32*)Memory::GetPointer(params.address),
+ params.size);
break;
+ }
+ // It's assumed that the two "blocks" behave equivalently.
+ // Presumably this is done simply to allow two memory fills to run in parallel.
case GXCommandId::SET_MEMORY_FILL:
+ {
+ auto& params = command.memory_fill;
+ WriteGPURegister(GPU::Regs::MemoryFill, params.start1 >> 3);
+ WriteGPURegister(GPU::Regs::MemoryFill + 1, params.end1 >> 3);
+ WriteGPURegister(GPU::Regs::MemoryFill + 2, params.end1 - params.start1);
+ WriteGPURegister(GPU::Regs::MemoryFill + 3, params.value1);
+
+ WriteGPURegister(GPU::Regs::MemoryFill + 4, params.start2 >> 3);
+ WriteGPURegister(GPU::Regs::MemoryFill + 5, params.end2 >> 3);
+ WriteGPURegister(GPU::Regs::MemoryFill + 6, params.end2 - params.start2);
+ WriteGPURegister(GPU::Regs::MemoryFill + 7, params.value2);
break;
+ }
+ // TODO: Check if texture copies are implemented correctly..
case GXCommandId::SET_DISPLAY_TRANSFER:
- break;
-
case GXCommandId::SET_TEXTURE_COPY:
+ {
+ auto& params = command.image_copy;
+ WriteGPURegister(GPU::Regs::DisplayTransfer, params.in_buffer_address >> 3);
+ WriteGPURegister(GPU::Regs::DisplayTransfer + 1, params.out_buffer_address >> 3);
+ WriteGPURegister(GPU::Regs::DisplayTransfer + 3, params.in_buffer_size);
+ WriteGPURegister(GPU::Regs::DisplayTransfer + 2, params.out_buffer_size);
+ WriteGPURegister(GPU::Regs::DisplayTransfer + 4, params.flags);
+
+ // TODO: Should this only be ORed with 1 for texture copies?
+ // trigger transfer
+ WriteGPURegister(GPU::Regs::DisplayTransfer + 6, 1);
break;
+ }
+ // TODO: Figure out what exactly SET_COMMAND_LIST_FIRST and SET_COMMAND_LIST_LAST
+ // are supposed to do.
case GXCommandId::SET_COMMAND_LIST_FIRST:
{
- //u32* buf0_data = (u32*)Memory::GetPointer(cmd_buff[1]);
- //u32* buf1_data = (u32*)Memory::GetPointer(cmd_buff[3]);
- //u32* buf2_data = (u32*)Memory::GetPointer(cmd_buff[5]);
break;
}
default:
- ERROR_LOG(GSP, "unknown command 0x%08X", cmd_buff[0]);
+ ERROR_LOG(GSP, "unknown command 0x%08X", (int)command.id.Value());
}
GX_FinishCommand(g_thread_id);
}
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010082, nullptr, "WriteHWRegs"},
+ {0x00010082, WriteHWRegs, "WriteHWRegs"},
{0x00020084, nullptr, "WriteHWRegsWithMask"},
{0x00030082, nullptr, "WriteHWRegRepeat"},
{0x00040080, ReadHWRegs, "ReadHWRegs"},
@@ -208,6 +275,7 @@ const Interface::FunctionInfo FunctionTable[] = {
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem");
}
Interface::~Interface() {
diff --git a/src/core/hle/service/gsp.h b/src/core/hle/service/gsp.h
index 214de140f..a83cb4846 100644
--- a/src/core/hle/service/gsp.h
+++ b/src/core/hle/service/gsp.h
@@ -4,6 +4,7 @@
#pragma once
+#include "common/bit_field.h"
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -12,21 +13,58 @@
namespace GSP_GPU {
enum class GXCommandId : u32 {
- REQUEST_DMA = 0x00000000,
- SET_COMMAND_LIST_LAST = 0x00000001,
- SET_MEMORY_FILL = 0x00000002, // TODO: Confirm? (lictru uses 0x01000102)
- SET_DISPLAY_TRANSFER = 0x00000003,
- SET_TEXTURE_COPY = 0x00000004,
- SET_COMMAND_LIST_FIRST = 0x00000005,
+ REQUEST_DMA = 0x00,
+ SET_COMMAND_LIST_LAST = 0x01,
+
+ // Fills a given memory range with a particular value
+ SET_MEMORY_FILL = 0x02,
+
+ // Copies an image and optionally performs color-conversion or scaling.
+ // This is highly similar to the GameCube's EFB copy feature
+ SET_DISPLAY_TRANSFER = 0x03,
+
+ // Conceptionally similar to SET_DISPLAY_TRANSFER and presumable uses the same hardware path
+ SET_TEXTURE_COPY = 0x04,
+
+ SET_COMMAND_LIST_FIRST = 0x05,
};
-union GXCommand {
- struct {
- GXCommandId id;
- };
+struct GXCommand {
+ BitField<0, 8, GXCommandId> id;
- u32 data[0x20];
+ union {
+ struct {
+ u32 source_address;
+ u32 dest_address;
+ u32 size;
+ } dma_request;
+
+ struct {
+ u32 address;
+ u32 size;
+ } set_command_list_last;
+
+ struct {
+ u32 start1;
+ u32 value1;
+ u32 end1;
+ u32 start2;
+ u32 value2;
+ u32 end2;
+ } memory_fill;
+
+ struct {
+ u32 in_buffer_address;
+ u32 out_buffer_address;
+ u32 in_buffer_size;
+ u32 out_buffer_size;
+ u32 flags;
+ } image_copy;
+
+ u8 raw_data[0x1C];
+ };
};
+static_assert(sizeof(GXCommand) == 0x20, "GXCommand struct has incorrect size");
/// Interface to "srv:" service
class Interface : public Service::Interface {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 4a1ac857e..d3af2768a 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -10,6 +10,7 @@
#include "core/hle/service/service.h"
#include "core/hle/service/apt.h"
+#include "core/hle/service/fs.h"
#include "core/hle/service/gsp.h"
#include "core/hle/service/hid.h"
#include "core/hle/service/ndm.h"
@@ -71,6 +72,7 @@ void Init() {
g_manager->AddService(new SRV::Interface);
g_manager->AddService(new APT_U::Interface);
+ g_manager->AddService(new FS_User::Interface);
g_manager->AddService(new GSP_GPU::Interface);
g_manager->AddService(new HID_User::Interface);
g_manager->AddService(new NDM_U::Interface);
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 441d8ce8d..17967f260 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -9,9 +9,11 @@
#include "core/mem_map.h"
+#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/function_wrappers.h"
@@ -28,11 +30,6 @@ enum ControlMemoryOperation {
MEMORY_OPERATION_GSP_HEAP = 0x00010003,
};
-enum MapMemoryPermission {
- MEMORY_PERMISSION_UNMAP = 0x00000000,
- MEMORY_PERMISSION_NORMAL = 0x00000001,
-};
-
/// Map application or GSP heap memory
Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
@@ -58,17 +55,21 @@ Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 siz
}
/// Maps a memory block to specified address
-Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherpermission) {
+Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
DEBUG_LOG(SVC, "called memblock=0x08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
- memblock, addr, mypermissions, otherpermission);
- switch (mypermissions) {
- case MEMORY_PERMISSION_NORMAL:
- case MEMORY_PERMISSION_NORMAL + 1:
- case MEMORY_PERMISSION_NORMAL + 2:
- Memory::MapBlock_Shared(memblock, addr, mypermissions);
+ handle, addr, permissions, other_permissions);
+
+ Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions);
+ switch (permissions_type) {
+ case Kernel::MemoryPermission::Read:
+ case Kernel::MemoryPermission::Write:
+ case Kernel::MemoryPermission::ReadWrite:
+ case Kernel::MemoryPermission::DontCare:
+ Kernel::MapSharedMemory(handle, addr, permissions_type,
+ static_cast<Kernel::MemoryPermission>(other_permissions));
break;
default:
- ERROR_LOG(OSHLE, "unknown permissions=0x%08X", mypermissions);
+ ERROR_LOG(OSHLE, "unknown permissions=0x%08X", permissions);
}
return 0;
}
@@ -175,18 +176,19 @@ Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wa
}
/// Create an address arbiter (to allocate access to shared resources)
-Result CreateAddressArbiter(void* arbiter) {
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called");
- Core::g_app_core->SetReg(1, 0xFABBDADD);
+Result CreateAddressArbiter(u32* arbiter) {
+ DEBUG_LOG(SVC, "called");
+ Handle handle = Kernel::CreateAddressArbiter();
+ *arbiter = handle;
return 0;
}
/// Arbitrate address
-Result ArbitrateAddress(Handle arbiter, u32 addr, u32 _type, u32 value, s64 nanoseconds) {
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called");
- ArbitrationType type = (ArbitrationType)_type;
- Memory::Write32(addr, type);
- return 0;
+Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
+ DEBUG_LOG(SVC, "called arbiter=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X, "
+ "nanoseconds=%d", arbiter, address, type, value, nanoseconds);
+ return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), address,
+ value);
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit