MerryMage 10 лет назад
Родитель
Сommit
2b93313348

+ 1 - 0
src/core/CMakeLists.txt

@@ -250,6 +250,7 @@ set(HEADERS
             tracer/citrace.h
             memory.h
             memory_setup.h
+            mmio.h
             settings.h
             system.h
             )

+ 4 - 3
src/core/hle/kernel/vm_manager.cpp

@@ -8,6 +8,7 @@
 
 #include "core/hle/kernel/vm_manager.h"
 #include "core/memory_setup.h"
+#include "core/mmio.h"
 
 namespace Kernel {
 
@@ -104,7 +105,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8 * m
     return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
 }
 
-ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state) {
+ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler) {
     // This is the appropriately sized VMA that will turn into our allocation.
     CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
     VirtualMemoryArea& final_vma = vma_handle->second;
@@ -114,6 +115,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u3
     final_vma.permissions = VMAPermission::ReadWrite;
     final_vma.meminfo_state = state;
     final_vma.paddr = paddr;
+    final_vma.mmio_handler = mmio_handler;
     UpdatePageTableForVMA(final_vma);
 
     return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
@@ -330,8 +332,7 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
         Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory);
         break;
     case VMAType::MMIO:
-        // TODO(yuriks): Add support for MMIO handlers.
-        Memory::MapIoRegion(vma.base, vma.size);
+        Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler);
         break;
     }
 }

+ 4 - 1
src/core/hle/kernel/vm_manager.h

@@ -11,6 +11,7 @@
 #include "common/common_types.h"
 
 #include "core/hle/result.h"
+#include "core/mmio.h"
 
 namespace Kernel {
 
@@ -92,6 +93,7 @@ struct VirtualMemoryArea {
     // Settings for type = MMIO
     /// Physical address of the register area this VMA maps to.
     PAddr paddr = 0;
+    Memory::MMIORegionPointer mmio_handler = nullptr;
 
     /// Tests if this area can be merged to the right with `next`.
     bool CanBeMergedWith(const VirtualMemoryArea& next) const;
@@ -168,8 +170,9 @@ public:
      * @param paddr The physical address where the registers are present.
      * @param size Size of the mapping.
      * @param state MemoryState tag to attach to the VMA.
+     * @param mmio_handler The handler that will implement read and write for this MMIO region.
      */
-    ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state);
+    ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler);
 
     /// Unmaps a range of addresses, splitting VMAs as necessary.
     ResultCode UnmapRange(VAddr target, u32 size);

+ 80 - 6
src/core/memory.cpp

@@ -13,6 +13,7 @@
 #include "core/hle/kernel/process.h"
 #include "core/memory.h"
 #include "core/memory_setup.h"
+#include "core/mmio.h"
 
 namespace Memory {
 
@@ -25,6 +26,12 @@ enum class PageType {
     Special,
 };
 
+struct SpecialRegion {
+    VAddr base;
+    u32 size;
+    MMIORegionPointer handler;
+};
+
 /**
  * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
  * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
@@ -40,9 +47,14 @@ struct PageTable {
      */
     std::array<u8*, NUM_ENTRIES> pointers;
 
+    /**
+     * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of type `Special`.
+     */
+    std::vector<SpecialRegion> special_regions;
+
     /**
      * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
-     * the corresponding entry in `pointer` MUST be set to null.
+     * the corresponding entry in `pointers` MUST be set to null.
      */
     std::array<PageType, NUM_ENTRIES> attributes;
 };
@@ -80,10 +92,12 @@ void MapMemoryRegion(VAddr base, u32 size, u8* target) {
     MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
 }
 
-void MapIoRegion(VAddr base, u32 size) {
+void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) {
     ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
     ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
     MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
+
+    current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
 }
 
 void UnmapRegion(VAddr base, u32 size) {
@@ -92,6 +106,22 @@ void UnmapRegion(VAddr base, u32 size) {
     MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
 }
 
+/**
+ * This function should only be called for virtual addreses with attribute `PageType::Special`.
+ */
+static MMIORegionPointer GetMMIOHandler(VAddr vaddr) {
+    for (const auto& region : current_page_table->special_regions) {
+        if (vaddr >= region.base && vaddr < (region.base + region.size)) {
+            return region.handler;
+        }
+    }
+    ASSERT_MSG(false, "Mapped IO page without a handler @ %08X", vaddr);
+    return nullptr; // Should never happen
+}
+
+template<typename T>
+T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr);
+
 template <typename T>
 T Read(const VAddr vaddr) {
     const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
@@ -108,14 +138,17 @@ T Read(const VAddr vaddr) {
         return 0;
     case PageType::Memory:
         ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
+        break;
     case PageType::Special:
-        LOG_ERROR(HW_Memory, "I/O reads aren't implemented yet @ %08X", vaddr);
-        return 0;
+        return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr);
     default:
         UNREACHABLE();
     }
 }
 
+template<typename T>
+void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data);
+
 template <typename T>
 void Write(const VAddr vaddr, const T data) {
     u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
@@ -131,9 +164,10 @@ void Write(const VAddr vaddr, const T data) {
         return;
     case PageType::Memory:
         ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
+        break;
     case PageType::Special:
-        LOG_ERROR(HW_Memory, "I/O writes aren't implemented yet @ %08X", vaddr);
-        return;
+        WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data);
+        break;
     default:
         UNREACHABLE();
     }
@@ -191,6 +225,46 @@ void WriteBlock(const VAddr addr, const u8* data, const size_t size) {
     }
 }
 
+template<>
+u8 ReadMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr) {
+    return mmio_handler->Read8(addr);
+}
+
+template<>
+u16 ReadMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr) {
+    return mmio_handler->Read16(addr);
+}
+
+template<>
+u32 ReadMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr) {
+    return mmio_handler->Read32(addr);
+}
+
+template<>
+u64 ReadMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr) {
+    return mmio_handler->Read64(addr);
+}
+
+template<>
+void WriteMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr, const u8 data) {
+    mmio_handler->Write8(addr, data);
+}
+
+template<>
+void WriteMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr, const u16 data) {
+    mmio_handler->Write16(addr, data);
+}
+
+template<>
+void WriteMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr, const u32 data) {
+    mmio_handler->Write32(addr, data);
+}
+
+template<>
+void WriteMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr, const u64 data) {
+    mmio_handler->Write64(addr, data);
+}
+
 PAddr VirtualToPhysicalAddress(const VAddr addr) {
     if (addr == 0) {
         return 0;

+ 4 - 3
src/core/memory_setup.h

@@ -23,10 +23,11 @@ void MapMemoryRegion(VAddr base, u32 size, u8* target);
 
 /**
  * Maps a region of the emulated process address space as a IO region.
- * @note Currently this can only be used to mark a region as being IO, since actual memory-mapped
- *       IO isn't yet supported.
+ * @param base The address to start mapping at. Must be page-aligned.
+ * @param size The amount of bytes to map. Must be page-aligned.
+ * @param mmio_handler The handler that backs the mapping.
  */
-void MapIoRegion(VAddr base, u32 size);
+void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler);
 
 void UnmapRegion(VAddr base, u32 size);
 

+ 34 - 0
src/core/mmio.h

@@ -0,0 +1,34 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include "common/common_types.h"
+
+namespace Memory {
+
+/**
+ * Represents a device with memory mapped IO.
+ * A device may be mapped to multiple regions of memory.
+ */
+class MMIORegion {
+public:
+    virtual ~MMIORegion() = default;
+
+    virtual u8 Read8(VAddr addr) = 0;
+    virtual u16 Read16(VAddr addr) = 0;
+    virtual u32 Read32(VAddr addr) = 0;
+    virtual u64 Read64(VAddr addr) = 0;
+
+    virtual void Write8(VAddr addr, u8 data) = 0;
+    virtual void Write16(VAddr addr, u16 data) = 0;
+    virtual void Write32(VAddr addr, u32 data) = 0;
+    virtual void Write64(VAddr addr, u64 data) = 0;
+};
+
+using MMIORegionPointer = std::shared_ptr<MMIORegion>;
+
+};