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