瀏覽代碼

UserspaceEmulator: Add support for shared buffers (shbuf)

We track these separately from regular mmap() regions, as they have
slightly different behaviors.
Andreas Kling 5 年之前
父節點
當前提交
2da44dba44

+ 1 - 0
DevTools/UserspaceEmulator/CMakeLists.txt

@@ -1,6 +1,7 @@
 set(SOURCES
 set(SOURCES
     Emulator.cpp
     Emulator.cpp
     MmapRegion.cpp
     MmapRegion.cpp
+    SharedBufferRegion.cpp
     SimpleRegion.cpp
     SimpleRegion.cpp
     SoftCPU.cpp
     SoftCPU.cpp
     SoftMMU.cpp
     SoftMMU.cpp

+ 105 - 12
DevTools/UserspaceEmulator/Emulator.cpp

@@ -26,12 +26,14 @@
 
 
 #include "Emulator.h"
 #include "Emulator.h"
 #include "MmapRegion.h"
 #include "MmapRegion.h"
+#include "SharedBufferRegion.h"
 #include "SimpleRegion.h"
 #include "SimpleRegion.h"
 #include "SoftCPU.h"
 #include "SoftCPU.h"
 #include <AK/LexicalPath.h>
 #include <AK/LexicalPath.h>
 #include <AK/LogStream.h>
 #include <AK/LogStream.h>
 #include <Kernel/API/Syscall.h>
 #include <Kernel/API/Syscall.h>
 #include <fcntl.h>
 #include <fcntl.h>
+#include <serenity.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
 #include <sys/mman.h>
 #include <sys/mman.h>
@@ -194,6 +196,20 @@ u32 Emulator::virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3)
     dbgprintf("Syscall: %s (%x)\n", Syscall::to_string((Syscall::Function)function), function);
     dbgprintf("Syscall: %s (%x)\n", Syscall::to_string((Syscall::Function)function), function);
 #endif
 #endif
     switch (function) {
     switch (function) {
+    case SC_shbuf_create:
+        return virt$shbuf_create(arg1, arg2);
+    case SC_shbuf_allow_pid:
+        return virt$shbuf_allow_pid(arg1, arg2);
+    case SC_shbuf_allow_all:
+        return virt$shbuf_allow_all(arg1);
+    case SC_shbuf_get:
+        return virt$shbuf_get(arg1, arg2);
+    case SC_shbuf_release:
+        return virt$shbuf_release(arg1);
+    case SC_shbuf_seal:
+        return virt$shbuf_seal(arg1);
+    case SC_shbuf_set_volatile:
+        return virt$shbuf_set_volatile(arg1, arg2);
     case SC_mmap:
     case SC_mmap:
         return virt$mmap(arg1);
         return virt$mmap(arg1);
     case SC_munmap:
     case SC_munmap:
@@ -260,6 +276,8 @@ u32 Emulator::virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3)
         return virt$recvfrom(arg1);
         return virt$recvfrom(arg1);
     case SC_kill:
     case SC_kill:
         return virt$kill(arg1, arg2);
         return virt$kill(arg1, arg2);
+    case SC_set_mmap_name:
+        return virt$set_mmap_name(arg1);
     case SC_exit:
     case SC_exit:
         virt$exit((int)arg1);
         virt$exit((int)arg1);
         return 0;
         return 0;
@@ -270,6 +288,69 @@ u32 Emulator::virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3)
     }
     }
 }
 }
 
 
+int Emulator::virt$shbuf_create(int size, FlatPtr buffer)
+{
+    u8* host_data = nullptr;
+    int shbuf_id = syscall(SC_shbuf_create, size, &host_data);
+    if (shbuf_id < 0)
+        return -errno;
+    FlatPtr address = allocate_vm(size, PAGE_SIZE);
+    auto region = SharedBufferRegion::create_with_shbuf_id(address, size, shbuf_id, host_data);
+    m_mmu.add_region(move(region));
+    m_mmu.copy_to_vm(buffer, &address, sizeof(address));
+    return shbuf_id;
+}
+
+FlatPtr Emulator::virt$shbuf_get(int shbuf_id, FlatPtr size_ptr)
+{
+    size_t host_size = 0;
+    void* host_data = (void*)syscall(SC_shbuf_get, shbuf_id, &host_size);
+    if (host_data == (void*)-1)
+        return -errno;
+    FlatPtr address = allocate_vm(host_size, PAGE_SIZE);
+    auto region = SharedBufferRegion::create_with_shbuf_id(address, host_size, shbuf_id, (u8*)host_data);
+    m_mmu.add_region(move(region));
+    m_mmu.copy_to_vm(size_ptr, &host_size, sizeof(host_size));
+    return address;
+}
+
+int Emulator::virt$shbuf_allow_pid(int shbuf_id, pid_t peer_pid)
+{
+    auto* region = m_mmu.shbuf_region(shbuf_id);
+    ASSERT(region);
+    return region->allow_pid(peer_pid);
+}
+
+int Emulator::virt$shbuf_allow_all(int shbuf_id)
+{
+    auto* region = m_mmu.shbuf_region(shbuf_id);
+    ASSERT(region);
+    return region->allow_all();
+}
+
+int Emulator::virt$shbuf_release(int shbuf_id)
+{
+    auto* region = m_mmu.shbuf_region(shbuf_id);
+    ASSERT(region);
+    auto rc = region->release();
+    m_mmu.remove_region(*region);
+    return rc;
+}
+
+int Emulator::virt$shbuf_seal(int shbuf_id)
+{
+    auto* region = m_mmu.shbuf_region(shbuf_id);
+    ASSERT(region);
+    return region->seal();
+}
+
+int Emulator::virt$shbuf_set_volatile(int shbuf_id, bool is_volatile)
+{
+    auto* region = m_mmu.shbuf_region(shbuf_id);
+    ASSERT(region);
+    return region->set_volatile(is_volatile);
+}
+
 int Emulator::virt$fstat(int fd, FlatPtr statbuf)
 int Emulator::virt$fstat(int fd, FlatPtr statbuf)
 {
 {
     struct stat local_statbuf;
     struct stat local_statbuf;
@@ -337,6 +418,12 @@ int Emulator::virt$kill(pid_t pid, int signal)
     return syscall(SC_kill, pid, signal);
     return syscall(SC_kill, pid, signal);
 }
 }
 
 
+int Emulator::virt$set_mmap_name(FlatPtr)
+{
+    // FIXME: Implement.
+    return 0;
+}
+
 int Emulator::virt$get_process_name(FlatPtr buffer, int size)
 int Emulator::virt$get_process_name(FlatPtr buffer, int size)
 {
 {
     if (size < 9)
     if (size < 9)
@@ -503,27 +590,33 @@ u32 Emulator::virt$munmap(FlatPtr address, u32 size)
     return 0;
     return 0;
 }
 }
 
 
-u32 Emulator::virt$mmap(u32 params_addr)
+FlatPtr Emulator::allocate_vm(size_t size, size_t alignment)
 {
 {
-    Syscall::SC_mmap_params params;
-    mmu().copy_from_vm(&params, params_addr, sizeof(params));
-
-    ASSERT(params.addr == 0);
-
     // FIXME: Write a proper VM allocator
     // FIXME: Write a proper VM allocator
-    static u32 next_address = 0x30000000;
+    static FlatPtr next_address = 0x30000000;
 
 
-    u32 final_address = 0;
-    u32 final_size = round_up_to_power_of_two(params.size, PAGE_SIZE);
+    FlatPtr final_address;
 
 
-    if (params.alignment) {
+    if (alignment) {
         // FIXME: What if alignment is not a power of 2?
         // FIXME: What if alignment is not a power of 2?
-        final_address = round_up_to_power_of_two(next_address, params.alignment);
+        final_address = round_up_to_power_of_two(next_address, alignment);
     } else {
     } else {
         final_address = next_address;
         final_address = next_address;
     }
     }
 
 
-    next_address = final_address + final_size;
+    next_address = final_address + size;
+    return final_address;
+}
+
+u32 Emulator::virt$mmap(u32 params_addr)
+{
+    Syscall::SC_mmap_params params;
+    mmu().copy_from_vm(&params, params_addr, sizeof(params));
+
+    ASSERT(params.addr == 0);
+
+    u32 final_size = round_up_to_power_of_two(params.size, PAGE_SIZE);
+    u32 final_address = allocate_vm(final_size, params.alignment);
 
 
     if (params.flags & MAP_ANONYMOUS)
     if (params.flags & MAP_ANONYMOUS)
         mmu().add_region(MmapRegion::create_anonymous(final_address, final_size, params.prot));
         mmu().add_region(MmapRegion::create_anonymous(final_address, final_size, params.prot));

+ 10 - 0
DevTools/UserspaceEmulator/Emulator.h

@@ -57,6 +57,13 @@ private:
 
 
     void setup_stack(const Vector<String>& arguments);
     void setup_stack(const Vector<String>& arguments);
 
 
+    int virt$shbuf_create(int size, FlatPtr buffer);
+    int virt$shbuf_allow_pid(int, pid_t peer_pid);
+    int virt$shbuf_allow_all(int);
+    FlatPtr virt$shbuf_get(int shbuf_id, FlatPtr size);
+    int virt$shbuf_release(int shbuf_id);
+    int virt$shbuf_seal(int shbuf_id);
+    int virt$shbuf_set_volatile(int shbuf_id, bool);
     u32 virt$mmap(u32);
     u32 virt$mmap(u32);
     u32 virt$munmap(FlatPtr address, u32 size);
     u32 virt$munmap(FlatPtr address, u32 size);
     u32 virt$gettid();
     u32 virt$gettid();
@@ -75,6 +82,7 @@ private:
     int virt$mkdir(FlatPtr path, size_t path_length, mode_t mode);
     int virt$mkdir(FlatPtr path, size_t path_length, mode_t mode);
     int virt$unlink(FlatPtr path, size_t path_length);
     int virt$unlink(FlatPtr path, size_t path_length);
     int virt$get_process_name(FlatPtr buffer, int size);
     int virt$get_process_name(FlatPtr buffer, int size);
+    int virt$set_mmap_name(FlatPtr);
     int virt$dbgputstr(FlatPtr characters, int length);
     int virt$dbgputstr(FlatPtr characters, int length);
     int virt$dbgputch(char);
     int virt$dbgputch(char);
     int virt$fchmod(int, mode_t);
     int virt$fchmod(int, mode_t);
@@ -92,6 +100,8 @@ private:
     int virt$connect(int sockfd, FlatPtr address, socklen_t address_size);
     int virt$connect(int sockfd, FlatPtr address, socklen_t address_size);
     void virt$exit(int);
     void virt$exit(int);
 
 
+    FlatPtr allocate_vm(size_t size, size_t alignment);
+
     bool m_shutdown { false };
     bool m_shutdown { false };
     int m_exit_status { 0 };
     int m_exit_status { 0 };
 };
 };

+ 112 - 0
DevTools/UserspaceEmulator/SharedBufferRegion.cpp

@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "SharedBufferRegion.h"
+#include "Emulator.h"
+#include <Kernel/API/Syscall.h>
+#include <serenity.h>
+#include <sys/mman.h>
+
+namespace UserspaceEmulator {
+
+NonnullOwnPtr<SharedBufferRegion> SharedBufferRegion::create_with_shbuf_id(u32 base, u32 size, int shbuf_id, u8* host_data)
+{
+    return adopt_own(*new SharedBufferRegion(base, size, shbuf_id, host_data));
+}
+
+SharedBufferRegion::SharedBufferRegion(u32 base, u32 size, int shbuf_id, u8* host_data)
+    : Region(base, size)
+    , m_data(host_data)
+    , m_shbuf_id(shbuf_id)
+{
+}
+
+SharedBufferRegion::~SharedBufferRegion()
+{
+}
+
+u8 SharedBufferRegion::read8(FlatPtr offset)
+{
+    ASSERT(offset < size());
+    return *reinterpret_cast<const u8*>(m_data + offset);
+}
+
+u16 SharedBufferRegion::read16(u32 offset)
+{
+    ASSERT(offset + 1 < size());
+    return *reinterpret_cast<const u16*>(m_data + offset);
+}
+
+u32 SharedBufferRegion::read32(u32 offset)
+{
+    ASSERT(offset + 3 < size());
+    return *reinterpret_cast<const u32*>(m_data + offset);
+}
+
+void SharedBufferRegion::write8(u32 offset, u8 value)
+{
+    ASSERT(offset < size());
+    *reinterpret_cast<u8*>(m_data + offset) = value;
+}
+
+void SharedBufferRegion::write16(u32 offset, u16 value)
+{
+    ASSERT(offset + 1 < size());
+    *reinterpret_cast<u16*>(m_data + offset) = value;
+}
+
+void SharedBufferRegion::write32(u32 offset, u32 value)
+{
+    ASSERT(offset + 3 < size());
+    *reinterpret_cast<u32*>(m_data + offset) = value;
+}
+
+int SharedBufferRegion::allow_all()
+{
+    return syscall(SC_shbuf_allow_all, m_shbuf_id);
+}
+
+int SharedBufferRegion::allow_pid(pid_t pid)
+{
+    return syscall(SC_shbuf_allow_pid, m_shbuf_id, pid);
+}
+
+int SharedBufferRegion::seal()
+{
+    return syscall(SC_shbuf_seal, m_shbuf_id);
+}
+
+int SharedBufferRegion::release()
+{
+    return syscall(SC_shbuf_release, m_shbuf_id);
+}
+
+int SharedBufferRegion::set_volatile(bool is_volatile)
+{
+    return syscall(SC_shbuf_set_volatile, m_shbuf_id, is_volatile);
+}
+
+}

+ 66 - 0
DevTools/UserspaceEmulator/SharedBufferRegion.h

@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "SoftMMU.h"
+#include <sys/mman.h>
+
+namespace UserspaceEmulator {
+
+class SharedBufferRegion final : public SoftMMU::Region {
+public:
+    static NonnullOwnPtr<SharedBufferRegion> create_with_shbuf_id(u32 base, u32 size, int shbuf_id, u8* shbuf_data);
+    virtual ~SharedBufferRegion() override;
+
+    virtual u8 read8(u32 offset) override;
+    virtual u16 read16(u32 offset) override;
+    virtual u32 read32(u32 offset) override;
+
+    virtual void write8(u32 offset, u8 value) override;
+    virtual void write16(u32 offset, u16 value) override;
+    virtual void write32(u32 offset, u32 value) override;
+
+    u8* data() { return m_data; }
+
+    bool is_shared_buffer() const override { return true; }
+
+    int shbuf_id() const { return m_shbuf_id; }
+
+    int allow_all();
+    int allow_pid(pid_t);
+    int seal();
+    int release();
+    int set_volatile(bool);
+
+private:
+    SharedBufferRegion(u32 base, u32 size, int shbuf_id, u8* shbuf_data);
+
+    u8* m_data { nullptr };
+    int m_shbuf_id { 0 };
+};
+
+}

+ 10 - 0
DevTools/UserspaceEmulator/SoftMMU.cpp

@@ -25,6 +25,7 @@
  */
  */
 
 
 #include "SoftMMU.h"
 #include "SoftMMU.h"
+#include "SharedBufferRegion.h"
 #include <AK/ByteBuffer.h>
 #include <AK/ByteBuffer.h>
 
 
 namespace UserspaceEmulator {
 namespace UserspaceEmulator {
@@ -45,11 +46,15 @@ void SoftMMU::add_region(NonnullOwnPtr<Region> region)
 {
 {
     ASSERT(!find_region({ 0x20, region->base() }));
     ASSERT(!find_region({ 0x20, region->base() }));
     // FIXME: More sanity checks pls
     // FIXME: More sanity checks pls
+    if (region->is_shared_buffer())
+        m_shbuf_regions.set(static_cast<SharedBufferRegion*>(region.ptr())->shbuf_id(), region.ptr());
     m_regions.append(move(region));
     m_regions.append(move(region));
 }
 }
 
 
 void SoftMMU::remove_region(Region& region)
 void SoftMMU::remove_region(Region& region)
 {
 {
+    if (region.is_shared_buffer())
+        m_shbuf_regions.remove(static_cast<SharedBufferRegion&>(region).shbuf_id());
     m_regions.remove_first_matching([&](auto& entry) { return entry.ptr() == &region; });
     m_regions.remove_first_matching([&](auto& entry) { return entry.ptr() == &region; });
 }
 }
 
 
@@ -144,4 +149,9 @@ ByteBuffer SoftMMU::copy_buffer_from_vm(const FlatPtr source, size_t size)
     return buffer;
     return buffer;
 }
 }
 
 
+SharedBufferRegion* SoftMMU::shbuf_region(int shbuf_id)
+{
+    return (SharedBufferRegion*)m_shbuf_regions.get(shbuf_id).value_or(nullptr);
+}
+
 }
 }

+ 7 - 0
DevTools/UserspaceEmulator/SoftMMU.h

@@ -26,6 +26,7 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <AK/HashMap.h>
 #include <AK/NonnullOwnPtrVector.h>
 #include <AK/NonnullOwnPtrVector.h>
 #include <AK/OwnPtr.h>
 #include <AK/OwnPtr.h>
 #include <AK/Types.h>
 #include <AK/Types.h>
@@ -33,6 +34,8 @@
 
 
 namespace UserspaceEmulator {
 namespace UserspaceEmulator {
 
 
+class SharedBufferRegion;
+
 class SoftMMU {
 class SoftMMU {
 public:
 public:
     class Region {
     class Region {
@@ -54,6 +57,7 @@ public:
         virtual u32 read32(u32 offset) = 0;
         virtual u32 read32(u32 offset) = 0;
 
 
         virtual u8* cacheable_ptr([[maybe_unused]] u32 offset) { return nullptr; }
         virtual u8* cacheable_ptr([[maybe_unused]] u32 offset) { return nullptr; }
+        virtual bool is_shared_buffer() const { return false; }
 
 
     protected:
     protected:
         Region(u32 base, u32 size)
         Region(u32 base, u32 size)
@@ -86,9 +90,12 @@ public:
     void copy_from_vm(void* destination, const FlatPtr source, size_t);
     void copy_from_vm(void* destination, const FlatPtr source, size_t);
     ByteBuffer copy_buffer_from_vm(const FlatPtr source, size_t);
     ByteBuffer copy_buffer_from_vm(const FlatPtr source, size_t);
 
 
+    SharedBufferRegion* shbuf_region(int shbuf_id);
+
 private:
 private:
     OwnPtr<Region> m_tls_region;
     OwnPtr<Region> m_tls_region;
     NonnullOwnPtrVector<Region> m_regions;
     NonnullOwnPtrVector<Region> m_regions;
+    HashMap<int, Region*> m_shbuf_regions;
 };
 };
 
 
 }
 }