mirror of
https://github.com/citra-emu/citra.git
synced 2024-12-19 02:31:04 +00:00
Merge pull request #28 from bunnei/shared-memory
Shared memory - adds preliminary support for shared memory kernel objects and correct block mapping.
This commit is contained in:
commit
6084084a5d
@ -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
|
||||
|
@ -170,6 +170,7 @@
|
||||
<ClCompile Include="hle\kernel\event.cpp" />
|
||||
<ClCompile Include="hle\kernel\kernel.cpp" />
|
||||
<ClCompile Include="hle\kernel\mutex.cpp" />
|
||||
<ClCompile Include="hle\kernel\shared_memory.cpp" />
|
||||
<ClCompile Include="hle\kernel\thread.cpp" />
|
||||
<ClCompile Include="hle\service\apt.cpp" />
|
||||
<ClCompile Include="hle\service\fs.cpp" />
|
||||
@ -222,6 +223,7 @@
|
||||
<ClInclude Include="hle\kernel\event.h" />
|
||||
<ClInclude Include="hle\kernel\kernel.h" />
|
||||
<ClInclude Include="hle\kernel\mutex.h" />
|
||||
<ClInclude Include="hle\kernel\shared_memory.h" />
|
||||
<ClInclude Include="hle\kernel\thread.h" />
|
||||
<ClInclude Include="hle\service\apt.h" />
|
||||
<ClInclude Include="hle\service\fs.h" />
|
||||
|
@ -179,6 +179,9 @@
|
||||
<ClCompile Include="file_sys\archive_romfs.cpp">
|
||||
<Filter>file_sys</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="hle\kernel\shared_memory.cpp">
|
||||
<Filter>hle\kernel</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="arm\disassembler\arm_disasm.h">
|
||||
@ -320,6 +323,9 @@
|
||||
<ClInclude Include="file_sys\archive_romfs.h">
|
||||
<Filter>file_sys</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="hle\kernel\shared_memory.h">
|
||||
<Filter>hle\kernel</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
105
src/core/hle/kernel/shared_memory.cpp
Normal file
105
src/core/hle/kernel/shared_memory.cpp
Normal file
@ -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
|
48
src/core/hle/kernel/shared_memory.h
Normal file
48
src/core/hle/kernel/shared_memory.h
Normal file
@ -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
|
@ -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() {
|
||||
|
@ -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<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;
|
||||
}
|
||||
|
@ -128,6 +128,12 @@ extern u8* g_exefs_code; ///< ExeFS:/.code is loaded here
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
template <typename T>
|
||||
inline void Read(T &var, const u32 addr);
|
||||
|
||||
template <typename T>
|
||||
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
|
||||
|
@ -41,7 +41,7 @@ u32 _VirtualAddress(const u32 addr) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
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<u8>(_var, addr);
|
||||
Read<u8>(_var, addr);
|
||||
return (u8)_var;
|
||||
}
|
||||
|
||||
u16 Read16(const u32 addr) {
|
||||
u16_le _var = 0;
|
||||
_Read<u16_le>(_var, addr);
|
||||
Read<u16_le>(_var, addr);
|
||||
return (u16)_var;
|
||||
}
|
||||
|
||||
u32 Read32(const u32 addr) {
|
||||
u32_le _var = 0;
|
||||
_Read<u32_le>(_var, addr);
|
||||
Read<u32_le>(_var, addr);
|
||||
return _var;
|
||||
}
|
||||
|
||||
u64 Read64(const u32 addr) {
|
||||
u64_le _var = 0;
|
||||
_Read<u64_le>(_var, addr);
|
||||
Read<u64_le>(_var, addr);
|
||||
return _var;
|
||||
}
|
||||
|
||||
@ -278,19 +256,19 @@ u32 Read16_ZX(const u32 addr) {
|
||||
}
|
||||
|
||||
void Write8(const u32 addr, const u8 data) {
|
||||
_Write<u8>(addr, data);
|
||||
Write<u8>(addr, data);
|
||||
}
|
||||
|
||||
void Write16(const u32 addr, const u16 data) {
|
||||
_Write<u16_le>(addr, data);
|
||||
Write<u16_le>(addr, data);
|
||||
}
|
||||
|
||||
void Write32(const u32 addr, const u32 data) {
|
||||
_Write<u32_le>(addr, data);
|
||||
Write<u32_le>(addr, data);
|
||||
}
|
||||
|
||||
void Write64(const u32 addr, const u64 data) {
|
||||
_Write<u64_le>(addr, data);
|
||||
Write<u64_le>(addr, data);
|
||||
}
|
||||
|
||||
void WriteBlock(const u32 addr, const u8* data, const int size) {
|
||||
|
Loading…
Reference in New Issue
Block a user