[kernel][vm] Add ZX_INFO_VMO to zx_object_get_info.

Adds ZX_INFO_VMO option to zx_object_get_info(). Returning back
the various vmo attributes.

ZX-2327.

Test: Added a unit test to query vmo attributes with ZX_INFO_VMO
to the vmo-test.

Change-Id: Ic16452aea3b71061b21141fa5c7a17b5a1f277ff
diff --git a/docs/syscalls/object_get_info.md b/docs/syscalls/object_get_info.md
index 007cfbc..2b4c440 100644
--- a/docs/syscalls/object_get_info.md
+++ b/docs/syscalls/object_get_info.md
@@ -298,6 +298,57 @@
 This returns a single *zx_info_vmar_t* that describes the range of address
 space that the VMAR occupies.
 
+### ZX_INFO_VMO
+
+*handle* type: **VM Object**
+
+*buffer* type: **zx_info_vmo_t[1]**
+
+```
+typedef struct zx_info_vmo {
+    // The koid of this VMO.
+    zx_koid_t koid;
+
+    // The name of this VMO.
+    char name[ZX_MAX_NAME_LEN];
+
+    // The size of this VMO.
+    uint64_t size_bytes;
+
+    // If this VMO is a clone, the koid of its parent. Otherwise, zero.
+    zx_koid_t parent_koid;
+
+    // The number of clones of this VMO, if any.
+    size_t num_children;
+
+    // The number of times this VMO is currently mapped into VMARs.
+    size_t num_mappings;
+
+    // An estimate of the number of unique address spaces that
+    // this VMO is mapped into.
+    size_t share_count;
+
+    // Bitwise OR of ZX_INFO_VMO_* values.
+    uint32_t flags;
+
+    // If |ZX_INFO_VMO_TYPE(flags) == ZX_INFO_VMO_TYPE_PAGED|, the amount of
+    // memory currently allocated to this VMO.
+    uint64_t committed_bytes;
+
+    // If |flags & ZX_INFO_VMO_VIA_HANDLE|, the handle rights.
+    // Undefined otherwise.
+    zx_rights_t handle_rights;
+
+    // VMO creation options. This is a bitmask of
+    // kResizable    = (1u << 0);
+    // kContiguous   = (1u << 1);
+    uint32_t create_options;
+} zx_info_vmo_t;
+```
+
+This returns a single *zx_info_vmo_t* that describes various attrubutes of
+the VMO.
+
 ### ZX_INFO_SOCKET
 
 *handle* type: **Socket**
diff --git a/kernel/object/diagnostics.cpp b/kernel/object/diagnostics.cpp
index 666ae7e..3cbc009 100644
--- a/kernel/object/diagnostics.cpp
+++ b/kernel/object/diagnostics.cpp
@@ -553,29 +553,6 @@
 }
 
 namespace {
-zx_info_vmo_t VmoToInfoEntry(const VmObject* vmo,
-                             bool is_handle, zx_rights_t handle_rights) {
-    zx_info_vmo_t entry = {};
-    entry.koid = vmo->user_id();
-    vmo->get_name(entry.name, sizeof(entry.name));
-    entry.size_bytes = vmo->size();
-    entry.parent_koid = vmo->parent_user_id();
-    entry.num_children = vmo->num_children();
-    entry.num_mappings = vmo->num_mappings();
-    entry.share_count = vmo->share_count();
-    entry.flags =
-        (vmo->is_paged() ? ZX_INFO_VMO_TYPE_PAGED : ZX_INFO_VMO_TYPE_PHYSICAL) |
-        (vmo->is_cow_clone() ? ZX_INFO_VMO_IS_COW_CLONE : 0);
-    entry.committed_bytes = vmo->AllocatedPages() * PAGE_SIZE;
-    if (is_handle) {
-        entry.flags |= ZX_INFO_VMO_VIA_HANDLE;
-        entry.handle_rights = handle_rights;
-    } else {
-        entry.flags |= ZX_INFO_VMO_VIA_MAPPING;
-    }
-    return entry;
-}
-
 // Builds a list of all VMOs mapped into a VmAspace.
 class AspaceVmoEnumerator final : public VmEnumerator {
 public:
diff --git a/kernel/object/include/object/vm_object_dispatcher.h b/kernel/object/include/object/vm_object_dispatcher.h
index 22b5687..0bfca36 100644
--- a/kernel/object/include/object/vm_object_dispatcher.h
+++ b/kernel/object/include/object/vm_object_dispatcher.h
@@ -49,6 +49,8 @@
 
     zx_status_t SetMappingCachePolicy(uint32_t cache_policy);
 
+    zx_info_vmo_t GetVmoInfo();
+
     const fbl::RefPtr<VmObject>& vmo() const { return vmo_; }
 
 private:
@@ -66,3 +68,6 @@
     // shares the same lock.
     CookieJar cookie_jar_;
 };
+
+zx_info_vmo_t VmoToInfoEntry(const VmObject* vmo,
+                             bool is_handle, zx_rights_t handle_rights);
diff --git a/kernel/object/vm_object_dispatcher.cpp b/kernel/object/vm_object_dispatcher.cpp
index 69c6161..807a44b 100644
--- a/kernel/object/vm_object_dispatcher.cpp
+++ b/kernel/object/vm_object_dispatcher.cpp
@@ -95,6 +95,35 @@
     return ZX_OK;
 }
 
+zx_info_vmo_t VmoToInfoEntry(const VmObject* vmo,
+                             bool is_handle, zx_rights_t handle_rights) {
+    zx_info_vmo_t entry = {};
+    entry.koid = vmo->user_id();
+    vmo->get_name(entry.name, sizeof(entry.name));
+    entry.size_bytes = vmo->size();
+    entry.create_options = vmo->create_options();
+    entry.parent_koid = vmo->parent_user_id();
+    entry.num_children = vmo->num_children();
+    entry.num_mappings = vmo->num_mappings();
+    entry.share_count = vmo->share_count();
+    entry.flags =
+        (vmo->is_paged() ? ZX_INFO_VMO_TYPE_PAGED : ZX_INFO_VMO_TYPE_PHYSICAL) |
+        (vmo->is_cow_clone() ? ZX_INFO_VMO_IS_COW_CLONE : 0);
+    entry.committed_bytes = vmo->AllocatedPages() * PAGE_SIZE;
+    if (is_handle) {
+        entry.flags |= ZX_INFO_VMO_VIA_HANDLE;
+        entry.handle_rights = handle_rights;
+    } else {
+        entry.flags |= ZX_INFO_VMO_VIA_MAPPING;
+    }
+    return entry;
+}
+
+zx_info_vmo_t VmObjectDispatcher::GetVmoInfo(void)
+{
+    return VmoToInfoEntry(vmo().get(), true, 0);
+}
+
 zx_status_t VmObjectDispatcher::RangeOp(uint32_t op, uint64_t offset, uint64_t size,
                                         user_inout_ptr<void> buffer, size_t buffer_size,
                                         zx_rights_t rights) {
diff --git a/kernel/syscalls/object.cpp b/kernel/syscalls/object.cpp
index c9d28b3..fe83a25 100644
--- a/kernel/syscalls/object.cpp
+++ b/kernel/syscalls/object.cpp
@@ -28,6 +28,7 @@
 #include <object/socket_dispatcher.h>
 #include <object/thread_dispatcher.h>
 #include <object/vm_address_region_dispatcher.h>
+#include <object/vm_object_dispatcher.h>
 
 #include <fbl/ref_ptr.h>
 
@@ -386,6 +387,35 @@
         }
         return status;
     }
+    case ZX_INFO_VMO: {
+        // lookup the dispatcher from handle
+        fbl::RefPtr<VmObjectDispatcher> vmo;
+        zx_status_t status = up->GetDispatcher(handle, &vmo);
+        if (status < 0)
+            return status;
+        auto vmos = _buffer.reinterpret<zx_info_vmo_t>();
+        zx_info_vmo_t entry;
+
+        entry = vmo->GetVmoInfo();
+        if (vmos.copy_array_to_user(&entry, 1, 0) != ZX_OK) {
+            return ZX_ERR_INVALID_ARGS;
+        }
+        if (_actual) {
+            size_t count = 1;
+            zx_status_t status = _actual.copy_to_user(count);
+            if (status != ZX_OK)
+                return status;
+        }
+        // Avail returned is 0, since we were just asked to read
+        // the info for a single vmo, hence nothing more is available (?)
+        if (_avail) {
+            size_t count = 0;
+            zx_status_t status = _avail.copy_to_user(count);
+            if (status != ZX_OK)
+                return status;
+        }
+        return status;
+    }
     case ZX_INFO_VMAR: {
         fbl::RefPtr<VmAddressRegionDispatcher> vmar;
         zx_status_t status = up->GetDispatcher(handle, &vmar);
diff --git a/kernel/vm/include/vm/vm_object.h b/kernel/vm/include/vm/vm_object.h
index b492b01..ad90330 100644
--- a/kernel/vm/include/vm/vm_object.h
+++ b/kernel/vm/include/vm/vm_object.h
@@ -47,6 +47,7 @@
     virtual zx_status_t ResizeLocked(uint64_t size) TA_REQ(lock_) { return ZX_ERR_NOT_SUPPORTED; }
 
     virtual uint64_t size() const { return 0; }
+    virtual uint32_t create_options() const { return 0; }
 
     // Returns true if the object is backed by RAM.
     virtual bool is_paged() const { return false; }
diff --git a/kernel/vm/include/vm/vm_object_paged.h b/kernel/vm/include/vm/vm_object_paged.h
index 00bacf5..e5c02fd 100644
--- a/kernel/vm/include/vm/vm_object_paged.h
+++ b/kernel/vm/include/vm/vm_object_paged.h
@@ -46,6 +46,7 @@
 
     zx_status_t Resize(uint64_t size) override;
     zx_status_t ResizeLocked(uint64_t size) override TA_REQ(lock_);
+    uint32_t create_options() const override { return options_; }
     uint64_t size() const override
         // TODO: Figure out whether it's safe to lock here without causing
         // any deadlocks.
diff --git a/system/public/zircon/syscalls/object.h b/system/public/zircon/syscalls/object.h
index 7ef8df6..59ed23d 100644
--- a/system/public/zircon/syscalls/object.h
+++ b/system/public/zircon/syscalls/object.h
@@ -34,6 +34,7 @@
     ZX_INFO_BTI                        = 20, // zx_info_bti_t[1]
     ZX_INFO_PROCESS_HANDLE_STATS       = 21, // zx_info_process_handle_stats_t[1]
     ZX_INFO_SOCKET                     = 22, // zx_info_socket_t[1]
+    ZX_INFO_VMO                        = 23, // zx_info_vmo_t[1]
 } zx_object_info_topic_t;
 
 typedef uint32_t zx_obj_props_t;
@@ -292,6 +293,11 @@
     // If |flags & ZX_INFO_VMO_VIA_HANDLE|, the handle rights.
     // Undefined otherwise.
     zx_rights_t handle_rights;
+
+    // VMO creation options. This is a bitmask of
+    // kResizable    = (1u << 0);
+    // kContiguous   = (1u << 1);
+    uint32_t create_options;
 } zx_info_vmo_t;
 
 // kernel statistics per cpu
diff --git a/system/utest/vmo/vmo.cpp b/system/utest/vmo/vmo.cpp
index f731fcb..2fabe3d 100644
--- a/system/utest/vmo/vmo.cpp
+++ b/system/utest/vmo/vmo.cpp
@@ -407,6 +407,51 @@
     return vmo_no_resize_helper(vmo, len);
 }
 
+bool vmo_info_test() {
+    size_t len = PAGE_SIZE * 4;
+    zx_handle_t vmo = ZX_HANDLE_INVALID;
+    zx_info_vmo_t info;
+    zx_status_t status;
+
+    // Create a non-resizeable VMO, query the INFO on it
+    // and dump it.
+    status = zx_vmo_create(len, ZX_VMO_NON_RESIZABLE, &vmo);
+    EXPECT_EQ(ZX_OK, status, "vm_info_test: vmo_create");
+
+    status = zx_object_get_info(vmo, ZX_INFO_VMO, &info,
+                                sizeof(info), nullptr, nullptr);
+    EXPECT_EQ(ZX_OK, status, "vm_info_test: info_vmo");
+
+    status = zx_handle_close(vmo);
+    EXPECT_EQ(ZX_OK, status, "vm_info_test: handle_close");
+
+    EXPECT_EQ(info.size_bytes, len, "vm_info_test: info_vmo.size_bytes");
+    EXPECT_NE(info.create_options, (1u << 0),
+              "vm_info_test: info_vmo.create_options");
+//    printf("NON_Resizeable VMO, size = %lu, create_options = %ux\n",
+//           info.size_bytes, info.create_options);
+
+    // Create a resizeable VMO, query the INFO on it and dump it.
+    len = PAGE_SIZE * 8;
+    zx_vmo_create(len, 0, &vmo);
+    EXPECT_EQ(ZX_OK, status, "vm_info_test: vmo_create");
+
+    status = zx_object_get_info(vmo, ZX_INFO_VMO, &info,
+                                sizeof(info), nullptr, nullptr);
+    EXPECT_EQ(ZX_OK, status, "vm_info_test: info_vmo");
+
+    status = zx_handle_close(vmo);
+    EXPECT_EQ(ZX_OK, status, "vm_info_test: handle_close");
+
+    EXPECT_EQ(info.size_bytes, len, "vm_info_test: info_vmo.size_bytes");
+    EXPECT_EQ(info.create_options, (1u << 0),
+              "vm_info_test: info_vmo.create_options");
+//    printf("Resizeable VMO, size = %lu, create_options = %ux\n",
+//           info.size_bytes, info.create_options);
+
+    END_TEST;
+}
+
 bool vmo_no_resize_clone_test() {
     const size_t len = PAGE_SIZE * 4;
     zx_handle_t vmo = ZX_HANDLE_INVALID;
@@ -1748,6 +1793,7 @@
 RUN_TEST(vmo_resize_hazard);
 RUN_TEST(vmo_clone_resize_clone_hazard);
 RUN_TEST(vmo_clone_resize_parent_ok);
+RUN_TEST(vmo_info_test);
 RUN_TEST_LARGE(vmo_unmap_coherency);
 END_TEST_CASE(vmo_tests)