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