diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 9ee803fda..0d3189d29 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -38,6 +38,7 @@ set(SRCS core.cpp
hle/kernel/event.cpp
hle/kernel/kernel.cpp
hle/kernel/mutex.cpp
+ hle/kernel/shared_memory.cpp
hle/kernel/thread.cpp
hle/service/apt.cpp
hle/service/fs.cpp
@@ -85,6 +86,7 @@ set(HEADERS core.h
hle/kernel/archive.h
hle/kernel/kernel.h
hle/kernel/mutex.h
+ hle/kernel/shared_memory.h
hle/kernel/thread.h
hle/function_wrappers.h
hle/service/apt.h
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj
index 4e521903c..a09a4a1f1 100644
--- a/src/core/core.vcxproj
+++ b/src/core/core.vcxproj
@@ -170,6 +170,7 @@
+
@@ -222,6 +223,7 @@
+
diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters
index 17829b8b1..7e6a7f91b 100644
--- a/src/core/core.vcxproj.filters
+++ b/src/core/core.vcxproj.filters
@@ -179,6 +179,9 @@
file_sys
+
+ hle\kernel
+
@@ -320,6 +323,9 @@
file_sys
+
+ hle\kernel
+
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(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(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/service/gsp.cpp b/src/core/hle/service/gsp.cpp
index f75ba75c2..1fdbdf342 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,24 @@ 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;
+
+enum {
+ REG_FRAMEBUFFER_1 = 0x00400468,
+ REG_FRAMEBUFFER_2 = 0x00400494,
+};
/// 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,19 +67,6 @@ void GX_FinishCommand(u32 thread_id) {
// TODO: Increment header->index?
}
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace GSP_GPU
-
-namespace GSP_GPU {
-
-Handle g_event_handle = 0;
-u32 g_thread_id = 0;
-
-enum {
- REG_FRAMEBUFFER_1 = 0x00400468,
- REG_FRAMEBUFFER_2 = 0x00400494,
-};
-
/// 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};
@@ -103,24 +101,34 @@ void ReadHWRegs(Service::Interface* self) {
}
+/**
+ * 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];
+ g_event = cmd_buff[3];
- _assert_msg_(GSP, (event_handle != 0), "called, but event is nullptr!");
+ _assert_msg_(GSP, (g_event != 0), "handle is not valid!");
- g_event_handle = event_handle;
-
- 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
// 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
}
@@ -208,6 +216,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/svc.cpp b/src/core/hle/svc.cpp
index 441d8ce8d..9130d77ec 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -12,6 +12,7 @@
#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 +29,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 +54,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(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(other_permissions));
break;
default:
- ERROR_LOG(OSHLE, "unknown permissions=0x%08X", mypermissions);
+ ERROR_LOG(OSHLE, "unknown permissions=0x%08X", permissions);
}
return 0;
}
diff --git a/src/core/mem_map.h b/src/core/mem_map.h
index d5899e4bb..12941f558 100644
--- a/src/core/mem_map.h
+++ b/src/core/mem_map.h
@@ -128,6 +128,12 @@ extern u8* g_exefs_code; ///< ExeFS:/.code is loaded here
void Init();
void Shutdown();
+template
+inline void Read(T &var, const u32 addr);
+
+template
+inline void Write(u32 addr, const T data);
+
u8 Read8(const u32 addr);
u16 Read16(const u32 addr);
u32 Read32(const u32 addr);
@@ -143,14 +149,6 @@ void WriteBlock(const u32 addr, const u8* data, const int size);
u8* GetPointer(const u32 Address);
-/**
- * Maps a block of memory in shared memory
- * @param handle Handle to map memory block for
- * @param addr Address to map memory block to
- * @param permissions Memory map permissions
- */
-u32 MapBlock_Shared(u32 handle, u32 addr,u32 permissions) ;
-
/**
* Maps a block of memory on the heap
* @param size Size of block in bytes
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp
index 37913119e..0342122df 100644
--- a/src/core/mem_map_funcs.cpp
+++ b/src/core/mem_map_funcs.cpp
@@ -41,7 +41,7 @@ u32 _VirtualAddress(const u32 addr) {
}
template
-inline void _Read(T &var, const u32 addr) {
+inline void Read(T &var, const u32 addr) {
// TODO: Figure out the fastest order of tests for both read and write (they are probably different).
// TODO: Make sure this represents the mirrors in a correct way.
// Could just do a base-relative read, too.... TODO
@@ -91,7 +91,7 @@ inline void _Read(T &var, const u32 addr) {
}
template
-inline void _Write(u32 addr, const T data) {
+inline void Write(u32 addr, const T data) {
u32 vaddr = _VirtualAddress(addr);
// Kernel memory command buffer
@@ -177,28 +177,6 @@ u8 *GetPointer(const u32 addr) {
}
}
-/**
- * Maps a block of memory in shared memory
- * @param handle Handle to map memory block for
- * @param addr Address to map memory block to
- * @param permissions Memory map permissions
- */
-u32 MapBlock_Shared(u32 handle, u32 addr,u32 permissions) {
- MemoryBlock block;
-
- block.handle = handle;
- block.base_address = addr;
- block.permissions = permissions;
-
- if (g_shared_map.size() > 0) {
- const MemoryBlock last_block = g_shared_map.rbegin()->second;
- block.address = last_block.address + last_block.size;
- }
- g_shared_map[block.GetVirtualAddress()] = block;
-
- return block.GetVirtualAddress();
-}
-
/**
* Maps a block of memory on the heap
* @param size Size of block in bytes
@@ -247,25 +225,25 @@ u32 MapBlock_HeapGSP(u32 size, u32 operation, u32 permissions) {
u8 Read8(const u32 addr) {
u8 _var = 0;
- _Read(_var, addr);
+ Read(_var, addr);
return (u8)_var;
}
u16 Read16(const u32 addr) {
u16_le _var = 0;
- _Read(_var, addr);
+ Read(_var, addr);
return (u16)_var;
}
u32 Read32(const u32 addr) {
u32_le _var = 0;
- _Read(_var, addr);
+ Read(_var, addr);
return _var;
}
u64 Read64(const u32 addr) {
u64_le _var = 0;
- _Read(_var, addr);
+ Read(_var, addr);
return _var;
}
@@ -278,19 +256,19 @@ u32 Read16_ZX(const u32 addr) {
}
void Write8(const u32 addr, const u8 data) {
- _Write(addr, data);
+ Write(addr, data);
}
void Write16(const u32 addr, const u16 data) {
- _Write(addr, data);
+ Write(addr, data);
}
void Write32(const u32 addr, const u32 data) {
- _Write(addr, data);
+ Write(addr, data);
}
void Write64(const u32 addr, const u64 data) {
- _Write(addr, data);
+ Write(addr, data);
}
void WriteBlock(const u32 addr, const u8* data, const int size) {