diff options
Diffstat (limited to 'src/core/hle')
| -rw-r--r-- | src/core/hle/applets/applet.h | 1 | ||||
| -rw-r--r-- | src/core/hle/applets/mii_selector.cpp | 9 | ||||
| -rw-r--r-- | src/core/hle/applets/swkbd.cpp | 8 | ||||
| -rw-r--r-- | src/core/hle/function_wrappers.h | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/memory.cpp | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/process.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/shared_memory.cpp | 177 | ||||
| -rw-r--r-- | src/core/hle/kernel/shared_memory.h | 48 | ||||
| -rw-r--r-- | src/core/hle/result.h | 1 | ||||
| -rw-r--r-- | src/core/hle/service/apt/apt.cpp | 58 | ||||
| -rw-r--r-- | src/core/hle/service/apt/bcfnt/bcfnt.cpp | 71 | ||||
| -rw-r--r-- | src/core/hle/service/apt/bcfnt/bcfnt.h | 87 | ||||
| -rw-r--r-- | src/core/hle/service/csnd_snd.cpp | 13 | ||||
| -rw-r--r-- | src/core/hle/service/gsp_gpu.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/service/hid/hid.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/service/ir/ir.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 49 | 
17 files changed, 415 insertions, 128 deletions
| diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h index af442f81d..754c6f7db 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<std::vector<u8>> 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 b4456ca90..bf39eca22 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<std::vector<u8>>(capture_info.size); +    // Create a SharedMemory that directly points to this heap block. +    framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(), +                                                               MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, +                                                               "MiiSelector Memory");      // Send the response message with the newly created SharedMemory      Service::APT::MessageParameter result; diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index 87238aa1c..90c6adc65 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<std::vector<u8>>(capture_info.size); +    // Create a SharedMemory that directly points to this heap block. +    framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(), +                                                               MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, +                                                               "SoftwareKeyboard Memory");      // Send the response message with the newly created SharedMemory      Service::APT::MessageParameter result; diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 4d718b681..bf7f875b6 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -170,7 +170,8 @@ template<ResultCode func(s64*, u32, s32)> void Wrap() {  template<ResultCode func(u32*, u32, u32, u32, u32)> 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 61a741e28..4be20db22 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 0546f6e16..69302cc82 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -209,7 +209,7 @@ ResultVal<VAddr> 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 d90f0f00f..6a22c8986 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> SharedMemory::Create(u32 size, MemoryPermission permissions, -        MemoryPermission other_permissions, std::string name) { +SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions, +        MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) {      SharedPtr<SharedMemory> 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<std::vector<u8>>(vma.backing_block->data() + vma.offset, +                                                                         vma.backing_block->data() + vma.offset + size); +        shared_memory->backing_block_offset = 0; +        // Unmap the existing pages +        vm_manager.UnmapRange(address, size); +        // Map our own block into the address space +        vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size, MemoryState::Shared); +        // Reprotect the block with the new permissions +        vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions)); +    } + +    shared_memory->base_address = address;      return shared_memory;  } -ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, -        MemoryPermission other_permissions) { +SharedPtr<SharedMemory> SharedMemory::CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size, +                                                      MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { +    SharedPtr<SharedMemory> shared_memory(new SharedMemory); -    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); -    } +    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; -    // TODO(Subv): Return E0E01BEE when permissions and other_permissions don't -    // match what was specified when the memory block was created. +    return shared_memory; +} -    // TODO(Subv): Return E0E01BEE when address should be 0. -    // Note: Find out when that's the case. +ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, +        MemoryPermission other_permissions) { -    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); -        } +    MemoryPermission own_other_permissions = target_process == owner_process ? this->permissions : this->other_permissions; -        // 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; +    // 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);      } -    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); +    // Error out if the requested permissions don't match what the creator process allows. +    if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { +        LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", +                  GetObjectId(), address, name.c_str()); +        return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);      } -    // TODO: Test permissions +    // 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); +    } -    // 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); +    // Error out if the provided permissions are not compatible with what the creator process needs. +    if (other_permissions != MemoryPermission::DontCare && +        static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) { +        LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", +                  GetObjectId(), address, name.c_str()); +        return ResultCode(ErrorDescription::WrongPermission, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent);      } -    this->base_address = address; +    // 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); +    }*/ -    return RESULT_SUCCESS; -} +    // 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. -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 (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); +        }      } -    if (base_address != address) -        return ResultCode(ErrorDescription::WrongAddress, ErrorModule::OS, ErrorSummary::InvalidState, ErrorLevel::Usage); +    VAddr target_address = address; -    base_address = 0; +    if (base_address == 0 && target_address == 0) { +        // Calculate the address at which to map the memory block. +        target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address); +    } + +    // Map the memory block into the target process +    auto result = target_process->vm_manager.MapMemoryBlock(target_address, backing_block, backing_block_offset, size, MemoryState::Shared); +    if (result.Failed()) { +        LOG_ERROR(Kernel, "cannot map id=%u, target_address=0x%08X name=%s, error mapping to virtual memory", +                  GetObjectId(), target_address, name.c_str()); +        return result.Code(); +    } -    return RESULT_SUCCESS; +    return target_process->vm_manager.ReprotectRange(target_address, size, ConvertPermissions(permissions));  } -u8* SharedMemory::GetPointer(u32 offset) { -    if (base_address != 0) -        return Memory::GetPointer(base_address + offset); +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<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute); +    return static_cast<VMAPermission>(masked_permissions); +}; -    LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId()); -    return nullptr; +u8* SharedMemory::GetPointer(u32 offset) { +    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 b51049ad0..0c404a9f8 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<SharedMemory> Create(u32 size, MemoryPermission permissions, -            MemoryPermission other_permissions, std::string name = "Unknown"); +    static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions, +            MemoryPermission other_permissions, VAddr address = 0, MemoryRegion region = MemoryRegion::BASE, std::string name = "Unknown"); + +    /** +     * Creates a shared memory object from a block of memory managed by an HLE applet. +     * @param heap_block Heap block of the HLE applet. +     * @param offset The offset into the heap block that the SharedMemory will map. +     * @param size Size of the memory block. Must be page-aligned. +     * @param 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<SharedMemory> CreateForApplet(std::shared_ptr<std::vector<u8>> 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<Process> 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<std::vector<u8>> backing_block; +    /// Offset into the backing block for this shared memory. +    u32 backing_block_offset;      /// Size of the memory block. Page-aligned.      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 3fc1ab4ee..bfb3327ce 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 6d72e8188..73fce6079 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<Kernel::SharedMemory> shared_font_mem; +static bool shared_font_relocated = false;  static Kernel::SharedPtr<Kernel::Mutex> lock;  static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event  static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event -static std::shared_ptr<std::vector<u8>> shared_font; -  static u32 cpu_percent; ///< CPU time available to the running application  /// 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<std::vector<u8>>((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 000000000..b0d39d4a5 --- /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<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address) { +    static const u32 SharedFontStartOffset = 0x80; +    u8* data = shared_font->GetPointer(SharedFontStartOffset); + +    CFNT cfnt; +    memcpy(&cfnt, data, sizeof(cfnt)); + +    // Advance past the header +    data = shared_font->GetPointer(SharedFontStartOffset + cfnt.header_size); + +    for (unsigned block = 0; block < cfnt.num_blocks; ++block) { + +        u32 section_size = 0; +        if (memcmp(data, "FINF", 4) == 0) { +            BCFNT::FINF finf; +            memcpy(&finf, data, sizeof(finf)); +            section_size = finf.section_size; + +            // Relocate the offsets in the FINF section +            finf.cmap_offset += new_address - previous_address; +            finf.cwdh_offset += new_address - previous_address; +            finf.tglp_offset += new_address - previous_address; + +            memcpy(data, &finf, sizeof(finf)); +        } else if (memcmp(data, "CMAP", 4) == 0) { +            BCFNT::CMAP cmap; +            memcpy(&cmap, data, sizeof(cmap)); +            section_size = cmap.section_size; + +            // Relocate the offsets in the CMAP section +            cmap.next_cmap_offset += new_address - previous_address; + +            memcpy(data, &cmap, sizeof(cmap)); +        } else if (memcmp(data, "CWDH", 4) == 0) { +            BCFNT::CWDH cwdh; +            memcpy(&cwdh, data, sizeof(cwdh)); +            section_size = cwdh.section_size; + +            // Relocate the offsets in the CWDH section +            cwdh.next_cwdh_offset += new_address - previous_address; + +            memcpy(data, &cwdh, sizeof(cwdh)); +        } else if (memcmp(data, "TGLP", 4) == 0) { +            BCFNT::TGLP tglp; +            memcpy(&tglp, data, sizeof(tglp)); +            section_size = tglp.section_size; + +            // Relocate the offsets in the TGLP section +            tglp.sheet_data_offset += new_address - previous_address; + +            memcpy(data, &tglp, sizeof(tglp)); +        } + +        data += section_size; +    } +} + +} // namespace BCFNT +} // namespace APT +} // namespace Service
\ 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 000000000..388c6bea0 --- /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<Kernel::SharedMemory> 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 6318bf2a7..d2bb8941c 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 <cstring> +#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<Kernel::Mutex> 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 b4c146e08..8ded9b09b 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 1053d0f40..d216cecb4 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 505c441c6..079a87e48 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 60c8747f3..3a53126c1 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<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(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<MemoryPermission>(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<SharedMemory> shared_memory = nullptr;      using Kernel::MemoryPermission; -    SharedPtr<SharedMemory> 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<MemoryPermission>(my_permission)) || +        !VerifyPermissions(static_cast<MemoryPermission>(other_permission))) +        return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, +                          ErrorSummary::InvalidArgument, ErrorLevel::Usage); + +    if (addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) { +        return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); +    } + +    // When trying to create a memory block with address = 0, +    // if the process has the Shared Device Memory flag in the exheader, +    // then we have to allocate from the same region as the caller process instead of the BASE region. +    Kernel::MemoryRegion region = Kernel::MemoryRegion::BASE; +    if (addr == 0 && Kernel::g_current_process->flags.shared_device_mem) +        region = Kernel::g_current_process->flags.memory_region; + +    shared_memory = SharedMemory::Create(Kernel::g_current_process, size, +                                static_cast<MemoryPermission>(my_permission), static_cast<MemoryPermission>(other_permission), addr, region);      CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory))); -    LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr); +    LOG_WARNING(Kernel_SVC, "called addr=0x%08X", addr);      return RESULT_SUCCESS;  } | 
