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/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 b4456ca90b..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(capture_info.size, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, "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 87238aa1c9..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(capture_info.size, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, "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/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/memory.cpp b/src/core/hle/kernel/memory.cpp index 61a741e28a..4be20db22d 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) }; 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) { diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index d90f0f00f0..6a22c89860 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,157 @@ 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; + + // 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>(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; } -ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, +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) { - 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); + 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); } - // TODO(Subv): Return E0E01BEE when permissions and other_permissions don't - // match what was specified when the memory block was created. + // 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); + } - // TODO(Subv): Return E0E01BEE when address should be 0. - // Note: Find out when that's the case. + // 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); + } - 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); + // 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); + } + + // 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); } - - // 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()); - // TODO: Verify error code with hardware - return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, - ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + 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); } - // TODO: Test permissions - - // 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); + // 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(); } - this->base_address = address; - - return RESULT_SUCCESS; + return target_process->vm_manager.ReprotectRange(target_address, size, ConvertPermissions(permissions)); } -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); } +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) { - 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..0c404a9f84 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 { @@ -29,14 +30,29 @@ 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. + * @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(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 Create(u32 size, MemoryPermission permissions, - MemoryPermission other_permissions, std::string name = "Unknown"); + 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; } @@ -45,19 +61,27 @@ public: 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 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 +90,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/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/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 6d72e81887..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,23 +23,14 @@ 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; - /// 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 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 +66,25 @@ 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); + // 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. + // 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) { + 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, + // 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[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom(); } void NotifyToWait(Service::Interface* self) { @@ -433,14 +427,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,8 +451,8 @@ void Init() { } void Shutdown() { - shared_font = nullptr; shared_font_mem = nullptr; + shared_font_relocated = false; lock = nullptr; notification_event = nullptr; parameter_event = nullptr; 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 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..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; @@ -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", 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 +175,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 +195,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 +789,44 @@ 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: + case MemoryPermission::DontCare: + 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; }