1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #pragma once
8 
9 #include <assert.h>
10 #include <fbl/array.h>
11 #include <fbl/canary.h>
12 #include <fbl/intrusive_double_list.h>
13 #include <fbl/macros.h>
14 #include <fbl/ref_counted.h>
15 #include <fbl/ref_ptr.h>
16 #include <kernel/mutex.h>
17 #include <lib/user_copy/user_ptr.h>
18 #include <list.h>
19 #include <stdint.h>
20 #include <vm/page_source.h>
21 #include <vm/pmm.h>
22 #include <vm/vm.h>
23 #include <vm/vm_aspace.h>
24 #include <vm/vm_object.h>
25 #include <vm/vm_page_list.h>
26 #include <zircon/thread_annotations.h>
27 #include <zircon/types.h>
28 
29 // the main VM object type, holding a list of pages
30 class VmObjectPaged final : public VmObject {
31 public:
32     // |options_| is a bitmask of:
33     static constexpr uint32_t kResizable = (1u << 0);
34     static constexpr uint32_t kContiguous = (1u << 1);
35 
36     static zx_status_t Create(uint32_t pmm_alloc_flags,
37                               uint32_t options,
38                               uint64_t size, fbl::RefPtr<VmObject>* vmo);
39 
40     // Create a VMO backed by a contiguous range of physical memory.  The
41     // returned vmo has all of its pages committed, and does not allow
42     // decommitting them.
43     static zx_status_t CreateContiguous(uint32_t pmm_alloc_flags, uint64_t size,
44                                         uint8_t alignment_log2, fbl::RefPtr<VmObject>* vmo);
45 
46     static zx_status_t CreateFromROData(const void* data, size_t size, fbl::RefPtr<VmObject>* vmo);
47 
48     static zx_status_t CreateExternal(fbl::RefPtr<PageSource> src,
49                                       uint64_t size, fbl::RefPtr<VmObject>* vmo);
50 
51     zx_status_t Resize(uint64_t size) override;
52     zx_status_t ResizeLocked(uint64_t size) override TA_REQ(lock_);
create_options()53     uint32_t create_options() const override { return options_; }
size()54     uint64_t size() const override
55         // TODO: Figure out whether it's safe to lock here without causing
56         // any deadlocks.
57         TA_NO_THREAD_SAFETY_ANALYSIS { return size_; }
is_paged()58     bool is_paged() const override { return true; }
is_contiguous()59     bool is_contiguous() const override { return (options_ & kContiguous); }
is_resizable()60     bool is_resizable() const override { return (options_ & kResizable); }
61 
62     size_t AllocatedPagesInRange(uint64_t offset, uint64_t len) const override;
63 
64     zx_status_t CommitRange(uint64_t offset, uint64_t len) override;
65     zx_status_t DecommitRange(uint64_t offset, uint64_t len) override;
66 
67     zx_status_t Pin(uint64_t offset, uint64_t len) override;
68     void Unpin(uint64_t offset, uint64_t len) override;
69 
70     zx_status_t Read(void* ptr, uint64_t offset, size_t len) override;
71     zx_status_t Write(const void* ptr, uint64_t offset, size_t len) override;
72     zx_status_t Lookup(uint64_t offset, uint64_t len,
73                        vmo_lookup_fn_t lookup_fn, void* context) override;
74 
75     zx_status_t ReadUser(user_out_ptr<void> ptr, uint64_t offset, size_t len) override;
76     zx_status_t WriteUser(user_in_ptr<const void> ptr, uint64_t offset, size_t len) override;
77 
78     void Dump(uint depth, bool verbose) override;
79 
80     zx_status_t InvalidateCache(const uint64_t offset, const uint64_t len) override;
81     zx_status_t CleanCache(const uint64_t offset, const uint64_t len) override;
82     zx_status_t CleanInvalidateCache(const uint64_t offset, const uint64_t len) override;
83     zx_status_t SyncCache(const uint64_t offset, const uint64_t len) override;
84 
85     zx_status_t GetPageLocked(uint64_t offset, uint pf_flags, list_node* free_list,
86                               vm_page_t**, paddr_t*) override
87         // Calls a Locked method of the parent, which confuses analysis.
88         TA_NO_THREAD_SAFETY_ANALYSIS;
89 
90     zx_status_t CloneCOW(bool resizable, uint64_t offset, uint64_t size, bool copy_name,
91                          fbl::RefPtr<VmObject>* clone_vmo) override
92         // Calls a Locked method of the child, which confuses analysis.
93         TA_NO_THREAD_SAFETY_ANALYSIS;
94 
95     void RangeChangeUpdateFromParentLocked(uint64_t offset, uint64_t len) override
96         // Called under the parent's lock, which confuses analysis.
97         TA_NO_THREAD_SAFETY_ANALYSIS;
98 
99     uint32_t GetMappingCachePolicy() const override;
100     zx_status_t SetMappingCachePolicy(const uint32_t cache_policy) override;
101 
102     // maximum size of a VMO is one page less than the full 64bit range
103     static const uint64_t MAX_SIZE = ROUNDDOWN(UINT64_MAX, PAGE_SIZE);
104 
105 private:
106     // private constructor (use Create())
107     VmObjectPaged(
108         uint32_t options, uint32_t pmm_alloc_flags, uint64_t size,
109         fbl::RefPtr<VmObject> parent, fbl::RefPtr<PageSource> page_source);
110 
111     // private destructor, only called from refptr
112     ~VmObjectPaged() override;
113     friend fbl::RefPtr<VmObjectPaged>;
114 
115     DISALLOW_COPY_ASSIGN_AND_MOVE(VmObjectPaged);
116 
117     // perform a cache maintenance operation against the vmo.
118     enum class CacheOpType { Invalidate,
119                              Clean,
120                              CleanInvalidate,
121                              Sync
122     };
123     zx_status_t CacheOp(const uint64_t offset, const uint64_t len, const CacheOpType type);
124 
125     // add a page to the object
126     zx_status_t AddPage(vm_page_t* p, uint64_t offset);
127     zx_status_t AddPageLocked(vm_page_t* p, uint64_t offset) TA_REQ(lock_);
128 
129     // internal page list routine
130     void AddPageToArray(size_t index, vm_page_t* p);
131 
132     zx_status_t PinLocked(uint64_t offset, uint64_t len) TA_REQ(lock_);
133     void UnpinLocked(uint64_t offset, uint64_t len) TA_REQ(lock_);
134 
135     // internal check if any pages in a range are pinned
136     bool AnyPagesPinnedLocked(uint64_t offset, size_t len) TA_REQ(lock_);
137 
138     // internal read/write routine that takes a templated copy function to help share some code
139     template <typename T>
140     zx_status_t ReadWriteInternal(uint64_t offset, size_t len, bool write, T copyfunc);
141 
142     // set our offset within our parent
143     zx_status_t SetParentOffsetLocked(uint64_t o) TA_REQ(lock_);
144 
145     // members
146     const uint32_t options_;
147     uint64_t size_ TA_GUARDED(lock_) = 0;
148     uint64_t parent_offset_ TA_GUARDED(lock_) = 0;
149     uint32_t pmm_alloc_flags_ TA_GUARDED(lock_) = PMM_ALLOC_FLAG_ANY;
150     uint32_t cache_policy_ TA_GUARDED(lock_) = ARCH_MMU_FLAG_CACHED;
151 
152     // The page source, if any.
153     const fbl::RefPtr<PageSource> page_source_;
154 
155     // a tree of pages
156     VmPageList page_list_ TA_GUARDED(lock_);
157 };
158