From 42a50da76b816fff95ae0e438d3e966f6f991b8a Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 17 Apr 2016 13:36:15 -0500 Subject: [PATCH 1/9] Kernel/SVC: Fixed the register order for svcCreateMemoryBlock. R0 is used as the last parameter instead of R4. --- src/core/hle/function_wrappers.h | 3 ++- src/core/hle/kernel/process.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 4d718b681b..bf7f875b6a 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -170,7 +170,8 @@ template void Wrap() { template void Wrap() { u32 param_1 = 0; - u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw; + // The last parameter is passed in R0 instead of R4 + u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3), PARAM(0)).raw; Core::g_app_core->SetReg(1, param_1); FuncReturn(retval); } diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 0546f6e16b..69302cc825 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -209,7 +209,7 @@ ResultVal Process::LinearAllocate(VAddr target, u32 size, VMAPermission p 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, // but expansions are only allowed at the end. if (target == heap_end) { From 1bd0cf542ff8db1cda572c4d92462643296af121 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 17 Apr 2016 21:07:52 -0500 Subject: [PATCH 2/9] Kernel/SharedMemory: Properly implemented shared memory support. Applications can request the kernel to allocate a piece of the linear heap for them when creating a shared memory object. Shared memory areas are now properly mapped into the target processes when calling svcMapMemoryBlock. Removed the APT Shared Font hack as it is no longer needed. --- src/core/hle/applets/mii_selector.cpp | 4 +- src/core/hle/applets/swkbd.cpp | 4 +- src/core/hle/kernel/shared_memory.cpp | 116 +++++++++++++------------- src/core/hle/kernel/shared_memory.h | 28 +++++-- src/core/hle/service/apt/apt.cpp | 38 +++------ src/core/hle/service/csnd_snd.cpp | 13 +-- src/core/hle/service/gsp_gpu.cpp | 5 +- src/core/hle/service/hid/hid.cpp | 5 +- src/core/hle/service/ir/ir.cpp | 5 +- src/core/hle/svc.cpp | 47 ++++++++--- 10 files changed, 147 insertions(+), 118 deletions(-) diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index b4456ca90b..7f174f3e6a 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp @@ -36,8 +36,8 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p memcpy(&capture_info, parameter.data, sizeof(capture_info)); using Kernel::MemoryPermission; - framebuffer_memory = Kernel::SharedMemory::Create(capture_info.size, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, "MiiSelector Memory"); + framebuffer_memory = Kernel::SharedMemory::Create(nullptr, capture_info.size, MemoryPermission::ReadWrite, + MemoryPermission::ReadWrite, 0, Kernel::MemoryRegion::BASE, "MiiSelector Memory"); // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index 87238aa1c9..e0c134182f 100644 --- a/src/core/hle/applets/swkbd.cpp +++ b/src/core/hle/applets/swkbd.cpp @@ -40,8 +40,8 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con memcpy(&capture_info, parameter.data, sizeof(capture_info)); using Kernel::MemoryPermission; - framebuffer_memory = Kernel::SharedMemory::Create(capture_info.size, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); + framebuffer_memory = Kernel::SharedMemory::Create(nullptr, capture_info.size, MemoryPermission::ReadWrite, + MemoryPermission::ReadWrite, 0, Kernel::MemoryRegion::BASE, "SoftwareKeyboard Memory"); // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index d90f0f00f0..6f731f3176 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -7,6 +7,7 @@ #include "common/logging/log.h" #include "core/memory.h" +#include "core/hle/kernel/memory.h" #include "core/hle/kernel/shared_memory.h" namespace Kernel { @@ -14,93 +15,94 @@ namespace Kernel { SharedMemory::SharedMemory() {} SharedMemory::~SharedMemory() {} -SharedPtr SharedMemory::Create(u32 size, MemoryPermission permissions, - MemoryPermission other_permissions, std::string name) { +SharedPtr SharedMemory::Create(SharedPtr owner_process, u32 size, MemoryPermission permissions, + MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) { SharedPtr shared_memory(new SharedMemory); + shared_memory->owner_process = owner_process; shared_memory->name = std::move(name); - shared_memory->base_address = 0x0; - shared_memory->fixed_address = 0x0; shared_memory->size = size; shared_memory->permissions = 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; + + // 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 { + 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>(vma.backing_block->data() + vma.offset, + vma.backing_block->data() + vma.offset + size); + // 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); + } + + shared_memory->base_address = address; return shared_memory; } -ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, +ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, MemoryPermission other_permissions) { - if (base_address != 0) { - LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: already mapped at 0x%08X!", - GetObjectId(), address, name.c_str(), base_address); - // TODO: Verify error code with hardware - return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, - ErrorSummary::InvalidArgument, ErrorLevel::Permanent); - } - // TODO(Subv): Return E0E01BEE when permissions and other_permissions don't // match what was specified when the memory block was created. - // TODO(Subv): Return E0E01BEE when address should be 0. - // Note: Find out when that's the case. + // 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); + }*/ - 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); - } + // 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. - // 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!", + 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()); - // TODO: Verify error code with hardware - return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, - ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); } - // TODO: Test permissions + VAddr target_address = address; - // HACK: Since there's no way to write to the memory block without mapping it onto the game - // process yet, at least initialize memory the first time it's mapped. - if (address != this->base_address) { - std::memset(Memory::GetPointer(address), 0, size); + 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); } - this->base_address = address; + // Map the memory block into the target process + target_process->vm_manager.MapMemoryBlock(target_address, backing_block, backing_block_offset, size, MemoryState::Shared); return RESULT_SUCCESS; } -ResultCode SharedMemory::Unmap(VAddr address) { - if (base_address == 0) { - // TODO(Subv): Verify what actually happens when you want to unmap a memory block that - // 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; +ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { + // TODO(Subv): Verify what happens if the application tries to unmap an address that is not mapped to a SharedMemory. + return target_process->vm_manager.UnmapRange(address, size); } u8* SharedMemory::GetPointer(u32 offset) { - if (base_address != 0) - return Memory::GetPointer(base_address + offset); - - LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId()); - return nullptr; + return backing_block->data() + backing_block_offset + offset; } } // namespace diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index b51049ad03..b442cb7646 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -9,6 +9,7 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/result.h" namespace Kernel { @@ -30,13 +31,16 @@ class SharedMemory final : public Object { public: /** * 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 Create(u32 size, MemoryPermission permissions, - MemoryPermission other_permissions, std::string name = "Unknown"); + static SharedPtr Create(SharedPtr owner_process, u32 size, MemoryPermission permissions, + MemoryPermission other_permissions, VAddr address = 0, MemoryRegion region = MemoryRegion::BASE, std::string name = "Unknown"); std::string GetTypeName() const override { return "SharedMemory"; } std::string GetName() const override { return name; } @@ -45,19 +49,21 @@ public: HandleType GetHandleType() const override { return HANDLE_TYPE; } /** - * Maps a shared memory block to an address in system memory + * 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 permissions Memory block map 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 + * @param target_process Process from which to umap the memory block. * @param address Address in system memory where the shared memory block is mapped * @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 @@ -66,10 +72,16 @@ public: */ u8* GetPointer(u32 offset = 0); - /// Address of shared memory block in the process. + /// Process that created this shared memory block. + SharedPtr owner_process; + /// Address of shared memory block in the owner process if specified. VAddr base_address; - /// Fixed address to allow mapping to. Used for blocks created from the linear heap. - VAddr fixed_address; + /// Physical address of the shared memory block in the linear heap if no address was specified during creation. + PAddr linear_heap_phys_address; + /// Backing memory for this shared memory block. + std::shared_ptr> backing_block; + /// Offset into the backing block for this shared memory. + u32 backing_block_offset; /// Size of the memory block. Page-aligned. u32 size; /// Permission restrictions applied to the process which created the block. diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 6d72e81887..e7b8f53059 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -37,8 +37,6 @@ static Kernel::SharedPtr lock; static Kernel::SharedPtr notification_event; ///< APT notification event static Kernel::SharedPtr parameter_event; ///< APT parameter event -static std::shared_ptr> shared_font; - static u32 cpu_percent; ///< CPU time available to the running application /// Parameter data to be returned in the next call to Glance/ReceiveParameter @@ -74,23 +72,14 @@ void Initialize(Service::Interface* self) { void GetSharedFont(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - if (shared_font != nullptr) { - // TODO(yuriks): This is a hack to keep this working right now even with our completely - // broken shared memory system. - shared_font_mem->fixed_address = SHARED_FONT_VADDR; - Kernel::g_current_process->vm_manager.MapMemoryBlock(shared_font_mem->fixed_address, - shared_font, 0, shared_font_mem->size, Kernel::MemoryState::Shared); - - cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = SHARED_FONT_VADDR; - cmd_buff[3] = IPC::MoveHandleDesc(); - 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); - } + cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + // Since the SharedMemory interface doesn't provide the address at which the memory was allocated, + // the 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] = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address); + cmd_buff[3] = IPC::MoveHandleDesc(); + cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom(); } void NotifyToWait(Service::Interface* self) { @@ -433,14 +422,12 @@ void Init() { FileUtil::IOFile file(filepath, "rb"); if (file.IsOpen()) { - // Read shared font data - shared_font = std::make_shared>((size_t)file.GetSize()); - file.ReadBytes(shared_font->data(), shared_font->size()); - // Create shared font memory object using Kernel::MemoryPermission; - shared_font_mem = Kernel::SharedMemory::Create(3 * 1024 * 1024, // 3MB - MemoryPermission::ReadWrite, MemoryPermission::Read, "APT_U:shared_font_mem"); + shared_font_mem = Kernel::SharedMemory::Create(nullptr, 0x332000, // 3272 KB + MemoryPermission::ReadWrite, MemoryPermission::Read, 0, Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); + // Read shared font data + file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize()); } else { LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str()); shared_font_mem = nullptr; @@ -459,7 +446,6 @@ void Init() { } void Shutdown() { - shared_font = nullptr; shared_font_mem = nullptr; lock = nullptr; notification_event = nullptr; diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index 6318bf2a7c..d2bb8941cd 100644 --- a/src/core/hle/service/csnd_snd.cpp +++ b/src/core/hle/service/csnd_snd.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include "common/alignment.h" #include "core/hle/hle.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/shared_memory.h" @@ -41,14 +42,16 @@ static Kernel::SharedPtr mutex = nullptr; void Initialize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - shared_memory = Kernel::SharedMemory::Create(cmd_buff[1], - Kernel::MemoryPermission::ReadWrite, - Kernel::MemoryPermission::ReadWrite, "CSNDSharedMem"); + u32 size = Common::AlignUp(cmd_buff[1], Memory::PAGE_SIZE); + using Kernel::MemoryPermission; + shared_memory = Kernel::SharedMemory::Create(nullptr, size, + MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + 0, Kernel::MemoryRegion::BASE, "CSND:SharedMemory"); mutex = Kernel::Mutex::Create(false); - cmd_buff[1] = 0; - cmd_buff[2] = 0x4000000; + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = IPC::MoveHandleDesc(2); cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom(); cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom(); } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index b4c146e081..8ded9b09bc 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -335,8 +335,9 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { g_interrupt_event->name = "GSP_GPU::interrupt_event"; using Kernel::MemoryPermission; - g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, "GSPSharedMem"); + g_shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000, + MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + 0, Kernel::MemoryRegion::BASE, "GSP:SharedMemory"); Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 1053d0f40c..d216cecb44 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -280,8 +280,9 @@ void Init() { AddService(new HID_SPVR_Interface); using Kernel::MemoryPermission; - shared_mem = SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, - MemoryPermission::Read, "HID:SharedMem"); + shared_mem = SharedMemory::Create(nullptr, 0x1000, + MemoryPermission::ReadWrite, MemoryPermission::Read, + 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); next_pad_index = 0; next_touch_index = 0; diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp index 505c441c6f..079a87e486 100644 --- a/src/core/hle/service/ir/ir.cpp +++ b/src/core/hle/service/ir/ir.cpp @@ -94,8 +94,9 @@ void Init() { AddService(new IR_User_Interface); using Kernel::MemoryPermission; - shared_memory = SharedMemory::Create(0x1000, Kernel::MemoryPermission::ReadWrite, - Kernel::MemoryPermission::ReadWrite, "IR:SharedMemory"); + shared_memory = SharedMemory::Create(nullptr, 0x1000, + Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::ReadWrite, + 0, Kernel::MemoryRegion::BASE, "IR:SharedMemory"); transfer_shared_memory = nullptr; // Create event handle(s) diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 60c8747f3d..701dffef3f 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -160,8 +160,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", 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 shared_memory = Kernel::g_handle_table.Get(handle); if (shared_memory == nullptr) return ERR_INVALID_HANDLE; @@ -176,7 +174,7 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o case MemoryPermission::WriteExecute: case MemoryPermission::ReadWriteExecute: case MemoryPermission::DontCare: - return shared_memory->Map(addr, permissions_type, + return shared_memory->Map(Kernel::g_current_process.get(), addr, permissions_type, static_cast(other_permissions)); default: LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); @@ -196,7 +194,7 @@ static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) { if (shared_memory == nullptr) 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 @@ -790,18 +788,43 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 if (size % Memory::PAGE_SIZE != 0) return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); - // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap - - // TODO(Subv): Implement this function properly + SharedPtr shared_memory = nullptr; using Kernel::MemoryPermission; - SharedPtr shared_memory = SharedMemory::Create(size, - (MemoryPermission)my_permission, (MemoryPermission)other_permission); - // Map the SharedMemory to the specified address - shared_memory->base_address = addr; + auto VerifyPermissions = [](MemoryPermission permission) { + // SharedMemory blocks can not be created with Execute permissions + switch (permission) { + case MemoryPermission::None: + case MemoryPermission::Read: + case MemoryPermission::Write: + case MemoryPermission::ReadWrite: + return true; + default: + return false; + } + }; + + if (!VerifyPermissions(static_cast(my_permission)) || + !VerifyPermissions(static_cast(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(my_permission), static_cast(other_permission), addr, region); 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; } From f72bd5721240d9f33d803546c4bbc21a6f4ee466 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 17 Apr 2016 21:10:17 -0500 Subject: [PATCH 3/9] Kernel/Memory: Remove the Shared Memory region from the legacy memory map. --- src/core/hle/kernel/memory.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 8626434480..6f1f0856c9 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -107,7 +107,6 @@ struct MemoryArea { // We don't declare the IO regions in here since its handled by other means. static MemoryArea memory_areas[] = { - {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory {VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM) {TLS_AREA_VADDR, TLS_AREA_SIZE, "TLS Area"}, // TLS memory }; From 9005cda66469070d9f1cd844720233e738fef74d Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 17 Apr 2016 21:12:37 -0500 Subject: [PATCH 4/9] APT: Implement relocating the shared font to its true address. --- src/core/hle/service/apt/apt.cpp | 83 ++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index e7b8f53059..ad6ba1facb 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -22,16 +22,73 @@ namespace Service { 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; +/// BCFNT Shared Font file structures +namespace BCFNT { +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; +}; +} /// Handle to shared memory region designated to for shared system font static Kernel::SharedPtr shared_font_mem; +static bool shared_font_relocated = false; static Kernel::SharedPtr lock; static Kernel::SharedPtr notification_event; ///< APT notification event @@ -72,12 +129,19 @@ void Initialize(Service::Interface* self) { void GetSharedFont(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + // The shared font has to be relocated to the new address before being passed to the application. + VAddr target_address = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address); + // The shared font dumped by 3dsutils (https://github.com/citra-emu/3dsutils) uses this address as base, + // so we relocate it from there to our real address. + static const VAddr SHARED_FONT_VADDR = 0x18000000; + if (!shared_font_relocated) + RelocateSharedFont(SHARED_FONT_VADDR, target_address); cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error // Since the SharedMemory interface doesn't provide the address at which the memory was allocated, - // the APT service calculates this address by scanning the entire address space (using svcQueryMemory) + // 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] = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address); + cmd_buff[2] = target_address; cmd_buff[3] = IPC::MoveHandleDesc(); cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom(); } @@ -447,6 +511,7 @@ void Init() { void Shutdown() { shared_font_mem = nullptr; + shared_font_relocated = false; lock = nullptr; notification_event = nullptr; parameter_event = nullptr; From 0fb6d2a24729401e17cd69264cdb630516d9c151 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 17 Apr 2016 21:58:51 -0500 Subject: [PATCH 5/9] Kernel: Implemented shared memory permissions. --- src/core/hle/kernel/shared_memory.cpp | 50 ++++++++++++++++++++++----- src/core/hle/kernel/shared_memory.h | 6 ++++ src/core/hle/result.h | 1 + src/core/hle/svc.cpp | 2 ++ 4 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 6f731f3176..c1e0e556be 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -46,6 +46,7 @@ SharedPtr SharedMemory::Create(SharedPtr owner_process, u 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; @@ -56,6 +57,8 @@ SharedPtr SharedMemory::Create(SharedPtr owner_process, u 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; @@ -65,8 +68,28 @@ SharedPtr SharedMemory::Create(SharedPtr owner_process, u ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, MemoryPermission other_permissions) { - // TODO(Subv): Return E0E01BEE when permissions and other_permissions don't - // match what was specified when the memory block was created. + MemoryPermission own_other_permissions = target_process == owner_process ? this->permissions : this->other_permissions; + + // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare + if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { + return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); + } + + // Error out if the requested permissions don't match what the creator process allows. + if (static_cast(permissions) & ~static_cast(own_other_permissions)) { + return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); + } + + // Heap-backed memory blocks can not be mapped with other_permissions = DontCare + if (base_address != 0 && other_permissions == MemoryPermission::DontCare) { + return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); + } + + // Error out if the provided permissions are not compatible with what the creator process needs. + if (other_permissions != MemoryPermission::DontCare && + static_cast(this->permissions) & ~static_cast(other_permissions)) { + return ResultCode(ErrorDescription::WrongPermission, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent); + } // TODO(Subv): Check for the Shared Device Mem flag in the creator process. /*if (was_created_with_shared_device_mem && address != 0) { @@ -76,11 +99,13 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi // 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 < 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); + 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; @@ -91,9 +116,11 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi } // Map the memory block into the target process - target_process->vm_manager.MapMemoryBlock(target_address, backing_block, backing_block_offset, size, MemoryState::Shared); + auto result = target_process->vm_manager.MapMemoryBlock(target_address, backing_block, backing_block_offset, size, MemoryState::Shared); + if (result.Failed()) + return result.Code(); - return RESULT_SUCCESS; + return target_process->vm_manager.ReprotectRange(target_address, size, ConvertPermissions(permissions)); } ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { @@ -101,6 +128,11 @@ ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { return target_process->vm_manager.UnmapRange(address, size); } +VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { + u32 masked_permissions = static_cast(permission) & static_cast(MemoryPermission::ReadWriteExecute); + return static_cast(masked_permissions); +}; + u8* SharedMemory::GetPointer(u32 offset) { return backing_block->data() + backing_block_offset + offset; } diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index b442cb7646..af145bef5c 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -48,6 +48,12 @@ public: static const HandleType HANDLE_TYPE = HandleType::SharedMemory; HandleType GetHandleType() const override { return HANDLE_TYPE; } + /** + * 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. diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 3fc1ab4eea..bfb3327ce0 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -17,6 +17,7 @@ /// Detailed description of the error. This listing is likely incomplete. enum class ErrorDescription : u32 { Success = 0, + WrongPermission = 46, OS_InvalidBufferDescriptor = 48, WrongAddress = 53, FS_NotFound = 120, diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 701dffef3f..3a53126c17 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -99,6 +99,7 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add switch (operation & MEMOP_OPERATION_MASK) { 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) { ResultCode result = process.HeapFree(addr0, size); if (result.IsError()) return result; @@ -798,6 +799,7 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 case MemoryPermission::Read: case MemoryPermission::Write: case MemoryPermission::ReadWrite: + case MemoryPermission::DontCare: return true; default: return false; From 5679d066b5bc0433378c9ac8f66066f2439eb6c7 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 19 Apr 2016 11:59:44 -0500 Subject: [PATCH 6/9] Kernel/SharedMemory: Log an error when Map fails. --- src/core/hle/kernel/shared_memory.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index c1e0e556be..2b6007caa3 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -77,17 +77,23 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi // Error out if the requested permissions don't match what the creator process allows. if (static_cast(permissions) & ~static_cast(own_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::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); } // Heap-backed memory blocks can not be mapped with other_permissions = DontCare if (base_address != 0 && other_permissions == MemoryPermission::DontCare) { + LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", + GetObjectId(), address, name.c_str()); return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); } // Error out if the provided permissions are not compatible with what the creator process needs. if (other_permissions != MemoryPermission::DontCare && static_cast(this->permissions) & ~static_cast(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); } @@ -117,8 +123,11 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi // 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()) + 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)); } From abf8dcd700ce6e8af35aff9b8fa568dfe6acfecb Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 19 Apr 2016 12:13:00 -0500 Subject: [PATCH 7/9] APT: Move the shared font loading and relocation functions to their own subdirectory services/apt/bcfnt. --- src/core/CMakeLists.txt | 2 + src/core/hle/service/apt/apt.cpp | 73 ++------------------ src/core/hle/service/apt/bcfnt/bcfnt.cpp | 71 +++++++++++++++++++ src/core/hle/service/apt/bcfnt/bcfnt.h | 87 ++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 66 deletions(-) create mode 100644 src/core/hle/service/apt/bcfnt/bcfnt.cpp create mode 100644 src/core/hle/service/apt/bcfnt/bcfnt.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a8d8916899..f6a7566bf6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -52,6 +52,7 @@ set(SRCS hle/service/apt/apt_a.cpp hle/service/apt/apt_s.cpp hle/service/apt/apt_u.cpp + hle/service/apt/bcfnt/bcfnt.cpp hle/service/boss/boss.cpp hle/service/boss/boss_p.cpp hle/service/boss/boss_u.cpp @@ -185,6 +186,7 @@ set(HEADERS hle/service/apt/apt_a.h hle/service/apt/apt_s.h hle/service/apt/apt_u.h + hle/service/apt/bcfnt/bcfnt.h hle/service/boss/boss.h hle/service/boss/boss_p.h hle/service/boss/boss_u.h diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index ad6ba1facb..73fce60795 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -12,6 +12,7 @@ #include "core/hle/service/apt/apt_a.h" #include "core/hle/service/apt/apt_s.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/kernel/event.h" @@ -22,70 +23,6 @@ namespace Service { namespace APT { -/// BCFNT Shared Font file structures -namespace BCFNT { -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; -}; -} - /// Handle to shared memory region designated to for shared system font static Kernel::SharedPtr shared_font_mem; static bool shared_font_relocated = false; @@ -133,9 +70,13 @@ void GetSharedFont(Service::Interface* self) { VAddr target_address = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address); // The shared font dumped by 3dsutils (https://github.com/citra-emu/3dsutils) uses this address as base, // so we relocate it from there to our real address. + // TODO(Subv): This address is wrong if the shared font is dumped from a n3DS, + // 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) - RelocateSharedFont(SHARED_FONT_VADDR, target_address); + 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[1] = RESULT_SUCCESS.raw; // No error // Since the SharedMemory interface doesn't provide the address at which the memory was allocated, diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.cpp b/src/core/hle/service/apt/bcfnt/bcfnt.cpp new file mode 100644 index 0000000000..b0d39d4a5b --- /dev/null +++ b/src/core/hle/service/apt/bcfnt/bcfnt.cpp @@ -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 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 \ No newline at end of file diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.h b/src/core/hle/service/apt/bcfnt/bcfnt.h new file mode 100644 index 0000000000..388c6bea0e --- /dev/null +++ b/src/core/hle/service/apt/bcfnt/bcfnt.h @@ -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 shared_font, VAddr previous_address, VAddr new_address); + +} // namespace BCFNT +} // namespace APT +} // namespace Service From d261984f5460ca700c372299df64d47f32e199b9 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 19 Apr 2016 15:56:11 -0500 Subject: [PATCH 8/9] Kernel: Account for automatically-allocated shared memories in the amount of used linear heap memory. --- src/core/hle/kernel/shared_memory.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 2b6007caa3..74947f023a 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -41,6 +41,11 @@ SharedPtr SharedMemory::Create(SharedPtr owner_process, u 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()); From ac2de12ed8a7cc2759e25325f388db92b3f356a6 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 8 May 2016 17:10:53 -0500 Subject: [PATCH 9/9] HLE/Applets: Give each applet its own block of heap memory, and use that when creating the framebuffer shared memory block. --- src/core/hle/applets/applet.h | 1 + src/core/hle/applets/mii_selector.cpp | 9 +++++++-- src/core/hle/applets/swkbd.cpp | 8 ++++++-- src/core/hle/kernel/shared_memory.cpp | 17 +++++++++++++++++ src/core/hle/kernel/shared_memory.h | 14 +++++++++++++- 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h index af442f81d5..754c6f7db5 100644 --- a/src/core/hle/applets/applet.h +++ b/src/core/hle/applets/applet.h @@ -65,6 +65,7 @@ protected: virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0; Service::APT::AppletId id; ///< Id of this Applet + std::shared_ptr> heap_memory; ///< Heap memory for this Applet }; /// Returns whether a library applet is currently running diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index 7f174f3e6a..bf39eca221 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp @@ -35,9 +35,14 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p ASSERT(sizeof(capture_info) == parameter.buffer_size); memcpy(&capture_info, parameter.data, sizeof(capture_info)); + using Kernel::MemoryPermission; - framebuffer_memory = Kernel::SharedMemory::Create(nullptr, capture_info.size, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, 0, Kernel::MemoryRegion::BASE, "MiiSelector Memory"); + // Allocate a heap block of the required size for this applet. + heap_memory = std::make_shared>(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 Service::APT::MessageParameter result; diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index e0c134182f..90c6adc656 100644 --- a/src/core/hle/applets/swkbd.cpp +++ b/src/core/hle/applets/swkbd.cpp @@ -40,8 +40,12 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con memcpy(&capture_info, parameter.data, sizeof(capture_info)); using Kernel::MemoryPermission; - framebuffer_memory = Kernel::SharedMemory::Create(nullptr, capture_info.size, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, 0, Kernel::MemoryRegion::BASE, "SoftwareKeyboard Memory"); + // Allocate a heap block of the required size for this applet. + heap_memory = std::make_shared>(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 Service::APT::MessageParameter result; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 74947f023a..6a22c89860 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -58,6 +58,7 @@ SharedPtr SharedMemory::Create(SharedPtr owner_process, u // Copy it over to our own storage shared_memory->backing_block = std::make_shared>(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 @@ -70,6 +71,22 @@ SharedPtr SharedMemory::Create(SharedPtr owner_process, u return shared_memory; } +SharedPtr SharedMemory::CreateForApplet(std::shared_ptr> heap_block, u32 offset, u32 size, + MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { + SharedPtr 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) { diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index af145bef5c..0c404a9f84 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -30,7 +30,7 @@ enum class MemoryPermission : u32 { class SharedMemory final : public Object { 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. @@ -42,6 +42,18 @@ public: static SharedPtr Create(SharedPtr 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 permissions Permission restrictions applied to the process which created the block. + * @param other_permissions Permission restrictions applied to other processes mapping the block. + * @param name Optional object name, used for debugging purposes. + */ + static SharedPtr CreateForApplet(std::shared_ptr> heap_block, u32 offset, u32 size, + MemoryPermission permissions, MemoryPermission other_permissions, std::string name = "Unknown Applet"); + std::string GetTypeName() const override { return "SharedMemory"; } std::string GetName() const override { return name; }