Merge pull request #28 from bunnei/shared-memory

Shared memory - adds preliminary support for shared memory kernel objects and correct block mapping.
master
bunnei 2014-07-05 10:28:14 +07:00
commit 6084084a5d
9 changed files with 226 additions and 78 deletions

@ -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" />

@ -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

@ -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) {