From 525492428d3b1ccbe2096944a7525b242d4c9e7b Mon Sep 17 00:00:00 2001
From: Subv <subv2112@gmail.com>
Date: Sun, 20 May 2018 14:23:49 -0500
Subject: [PATCH] GPU: Implemented the nvmap Free ioctl.

It releases a reference to an nvmap object
---
 src/core/hle/service/nvdrv/devices/nvmap.cpp | 35 ++++++++++++++++++++
 src/core/hle/service/nvdrv/devices/nvmap.h   | 14 +++++++-
 2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 8d883209f6..d66fb3a9c5 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -30,6 +30,8 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& o
         return IocFromId(input, output);
     case IoctlCommand::Param:
         return IocParam(input, output);
+    case IoctlCommand::Free:
+        return IocFree(input, output);
     }
 
     UNIMPLEMENTED_MSG("Unimplemented ioctl");
@@ -45,6 +47,7 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
     object->id = next_id++;
     object->size = params.size;
     object->status = Object::Status::Created;
+    object->refcount = 1;
 
     u32 handle = next_handle++;
     handles[handle] = std::move(object);
@@ -101,6 +104,8 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
                             [&](const auto& entry) { return entry.second->id == params.id; });
     ASSERT(itr != handles.end());
 
+    itr->second->refcount++;
+
     // Return the existing handle instead of creating a new one.
     params.handle = itr->first;
 
@@ -142,4 +147,34 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
     return 0;
 }
 
+u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
+    enum FreeFlags {
+        Freed = 0,
+        NotFreedYet = 1,
+    };
+
+    IocFreeParams params;
+    std::memcpy(&params, input.data(), sizeof(params));
+
+    NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
+
+    auto itr = handles.find(params.handle);
+    ASSERT(itr != handles.end());
+
+    itr->second->refcount--;
+
+    params.refcount = itr->second->refcount;
+    params.size = itr->second->size;
+
+    if (itr->second->refcount == 0)
+        params.flags = Freed;
+    else
+        params.flags = NotFreedYet;
+
+    handles.erase(params.handle);
+
+    std::memcpy(output.data(), &params, sizeof(params));
+    return 0;
+}
+
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 431eb37736..5a30441672 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -34,6 +34,7 @@ public:
         u8 kind;
         VAddr addr;
         Status status;
+        u32 refcount;
     };
 
     std::shared_ptr<Object> GetObject(u32 handle) const {
@@ -59,7 +60,8 @@ private:
         FromId = 0xC0080103,
         Alloc = 0xC0200104,
         Param = 0xC00C0109,
-        GetId = 0xC008010E
+        GetId = 0xC008010E,
+        Free = 0xC0180105,
     };
 
     struct IocCreateParams {
@@ -102,11 +104,21 @@ private:
         u32_le value;
     };
 
+    struct IocFreeParams {
+        u32_le handle;
+        INSERT_PADDING_BYTES(4);
+        u64_le refcount;
+        u32_le size;
+        u32_le flags;
+    };
+    static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
+
     u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
     u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
     u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
     u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
     u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output);
+    u32 IocFree(const std::vector<u8>& input, std::vector<u8>& output);
 };
 
 } // namespace Service::Nvidia::Devices