citra-emu
/
citra
Archived
1
0
Fork 0

Merge pull request #1689 from Subv/shmem

Kernel: Implemented shared memory.
This commit is contained in:
bunnei 2016-05-13 15:33:44 -04:00
commit 18b517e236
18 changed files with 420 additions and 131 deletions

View File

@ -52,6 +52,7 @@ set(SRCS
hle/service/apt/apt_a.cpp hle/service/apt/apt_a.cpp
hle/service/apt/apt_s.cpp hle/service/apt/apt_s.cpp
hle/service/apt/apt_u.cpp hle/service/apt/apt_u.cpp
hle/service/apt/bcfnt/bcfnt.cpp
hle/service/boss/boss.cpp hle/service/boss/boss.cpp
hle/service/boss/boss_p.cpp hle/service/boss/boss_p.cpp
hle/service/boss/boss_u.cpp hle/service/boss/boss_u.cpp
@ -185,6 +186,7 @@ set(HEADERS
hle/service/apt/apt_a.h hle/service/apt/apt_a.h
hle/service/apt/apt_s.h hle/service/apt/apt_s.h
hle/service/apt/apt_u.h hle/service/apt/apt_u.h
hle/service/apt/bcfnt/bcfnt.h
hle/service/boss/boss.h hle/service/boss/boss.h
hle/service/boss/boss_p.h hle/service/boss/boss_p.h
hle/service/boss/boss_u.h hle/service/boss/boss_u.h

View File

@ -65,6 +65,7 @@ protected:
virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0; virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0;
Service::APT::AppletId id; ///< Id of this Applet Service::APT::AppletId id; ///< Id of this Applet
std::shared_ptr<std::vector<u8>> heap_memory; ///< Heap memory for this Applet
}; };
/// Returns whether a library applet is currently running /// Returns whether a library applet is currently running

View File

@ -35,9 +35,14 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
ASSERT(sizeof(capture_info) == parameter.buffer_size); ASSERT(sizeof(capture_info) == parameter.buffer_size);
memcpy(&capture_info, parameter.data, sizeof(capture_info)); memcpy(&capture_info, parameter.data, sizeof(capture_info));
using Kernel::MemoryPermission; using Kernel::MemoryPermission;
framebuffer_memory = Kernel::SharedMemory::Create(capture_info.size, MemoryPermission::ReadWrite, // Allocate a heap block of the required size for this applet.
MemoryPermission::ReadWrite, "MiiSelector Memory"); heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
// Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(),
MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
"MiiSelector Memory");
// Send the response message with the newly created SharedMemory // Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result; Service::APT::MessageParameter result;

View File

@ -40,8 +40,12 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
memcpy(&capture_info, parameter.data, sizeof(capture_info)); memcpy(&capture_info, parameter.data, sizeof(capture_info));
using Kernel::MemoryPermission; using Kernel::MemoryPermission;
framebuffer_memory = Kernel::SharedMemory::Create(capture_info.size, MemoryPermission::ReadWrite, // Allocate a heap block of the required size for this applet.
MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
// Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(),
MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
"SoftwareKeyboard Memory");
// Send the response message with the newly created SharedMemory // Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result; Service::APT::MessageParameter result;

View File

@ -170,7 +170,8 @@ template<ResultCode func(s64*, u32, s32)> void Wrap() {
template<ResultCode func(u32*, u32, u32, u32, u32)> void Wrap() { template<ResultCode func(u32*, u32, u32, u32, u32)> void Wrap() {
u32 param_1 = 0; u32 param_1 = 0;
u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw; // The last parameter is passed in R0 instead of R4
u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(0)).raw;
Core::g_app_core->SetReg(1, param_1); Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval); FuncReturn(retval);
} }

View File

@ -107,7 +107,6 @@ struct MemoryArea {
// We don't declare the IO regions in here since its handled by other means. // We don't declare the IO regions in here since its handled by other means.
static MemoryArea memory_areas[] = { static MemoryArea memory_areas[] = {
{SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory
{VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM) {VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM)
}; };

View File

@ -209,7 +209,7 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p
return ERR_INVALID_ADDRESS; return ERR_INVALID_ADDRESS;
} }
// Expansion of the linear heap is only allowed if you do an allocation immediatelly at its // Expansion of the linear heap is only allowed if you do an allocation immediately at its
// end. It's possible to free gaps in the middle of the heap and then reallocate them later, // end. It's possible to free gaps in the middle of the heap and then reallocate them later,
// but expansions are only allowed at the end. // but expansions are only allowed at the end.
if (target == heap_end) { if (target == heap_end) {

View File

@ -7,6 +7,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/shared_memory.h"
namespace Kernel { namespace Kernel {
@ -14,93 +15,157 @@ namespace Kernel {
SharedMemory::SharedMemory() {} SharedMemory::SharedMemory() {}
SharedMemory::~SharedMemory() {} SharedMemory::~SharedMemory() {}
SharedPtr<SharedMemory> SharedMemory::Create(u32 size, MemoryPermission permissions, SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions,
MemoryPermission other_permissions, std::string name) { MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory); SharedPtr<SharedMemory> shared_memory(new SharedMemory);
shared_memory->owner_process = owner_process;
shared_memory->name = std::move(name); shared_memory->name = std::move(name);
shared_memory->base_address = 0x0;
shared_memory->fixed_address = 0x0;
shared_memory->size = size; shared_memory->size = size;
shared_memory->permissions = permissions; shared_memory->permissions = permissions;
shared_memory->other_permissions = other_permissions; shared_memory->other_permissions = other_permissions;
if (address == 0) {
// We need to allocate a block from the Linear Heap ourselves.
// We'll manually allocate some memory from the linear heap in the specified region.
MemoryRegionInfo* memory_region = GetMemoryRegion(region);
auto& linheap_memory = memory_region->linear_heap_memory;
ASSERT_MSG(linheap_memory->size() + size <= memory_region->size, "Not enough space in region to allocate shared memory!");
shared_memory->backing_block = linheap_memory;
shared_memory->backing_block_offset = linheap_memory->size();
// Allocate some memory from the end of the linear heap for this region.
linheap_memory->insert(linheap_memory->end(), size, 0);
memory_region->used += size;
shared_memory->linear_heap_phys_address = Memory::FCRAM_PADDR + memory_region->base + shared_memory->backing_block_offset;
// Increase the amount of used linear heap memory for the owner process.
if (shared_memory->owner_process != nullptr) {
shared_memory->owner_process->linear_heap_used += size;
}
// Refresh the address mappings for the current process.
if (Kernel::g_current_process != nullptr) {
Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
}
} else {
// TODO(Subv): What happens if an application tries to create multiple memory blocks pointing to the same address?
auto& vm_manager = shared_memory->owner_process->vm_manager;
// The memory is already available and mapped in the owner process.
auto vma = vm_manager.FindVMA(address)->second;
// Copy it over to our own storage
shared_memory->backing_block = std::make_shared<std::vector<u8>>(vma.backing_block->data() + vma.offset,
vma.backing_block->data() + vma.offset + size);
shared_memory->backing_block_offset = 0;
// Unmap the existing pages
vm_manager.UnmapRange(address, size);
// Map our own block into the address space
vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size, MemoryState::Shared);
// Reprotect the block with the new permissions
vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions));
}
shared_memory->base_address = address;
return shared_memory; return shared_memory;
} }
ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, SharedPtr<SharedMemory> SharedMemory::CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
shared_memory->owner_process = nullptr;
shared_memory->name = std::move(name);
shared_memory->size = size;
shared_memory->permissions = permissions;
shared_memory->other_permissions = other_permissions;
shared_memory->backing_block = heap_block;
shared_memory->backing_block_offset = offset;
shared_memory->base_address = Memory::HEAP_VADDR + offset;
return shared_memory;
}
ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
MemoryPermission other_permissions) { MemoryPermission other_permissions) {
if (base_address != 0) { MemoryPermission own_other_permissions = target_process == owner_process ? this->permissions : this->other_permissions;
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: already mapped at 0x%08X!",
GetObjectId(), address, name.c_str(), base_address); // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
// TODO: Verify error code with hardware if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
} }
// TODO(Subv): Return E0E01BEE when permissions and other_permissions don't // Error out if the requested permissions don't match what the creator process allows.
// match what was specified when the memory block was created. if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
// TODO(Subv): Return E0E01BEE when address should be 0.
// Note: Find out when that's the case.
if (fixed_address != 0) {
if (address != 0 && address != fixed_address) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: fixed_addres is 0x%08X!",
GetObjectId(), address, name.c_str(), fixed_address);
// TODO: Verify error code with hardware
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
// HACK(yuriks): This is only here to support the APT shared font mapping right now.
// Later, this should actually map the memory block onto the address space.
return RESULT_SUCCESS;
}
if (address < Memory::SHARED_MEMORY_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s outside of shared mem bounds!",
GetObjectId(), address, name.c_str()); GetObjectId(), address, name.c_str());
// TODO: Verify error code with hardware return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
} }
// TODO: Test permissions // Heap-backed memory blocks can not be mapped with other_permissions = DontCare
if (base_address != 0 && other_permissions == MemoryPermission::DontCare) {
// HACK: Since there's no way to write to the memory block without mapping it onto the game LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
// process yet, at least initialize memory the first time it's mapped. GetObjectId(), address, name.c_str());
if (address != this->base_address) { return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
std::memset(Memory::GetPointer(address), 0, size);
} }
this->base_address = address; // Error out if the provided permissions are not compatible with what the creator process needs.
if (other_permissions != MemoryPermission::DontCare &&
static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
GetObjectId(), address, name.c_str());
return ResultCode(ErrorDescription::WrongPermission, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
}
return RESULT_SUCCESS; // TODO(Subv): Check for the Shared Device Mem flag in the creator process.
/*if (was_created_with_shared_device_mem && address != 0) {
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}*/
// TODO(Subv): The same process that created a SharedMemory object
// can not map it in its own address space unless it was created with addr=0, result 0xD900182C.
if (address != 0) {
if (address < Memory::HEAP_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, invalid address",
GetObjectId(), address, name.c_str());
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
}
VAddr target_address = address;
if (base_address == 0 && target_address == 0) {
// Calculate the address at which to map the memory block.
target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address);
}
// Map the memory block into the target process
auto result = target_process->vm_manager.MapMemoryBlock(target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
if (result.Failed()) {
LOG_ERROR(Kernel, "cannot map id=%u, target_address=0x%08X name=%s, error mapping to virtual memory",
GetObjectId(), target_address, name.c_str());
return result.Code();
}
return target_process->vm_manager.ReprotectRange(target_address, size, ConvertPermissions(permissions));
} }
ResultCode SharedMemory::Unmap(VAddr address) { ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) {
if (base_address == 0) { // TODO(Subv): Verify what happens if the application tries to unmap an address that is not mapped to a SharedMemory.
// TODO(Subv): Verify what actually happens when you want to unmap a memory block that return target_process->vm_manager.UnmapRange(address, size);
// was originally mapped with address = 0
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
if (base_address != address)
return ResultCode(ErrorDescription::WrongAddress, ErrorModule::OS, ErrorSummary::InvalidState, ErrorLevel::Usage);
base_address = 0;
return RESULT_SUCCESS;
} }
VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
u32 masked_permissions = static_cast<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute);
return static_cast<VMAPermission>(masked_permissions);
};
u8* SharedMemory::GetPointer(u32 offset) { u8* SharedMemory::GetPointer(u32 offset) {
if (base_address != 0) return backing_block->data() + backing_block_offset + offset;
return Memory::GetPointer(base_address + offset);
LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId());
return nullptr;
} }
} // namespace } // namespace

View File

@ -9,6 +9,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/result.h" #include "core/hle/result.h"
namespace Kernel { namespace Kernel {
@ -29,14 +30,29 @@ enum class MemoryPermission : u32 {
class SharedMemory final : public Object { class SharedMemory final : public Object {
public: public:
/** /**
* Creates a shared memory object * Creates a shared memory object.
* @param owner_process Process that created this shared memory object.
* @param size Size of the memory block. Must be page-aligned.
* @param permissions Permission restrictions applied to the process which created the block.
* @param other_permissions Permission restrictions applied to other processes mapping the block.
* @param address The address from which to map the Shared Memory.
* @param region If the address is 0, the shared memory will be allocated in this region of the linear heap.
* @param name Optional object name, used for debugging purposes.
*/
static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address = 0, MemoryRegion region = MemoryRegion::BASE, std::string name = "Unknown");
/**
* Creates a shared memory object from a block of memory managed by an HLE applet.
* @param heap_block Heap block of the HLE applet.
* @param offset The offset into the heap block that the SharedMemory will map.
* @param size Size of the memory block. Must be page-aligned. * @param size Size of the memory block. Must be page-aligned.
* @param permissions Permission restrictions applied to the process which created the block. * @param permissions Permission restrictions applied to the process which created the block.
* @param other_permissions Permission restrictions applied to other processes mapping the block. * @param other_permissions Permission restrictions applied to other processes mapping the block.
* @param name Optional object name, used for debugging purposes. * @param name Optional object name, used for debugging purposes.
*/ */
static SharedPtr<SharedMemory> Create(u32 size, MemoryPermission permissions, static SharedPtr<SharedMemory> CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
MemoryPermission other_permissions, std::string name = "Unknown"); MemoryPermission permissions, MemoryPermission other_permissions, std::string name = "Unknown Applet");
std::string GetTypeName() const override { return "SharedMemory"; } std::string GetTypeName() const override { return "SharedMemory"; }
std::string GetName() const override { return name; } std::string GetName() const override { return name; }
@ -45,19 +61,27 @@ public:
HandleType GetHandleType() const override { return HANDLE_TYPE; } HandleType GetHandleType() const override { return HANDLE_TYPE; }
/** /**
* Maps a shared memory block to an address in system memory * Converts the specified MemoryPermission into the equivalent VMAPermission.
* @param permission The MemoryPermission to convert.
*/
static VMAPermission ConvertPermissions(MemoryPermission permission);
/**
* Maps a shared memory block to an address in the target process' address space
* @param target_process Process on which to map the memory block.
* @param address Address in system memory to map shared memory block to * @param address Address in system memory to map shared memory block to
* @param permissions Memory block map permissions (specified by SVC field) * @param permissions Memory block map permissions (specified by SVC field)
* @param other_permissions Memory block map other permissions (specified by SVC field) * @param other_permissions Memory block map other permissions (specified by SVC field)
*/ */
ResultCode Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions); ResultCode Map(Process* target_process, VAddr address, MemoryPermission permissions, MemoryPermission other_permissions);
/** /**
* Unmaps a shared memory block from the specified address in system memory * Unmaps a shared memory block from the specified address in system memory
* @param target_process Process from which to umap the memory block.
* @param address Address in system memory where the shared memory block is mapped * @param address Address in system memory where the shared memory block is mapped
* @return Result code of the unmap operation * @return Result code of the unmap operation
*/ */
ResultCode Unmap(VAddr address); ResultCode Unmap(Process* target_process, VAddr address);
/** /**
* Gets a pointer to the shared memory block * Gets a pointer to the shared memory block
@ -66,10 +90,16 @@ public:
*/ */
u8* GetPointer(u32 offset = 0); u8* GetPointer(u32 offset = 0);
/// Address of shared memory block in the process. /// Process that created this shared memory block.
SharedPtr<Process> owner_process;
/// Address of shared memory block in the owner process if specified.
VAddr base_address; VAddr base_address;
/// Fixed address to allow mapping to. Used for blocks created from the linear heap. /// Physical address of the shared memory block in the linear heap if no address was specified during creation.
VAddr fixed_address; PAddr linear_heap_phys_address;
/// Backing memory for this shared memory block.
std::shared_ptr<std::vector<u8>> backing_block;
/// Offset into the backing block for this shared memory.
u32 backing_block_offset;
/// Size of the memory block. Page-aligned. /// Size of the memory block. Page-aligned.
u32 size; u32 size;
/// Permission restrictions applied to the process which created the block. /// Permission restrictions applied to the process which created the block.

View File

@ -17,6 +17,7 @@
/// Detailed description of the error. This listing is likely incomplete. /// Detailed description of the error. This listing is likely incomplete.
enum class ErrorDescription : u32 { enum class ErrorDescription : u32 {
Success = 0, Success = 0,
WrongPermission = 46,
OS_InvalidBufferDescriptor = 48, OS_InvalidBufferDescriptor = 48,
WrongAddress = 53, WrongAddress = 53,
FS_NotFound = 120, FS_NotFound = 120,

View File

@ -12,6 +12,7 @@
#include "core/hle/service/apt/apt_a.h" #include "core/hle/service/apt/apt_a.h"
#include "core/hle/service/apt/apt_s.h" #include "core/hle/service/apt/apt_s.h"
#include "core/hle/service/apt/apt_u.h" #include "core/hle/service/apt/apt_u.h"
#include "core/hle/service/apt/bcfnt/bcfnt.h"
#include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/archive.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
@ -22,23 +23,14 @@
namespace Service { namespace Service {
namespace APT { namespace APT {
// Address used for shared font (as observed on HW)
// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via
// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any
// address other than 0x18000000 due to internal pointers in the shared font dump that would need to
// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then
// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
static const VAddr SHARED_FONT_VADDR = 0x18000000;
/// Handle to shared memory region designated to for shared system font /// Handle to shared memory region designated to for shared system font
static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
static bool shared_font_relocated = false;
static Kernel::SharedPtr<Kernel::Mutex> lock; static Kernel::SharedPtr<Kernel::Mutex> lock;
static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event
static std::shared_ptr<std::vector<u8>> shared_font;
static u32 cpu_percent; ///< CPU time available to the running application static u32 cpu_percent; ///< CPU time available to the running application
/// Parameter data to be returned in the next call to Glance/ReceiveParameter /// Parameter data to be returned in the next call to Glance/ReceiveParameter
@ -74,23 +66,25 @@ void Initialize(Service::Interface* self) {
void GetSharedFont(Service::Interface* self) { void GetSharedFont(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer(); u32* cmd_buff = Kernel::GetCommandBuffer();
if (shared_font != nullptr) { // The shared font has to be relocated to the new address before being passed to the application.
// TODO(yuriks): This is a hack to keep this working right now even with our completely VAddr target_address = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address);
// broken shared memory system. // The shared font dumped by 3dsutils (https://github.com/citra-emu/3dsutils) uses this address as base,
shared_font_mem->fixed_address = SHARED_FONT_VADDR; // so we relocate it from there to our real address.
Kernel::g_current_process->vm_manager.MapMemoryBlock(shared_font_mem->fixed_address, // TODO(Subv): This address is wrong if the shared font is dumped from a n3DS,
shared_font, 0, shared_font_mem->size, Kernel::MemoryState::Shared); // we need a way to automatically calculate the original address of the font from the file.
static const VAddr SHARED_FONT_VADDR = 0x18000000;
if (!shared_font_relocated) {
BCFNT::RelocateSharedFont(shared_font_mem, SHARED_FONT_VADDR, target_address);
shared_font_relocated = true;
}
cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = SHARED_FONT_VADDR; // Since the SharedMemory interface doesn't provide the address at which the memory was allocated,
// the real APT service calculates this address by scanning the entire address space (using svcQueryMemory)
// and searches for an allocation of the same size as the Shared Font.
cmd_buff[2] = target_address;
cmd_buff[3] = IPC::MoveHandleDesc(); cmd_buff[3] = IPC::MoveHandleDesc();
cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom(); cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
} else {
cmd_buff[0] = IPC::MakeHeader(0x44, 1, 0);
cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
}
} }
void NotifyToWait(Service::Interface* self) { void NotifyToWait(Service::Interface* self) {
@ -433,14 +427,12 @@ void Init() {
FileUtil::IOFile file(filepath, "rb"); FileUtil::IOFile file(filepath, "rb");
if (file.IsOpen()) { if (file.IsOpen()) {
// Read shared font data
shared_font = std::make_shared<std::vector<u8>>((size_t)file.GetSize());
file.ReadBytes(shared_font->data(), shared_font->size());
// Create shared font memory object // Create shared font memory object
using Kernel::MemoryPermission; using Kernel::MemoryPermission;
shared_font_mem = Kernel::SharedMemory::Create(3 * 1024 * 1024, // 3MB shared_font_mem = Kernel::SharedMemory::Create(nullptr, 0x332000, // 3272 KB
MemoryPermission::ReadWrite, MemoryPermission::Read, "APT_U:shared_font_mem"); MemoryPermission::ReadWrite, MemoryPermission::Read, 0, Kernel::MemoryRegion::SYSTEM, "APT:SharedFont");
// Read shared font data
file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
} else { } else {
LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str()); LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
shared_font_mem = nullptr; shared_font_mem = nullptr;
@ -459,8 +451,8 @@ void Init() {
} }
void Shutdown() { void Shutdown() {
shared_font = nullptr;
shared_font_mem = nullptr; shared_font_mem = nullptr;
shared_font_relocated = false;
lock = nullptr; lock = nullptr;
notification_event = nullptr; notification_event = nullptr;
parameter_event = nullptr; parameter_event = nullptr;

View File

@ -0,0 +1,71 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/apt/bcfnt/bcfnt.h"
#include "core/hle/service/service.h"
namespace Service {
namespace APT {
namespace BCFNT {
void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address) {
static const u32 SharedFontStartOffset = 0x80;
u8* data = shared_font->GetPointer(SharedFontStartOffset);
CFNT cfnt;
memcpy(&cfnt, data, sizeof(cfnt));
// Advance past the header
data = shared_font->GetPointer(SharedFontStartOffset + cfnt.header_size);
for (unsigned block = 0; block < cfnt.num_blocks; ++block) {
u32 section_size = 0;
if (memcmp(data, "FINF", 4) == 0) {
BCFNT::FINF finf;
memcpy(&finf, data, sizeof(finf));
section_size = finf.section_size;
// Relocate the offsets in the FINF section
finf.cmap_offset += new_address - previous_address;
finf.cwdh_offset += new_address - previous_address;
finf.tglp_offset += new_address - previous_address;
memcpy(data, &finf, sizeof(finf));
} else if (memcmp(data, "CMAP", 4) == 0) {
BCFNT::CMAP cmap;
memcpy(&cmap, data, sizeof(cmap));
section_size = cmap.section_size;
// Relocate the offsets in the CMAP section
cmap.next_cmap_offset += new_address - previous_address;
memcpy(data, &cmap, sizeof(cmap));
} else if (memcmp(data, "CWDH", 4) == 0) {
BCFNT::CWDH cwdh;
memcpy(&cwdh, data, sizeof(cwdh));
section_size = cwdh.section_size;
// Relocate the offsets in the CWDH section
cwdh.next_cwdh_offset += new_address - previous_address;
memcpy(data, &cwdh, sizeof(cwdh));
} else if (memcmp(data, "TGLP", 4) == 0) {
BCFNT::TGLP tglp;
memcpy(&tglp, data, sizeof(tglp));
section_size = tglp.section_size;
// Relocate the offsets in the TGLP section
tglp.sheet_data_offset += new_address - previous_address;
memcpy(data, &tglp, sizeof(tglp));
}
data += section_size;
}
}
} // namespace BCFNT
} // namespace APT
} // namespace Service

View File

@ -0,0 +1,87 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/swap.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/service.h"
namespace Service {
namespace APT {
namespace BCFNT { ///< BCFNT Shared Font file structures
struct CFNT {
u8 magic[4];
u16_le endianness;
u16_le header_size;
u32_le version;
u32_le file_size;
u32_le num_blocks;
};
struct FINF {
u8 magic[4];
u32_le section_size;
u8 font_type;
u8 line_feed;
u16_le alter_char_index;
u8 default_width[3];
u8 encoding;
u32_le tglp_offset;
u32_le cwdh_offset;
u32_le cmap_offset;
u8 height;
u8 width;
u8 ascent;
u8 reserved;
};
struct TGLP {
u8 magic[4];
u32_le section_size;
u8 cell_width;
u8 cell_height;
u8 baseline_position;
u8 max_character_width;
u32_le sheet_size;
u16_le num_sheets;
u16_le sheet_image_format;
u16_le num_columns;
u16_le num_rows;
u16_le sheet_width;
u16_le sheet_height;
u32_le sheet_data_offset;
};
struct CMAP {
u8 magic[4];
u32_le section_size;
u16_le code_begin;
u16_le code_end;
u16_le mapping_method;
u16_le reserved;
u32_le next_cmap_offset;
};
struct CWDH {
u8 magic[4];
u32_le section_size;
u16_le start_index;
u16_le end_index;
u32_le next_cwdh_offset;
};
/**
* Relocates the internal addresses of the BCFNT Shared Font to the new base.
* @param shared_font SharedMemory object that contains the Shared Font
* @param previous_address Previous address at which the offsets in the structure were based.
* @param new_address New base for the offsets in the structure.
*/
void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address);
} // namespace BCFNT
} // namespace APT
} // namespace Service

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <cstring> #include <cstring>
#include "common/alignment.h"
#include "core/hle/hle.h" #include "core/hle/hle.h"
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/shared_memory.h"
@ -41,14 +42,16 @@ static Kernel::SharedPtr<Kernel::Mutex> mutex = nullptr;
void Initialize(Service::Interface* self) { void Initialize(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer(); u32* cmd_buff = Kernel::GetCommandBuffer();
shared_memory = Kernel::SharedMemory::Create(cmd_buff[1], u32 size = Common::AlignUp(cmd_buff[1], Memory::PAGE_SIZE);
Kernel::MemoryPermission::ReadWrite, using Kernel::MemoryPermission;
Kernel::MemoryPermission::ReadWrite, "CSNDSharedMem"); shared_memory = Kernel::SharedMemory::Create(nullptr, size,
MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
0, Kernel::MemoryRegion::BASE, "CSND:SharedMemory");
mutex = Kernel::Mutex::Create(false); mutex = Kernel::Mutex::Create(false);
cmd_buff[1] = 0; cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = 0x4000000; cmd_buff[2] = IPC::MoveHandleDesc(2);
cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom(); cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom();
cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom(); cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom();
} }

View File

@ -335,8 +335,9 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
g_interrupt_event->name = "GSP_GPU::interrupt_event"; g_interrupt_event->name = "GSP_GPU::interrupt_event";
using Kernel::MemoryPermission; using Kernel::MemoryPermission;
g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, g_shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000,
MemoryPermission::ReadWrite, "GSPSharedMem"); MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
0, Kernel::MemoryRegion::BASE, "GSP:SharedMemory");
Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom();

View File

@ -280,8 +280,9 @@ void Init() {
AddService(new HID_SPVR_Interface); AddService(new HID_SPVR_Interface);
using Kernel::MemoryPermission; using Kernel::MemoryPermission;
shared_mem = SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, shared_mem = SharedMemory::Create(nullptr, 0x1000,
MemoryPermission::Read, "HID:SharedMem"); MemoryPermission::ReadWrite, MemoryPermission::Read,
0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
next_pad_index = 0; next_pad_index = 0;
next_touch_index = 0; next_touch_index = 0;

View File

@ -94,8 +94,9 @@ void Init() {
AddService(new IR_User_Interface); AddService(new IR_User_Interface);
using Kernel::MemoryPermission; using Kernel::MemoryPermission;
shared_memory = SharedMemory::Create(0x1000, Kernel::MemoryPermission::ReadWrite, shared_memory = SharedMemory::Create(nullptr, 0x1000,
Kernel::MemoryPermission::ReadWrite, "IR:SharedMemory"); Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::ReadWrite,
0, Kernel::MemoryRegion::BASE, "IR:SharedMemory");
transfer_shared_memory = nullptr; transfer_shared_memory = nullptr;
// Create event handle(s) // Create event handle(s)

View File

@ -99,6 +99,7 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add
switch (operation & MEMOP_OPERATION_MASK) { switch (operation & MEMOP_OPERATION_MASK) {
case MEMOP_FREE: case MEMOP_FREE:
{ {
// TODO(Subv): What happens if an application tries to FREE a block of memory that has a SharedMemory pointing to it?
if (addr0 >= Memory::HEAP_VADDR && addr0 < Memory::HEAP_VADDR_END) { if (addr0 >= Memory::HEAP_VADDR && addr0 < Memory::HEAP_VADDR_END) {
ResultCode result = process.HeapFree(addr0, size); ResultCode result = process.HeapFree(addr0, size);
if (result.IsError()) return result; if (result.IsError()) return result;
@ -160,8 +161,6 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
handle, addr, permissions, other_permissions); handle, addr, permissions, other_permissions);
// TODO(Subv): The same process that created a SharedMemory object can not map it in its own address space
SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
if (shared_memory == nullptr) if (shared_memory == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
@ -176,7 +175,7 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
case MemoryPermission::WriteExecute: case MemoryPermission::WriteExecute:
case MemoryPermission::ReadWriteExecute: case MemoryPermission::ReadWriteExecute:
case MemoryPermission::DontCare: case MemoryPermission::DontCare:
return shared_memory->Map(addr, permissions_type, return shared_memory->Map(Kernel::g_current_process.get(), addr, permissions_type,
static_cast<MemoryPermission>(other_permissions)); static_cast<MemoryPermission>(other_permissions));
default: default:
LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
@ -196,7 +195,7 @@ static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) {
if (shared_memory == nullptr) if (shared_memory == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
return shared_memory->Unmap(addr); return shared_memory->Unmap(Kernel::g_current_process.get(), addr);
} }
/// Connect to an OS service given the port name, returns the handle to the port to out /// Connect to an OS service given the port name, returns the handle to the port to out
@ -790,18 +789,44 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
if (size % Memory::PAGE_SIZE != 0) if (size % Memory::PAGE_SIZE != 0)
return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
// TODO(Subv): Return E0A01BF5 if the address is not in the application's heap SharedPtr<SharedMemory> shared_memory = nullptr;
// TODO(Subv): Implement this function properly
using Kernel::MemoryPermission; using Kernel::MemoryPermission;
SharedPtr<SharedMemory> shared_memory = SharedMemory::Create(size, auto VerifyPermissions = [](MemoryPermission permission) {
(MemoryPermission)my_permission, (MemoryPermission)other_permission); // SharedMemory blocks can not be created with Execute permissions
// Map the SharedMemory to the specified address switch (permission) {
shared_memory->base_address = addr; case MemoryPermission::None:
case MemoryPermission::Read:
case MemoryPermission::Write:
case MemoryPermission::ReadWrite:
case MemoryPermission::DontCare:
return true;
default:
return false;
}
};
if (!VerifyPermissions(static_cast<MemoryPermission>(my_permission)) ||
!VerifyPermissions(static_cast<MemoryPermission>(other_permission)))
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
if (addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) {
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
// When trying to create a memory block with address = 0,
// if the process has the Shared Device Memory flag in the exheader,
// then we have to allocate from the same region as the caller process instead of the BASE region.
Kernel::MemoryRegion region = Kernel::MemoryRegion::BASE;
if (addr == 0 && Kernel::g_current_process->flags.shared_device_mem)
region = Kernel::g_current_process->flags.memory_region;
shared_memory = SharedMemory::Create(Kernel::g_current_process, size,
static_cast<MemoryPermission>(my_permission), static_cast<MemoryPermission>(other_permission), addr, region);
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory))); CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory)));
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr); LOG_WARNING(Kernel_SVC, "called addr=0x%08X", addr);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }