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 #include <object/resource_dispatcher.h>
8 
9 #include <fbl/alloc_checker.h>
10 #include <inttypes.h>
11 #include <kernel/auto_lock.h>
12 #include <kernel/range_check.h>
13 #include <lib/counters.h>
14 #include <pretty/sizes.h>
15 #include <string.h>
16 #include <trace.h>
17 #include <vm/vm.h>
18 #include <zircon/rights.h>
19 #include <zircon/syscalls/resource.h>
20 
21 #define LOCAL_TRACE 0
22 
23 KCOUNTER(root_resource_created, "resource.root.created");
24 KCOUNTER(hypervisor_resource_created, "resource.hypervisor.created");
25 KCOUNTER(vmex_resource_created, "resource.vmex.created");
26 KCOUNTER(mmio_resource_created, "resource.mmio.created");
27 KCOUNTER(irq_resource_created, "resource.irq.created");
28 KCOUNTER(ioport_resource_created, "resource.ioport.created");
29 KCOUNTER(smc_resource_created, "resource.smc.created");
30 
31 // Storage for static members of ResourceDispatcher
32 RegionAllocator ResourceDispatcher::static_rallocs_[ZX_RSRC_KIND_COUNT];
33 ResourceDispatcher::ResourceList ResourceDispatcher::static_resource_list_;
34 RegionAllocator::RegionPool::RefPtr ResourceDispatcher::region_pool_;
35 const char* kLogTag = "Resources:";
36 
Create(fbl::RefPtr<ResourceDispatcher> * dispatcher,zx_rights_t * rights,uint32_t kind,uint64_t base,size_t size,uint32_t flags,const char name[ZX_MAX_NAME_LEN],RegionAllocator rallocs[ZX_RSRC_KIND_COUNT],ResourceList * resource_list)37 zx_status_t ResourceDispatcher::Create(fbl::RefPtr<ResourceDispatcher>* dispatcher,
38                                        zx_rights_t* rights,
39                                        uint32_t kind,
40                                        uint64_t base,
41                                        size_t size,
42                                        uint32_t flags,
43                                        const char name[ZX_MAX_NAME_LEN],
44                                        RegionAllocator rallocs[ZX_RSRC_KIND_COUNT],
45                                        ResourceList* resource_list) {
46     Guard<fbl::Mutex> guard{ResourcesLock::Get()};
47     if (kind >= ZX_RSRC_KIND_COUNT || (flags & ZX_RSRC_FLAGS_MASK) != flags) {
48         return ZX_ERR_INVALID_ARGS;
49     }
50 
51     // The first thing we need to do for any resource is ensure that it has not
52     // been exclusively reserved. If GetRegion succeeds and we have a region
53     // uptr then in the case of an exclusive resource we'll move it into the
54     // class instance. Otherwise, the resource is shared and we'll release it
55     // back to the allocator since we only used it to verify it existed in the
56     // allocator.
57     //
58     // TODO: Hypervisor resources should be represented in some other capability
59     // object because they represent a binary permission rather than anything
60     // more finely grained. It will work properly here because the base/size of a
61     // hypervisor resource is never checked, but it's a workaround until a
62     // proper capability exists for it.
63     zx_status_t status;
64     RegionAllocator::Region::UPtr region_uptr = nullptr;
65     switch (kind) {
66     case ZX_RSRC_KIND_ROOT:
67     case ZX_RSRC_KIND_HYPERVISOR:
68     case ZX_RSRC_KIND_VMEX:
69         // It does not make sense for an abstract resource type to have a base/size tuple
70         if (base || size) {
71             return ZX_ERR_INVALID_ARGS;
72         }
73         break;
74     default:
75         status = rallocs[kind].GetRegion({.base = base, .size = size}, region_uptr);
76         if (status != ZX_OK) {
77             LTRACEF("%s couldn't pull the resource out of the ralloc %d\n", kLogTag, status);
78             return status;
79         }
80     }
81 
82     // If the allocation is exclusive then a check needs to be made to ensure
83     // that no shared allocation already exists and/or overlaps. Shared
84     // resources don't need to do so because grabbing the exclusive region above
85     // (temporarily) ensures they are valid allocations. If this check fails
86     // then the region above will be released back to the pool anyway.
87     if (flags & ZX_RSRC_FLAG_EXCLUSIVE) {
88         auto callback = [&](const ResourceDispatcher& rsrc) {
89             LTRACEF("%s walking resources, found [%u, %#lx, %zu]\n", kLogTag, rsrc.get_kind(),
90                     rsrc.get_base(), rsrc.get_size());
91             if (kind != rsrc.get_kind()) {
92                 return ZX_OK;
93             }
94 
95             if (Intersects(base, size, rsrc.get_base(), rsrc.get_size())) {
96                 LTRACEF("%s [%#lx, %zu] intersects with [%#lx, %zu] found in list!\n", kLogTag,
97                         base, size, rsrc.get_base(), rsrc.get_size());
98                 return ZX_ERR_NOT_FOUND;
99             }
100 
101             return ZX_OK;
102         };
103         LTRACEF("%s scanning resource list for [%u, %#lx, %zu]\n", kLogTag, kind, base, size);
104         zx_status_t status = ResourceDispatcher::ForEachResourceLocked(callback, resource_list);
105         if (status != ZX_OK) {
106             return status;
107         }
108     }
109 
110     // We've passd the first hurdle, so it's time to construct the dispatcher
111     // itself.  The constructor will handle adding itself to the shared list if
112     // necessary.
113     fbl::AllocChecker ac;
114     auto disp = fbl::AdoptRef(new (&ac) ResourceDispatcher(kind, base, size, flags,
115                                                            ktl::move(region_uptr),
116                                                            rallocs, resource_list));
117     if (!ac.check()) {
118         return ZX_ERR_NO_MEMORY;
119     }
120 
121     if (name != nullptr) {
122         disp->set_name(name, ZX_MAX_NAME_LEN);
123     }
124 
125     *rights = default_rights();
126     *dispatcher = ktl::move(disp);
127 
128     LTRACEF("%s [%u, %#lx, %zu] resource created.\n", kLogTag, kind, base, size);
129     return ZX_OK;
130 }
131 
ResourceDispatcher(uint32_t kind,uint64_t base,uint64_t size,uint32_t flags,RegionAllocator::Region::UPtr && region,RegionAllocator rallocs[ZX_RSRC_KIND_COUNT],ResourceList * resource_list)132 ResourceDispatcher::ResourceDispatcher(uint32_t kind,
133                                        uint64_t base,
134                                        uint64_t size,
135                                        uint32_t flags,
136                                        RegionAllocator::Region::UPtr&& region,
137                                        RegionAllocator rallocs[ZX_RSRC_KIND_COUNT],
138                                        ResourceList* resource_list)
139     : kind_(kind), base_(base), size_(size), flags_(flags),
140       resource_list_(resource_list) {
141     if (flags_ & ZX_RSRC_FLAG_EXCLUSIVE) {
142         exclusive_region_ = ktl::move(region);
143     }
144 
145     switch (kind_) {
146     case ZX_RSRC_KIND_ROOT:
147         kcounter_add(root_resource_created, 1);
148         break;
149     case ZX_RSRC_KIND_HYPERVISOR:
150         kcounter_add(hypervisor_resource_created, 1);
151         break;
152     case ZX_RSRC_KIND_VMEX:
153         kcounter_add(vmex_resource_created, 1);
154         break;
155     case ZX_RSRC_KIND_MMIO:
156         kcounter_add(mmio_resource_created, 1);
157         break;
158     case ZX_RSRC_KIND_IRQ:
159         kcounter_add(irq_resource_created, 1);
160         break;
161     case ZX_RSRC_KIND_IOPORT:
162         kcounter_add(ioport_resource_created, 1);
163         break;
164     case ZX_RSRC_KIND_SMC:
165         kcounter_add(smc_resource_created, 1);
166         break;
167     }
168     resource_list_->push_back(this);
169 }
170 
~ResourceDispatcher()171 ResourceDispatcher::~ResourceDispatcher() {
172     // exclusive allocations will be released when the uptr goes out of scope,
173     // shared need to be removed from |all_shared_list_|
174     Guard<fbl::Mutex> guard{ResourcesLock::Get()};
175     char name[ZX_MAX_NAME_LEN];
176     get_name(name);
177     resource_list_->erase(*this);
178 }
179 
InitializeAllocator(uint32_t kind,uint64_t base,size_t size,RegionAllocator rallocs[ZX_RSRC_KIND_COUNT])180 zx_status_t ResourceDispatcher::InitializeAllocator(uint32_t kind,
181                                                     uint64_t base,
182                                                     size_t size,
183                                                     RegionAllocator rallocs[ZX_RSRC_KIND_COUNT]) {
184     DEBUG_ASSERT(kind < ZX_RSRC_KIND_COUNT);
185     DEBUG_ASSERT(size > 0);
186 
187     Guard<fbl::Mutex> guard{ResourcesLock::Get()};
188     zx_status_t status;
189 
190     // This method should only be called for resource kinds with bookkeeping.
191     if (kind >= ZX_RSRC_KIND_COUNT) {
192         return ZX_ERR_INVALID_ARGS;
193     }
194 
195     // Create the initial region pool if necessary. Its storage is allocated in this cpp file
196     if (region_pool_ == nullptr) {
197         region_pool_ = RegionAllocator::RegionPool::Create(kMaxRegionPoolSize);
198     }
199 
200     // Failure to allocate this early in boot is a critical error
201     DEBUG_ASSERT(region_pool_);
202 
203     status = rallocs[kind].SetRegionPool(region_pool_);
204     if (status != ZX_OK) {
205         return status;
206     }
207 
208     // Add the initial address space specified by the platform to the region allocator.
209     // This will be used for verifying both shared and exclusive allocations of address
210     // space.
211     status = rallocs[kind].AddRegion({.base = base, .size = size});
212     LTRACEF("%s added [%#lx, %zu] to kind %u in allocator %p: %d\n",
213             kLogTag, base, size, kind, rallocs, status);
214     return status;
215 }
216 
217 // Size specifiers for the debug output
218 constexpr int kTypeLen = 10;
219 constexpr int kFlagLen = 6;
220 constexpr int kNameLen = ZX_MAX_NAME_LEN - 1;
221 constexpr int kNumLen = 16;
222 constexpr int kPrettyLen = 8;
223 
224 // Utility function to format the flags into a user-readable string.
flags_to_string(uint32_t flags,char str[kFlagLen])225 static constexpr void flags_to_string(uint32_t flags, char str[kFlagLen]) {
226     str[0] = ' ';
227     str[1] = ' ';
228     str[2] = ' ';
229     str[3] = (flags & ZX_RSRC_FLAG_EXCLUSIVE) ? ' ' : 's';
230     str[4] = (flags & ZX_RSRC_FLAG_EXCLUSIVE) ? 'x' : ' ';
231     str[5] = '\0';
232 }
233 
pad_field(int width)234 static void pad_field(int width) {
235     printf("\t%.*s", width, "                        ");
236 }
237 
Dump()238 void ResourceDispatcher::Dump() {
239     uint32_t kind;
240     auto callback = [&](const ResourceDispatcher& r) -> zx_status_t {
241         char name[ZX_MAX_NAME_LEN];
242         char flag_str[kFlagLen];
243         char pretty_size[kPrettyLen];
244 
245         // exit early so we can print the list in a grouped format
246         // without adding overhead to the list management.
247         if (r.get_kind() != kind) {
248             return ZX_OK;
249         }
250 
251         // A safety check to make sure we don't need to worry about snprintf edge cases
252         r.get_name(name);
253         flags_to_string(r.get_flags(), flag_str);
254 
255         // IRQs are allocated one at a time, so range display doesn't make much sense.
256         switch (r.get_kind()) {
257         case ZX_RSRC_KIND_ROOT:
258             printf("%.*s", kTypeLen, "root");
259             pad_field(kFlagLen); // Root has no flags
260             printf("\t%.*s", kNameLen, name);
261             printf("\n");
262             break;
263         case ZX_RSRC_KIND_HYPERVISOR:
264             printf("%.*s", kTypeLen, "hypervisor");
265             printf("\t%.*s", kFlagLen, flag_str);
266             printf("\t%.*s", kNameLen, name);
267             printf("\n");
268             break;
269         case ZX_RSRC_KIND_IRQ:
270             printf("%.*s", kTypeLen, "irq");
271             printf("\t%.*s", kFlagLen, flag_str);
272             printf("\t%.*s", kNameLen, name);
273             printf("\t%#.*" PRIxPTR, kNumLen, r.get_base());
274             printf("\n");
275             break;
276         case ZX_RSRC_KIND_IOPORT:
277             printf("%.*s", kTypeLen, "io");
278             printf("\t%.*s", kFlagLen, flag_str);
279             printf("\t%.*s", kNameLen, name);
280             printf("\t%#.*" PRIxPTR, kNumLen, r.get_base());
281             printf("\t%#.*" PRIxPTR, kNumLen, r.get_base() + r.get_size());
282             printf("\t%.*s", kPrettyLen,
283                    format_size(pretty_size, sizeof(pretty_size), r.get_size()));
284             printf("\n");
285             break;
286         case ZX_RSRC_KIND_MMIO:
287             printf("%.*s", kTypeLen, "mmio");
288             printf("\t%.*s", kFlagLen, flag_str);
289             printf("\t%.*s", kNameLen, name);
290             printf("\t%#.*" PRIxPTR, kNumLen, r.get_base());
291             printf("\t%#.*" PRIxPTR, kNumLen, r.get_base() + r.get_size());
292             printf("\t%.*s", kPrettyLen,
293                    format_size(pretty_size, sizeof(pretty_size), r.get_size()));
294             printf("\n");
295             break;
296         case ZX_RSRC_KIND_SMC:
297             printf("%.*s", kTypeLen, "smc");
298             printf("\t%.*s", kFlagLen, flag_str);
299             printf("\t%.*s", kNameLen, name);
300             printf("\t%#.*" PRIxPTR, kNumLen, r.get_base());
301             printf("\t%#.*" PRIxPTR, kNumLen, r.get_base() + r.get_size());
302             printf("\t%.*s", kPrettyLen,
303                    format_size(pretty_size, sizeof(pretty_size), r.get_size()));
304             printf("\n");
305             break;
306         }
307 
308         return ZX_OK;
309     };
310 
311     printf("%10s\t%4s\t%31s\t%16s\t%16s\t%8s\n\n", "type", "flags", "name", "start", "end", "size");
312     for (kind = 0; kind < ZX_RSRC_KIND_COUNT; kind++) {
313         ResourceDispatcher::ForEachResource(callback);
314     }
315 }
316 
317 #include <lib/console.h>
318 
cmd_resources(int argc,const cmd_args * argv,uint32_t flags)319 static int cmd_resources(int argc, const cmd_args* argv, uint32_t flags) {
320     ResourceDispatcher::Dump();
321     return true;
322 }
323 
324 STATIC_COMMAND_START
325 STATIC_COMMAND("resource", "Inspect physical address space resource allocations", &cmd_resources)
326 STATIC_COMMAND_END(resources);
327