| // Copyright 2020 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <lib/counters.h> |
| #include <lib/root_resource_filter.h> |
| #include <lib/root_resource_filter_internal.h> |
| #include <stdio.h> |
| #include <trace.h> |
| |
| #include <ktl/algorithm.h> |
| #include <ktl/byte.h> |
| #include <lk/init.h> |
| #include <phys/handoff.h> |
| #include <vm/pmm.h> |
| #include <vm/vm_object_paged.h> |
| #include <vm/vm_object_physical.h> |
| |
| #include <ktl/enforce.h> |
| |
| #define LOCAL_TRACE 0 |
| |
| KCOUNTER(resource_ranges_denied, "resource.denied_ranges") |
| |
| namespace { |
| // The global singleton filter |
| RootResourceFilter g_root_resource_filter; |
| } // namespace |
| |
| void RootResourceFilter::Finalize() { |
| // Add the PMM arenas as regions we may not allocate from. |
| for (size_t i = 0, arena_count = pmm_num_arenas(); i < arena_count; ++i) { |
| pmm_arena_info_t info; |
| |
| // There is no reason for this to ever fail. |
| zx_status_t res = pmm_get_arena_info(1, i, &info, sizeof(info)); |
| ASSERT(res == ZX_OK); |
| |
| // Add the arena to the set of regions to deny, permitting it to merge with |
| // any pre-existing regions already in the set (shouldn't happen, but if it |
| // does, we want the union). If we cannot add the arena to our set of |
| // regions to deny, it can only be because we failed a heap allocation which |
| // should be impossible at this point. If it does happen, panic. We cannot |
| // run if we cannot enforce the deny list. |
| res = mmio_deny_.AddRegion({.base = info.base, .size = info.size}, |
| RegionAllocator::AllowOverlap::Yes); |
| ASSERT(res == ZX_OK); |
| } |
| |
| for (const zbi_mem_range_t& mem_range : gPhysHandoff->mem_config.get()) { |
| if (mem_range.type == ZBI_MEM_TYPE_RESERVED) { |
| mmio_deny_.SubtractRegion({.base = mem_range.paddr, .size = mem_range.length}, |
| RegionAllocator::AllowIncomplete::Yes); |
| } |
| } |
| |
| // Dump the deny list at spew level for debugging purposes. |
| if (DPRINTF_ENABLED_FOR_LEVEL(SPEW)) { |
| dprintf(SPEW, "Final MMIO Deny list is:\n"); |
| mmio_deny_.WalkAvailableRegions([](const ralloc_region_t* region) -> bool { |
| dprintf(SPEW, "Region [0x%lx, 0x%lx)\n", region->base, region->base + region->size); |
| return true; // Keep printing, don't stop now! |
| }); |
| } |
| } |
| |
| bool RootResourceFilter::IsRegionAllowed(uintptr_t base, size_t size, zx_rsrc_kind_t kind) const { |
| // Currently, we only need to track denied mmio regions. Someday, this may |
| // need to expand to other ranges as well (such as x64 IO ports) |
| if (kind != ZX_RSRC_KIND_MMIO) { |
| return true; |
| } |
| |
| return !mmio_deny_.TestRegionIntersects({.base = base, .size = size}, |
| RegionAllocator::TestRegionSet::Available); |
| } |
| |
| void root_resource_filter_add_deny_region(uintptr_t base, size_t size, zx_rsrc_kind_t kind) { |
| // We only enforce deny regions for MMIO right now. In the future, if someone |
| // wants to limit other regions as well (perhaps the I/O port space for x64), |
| // they need to come back here and add another RegionAllocator instance to |
| // enforce the rules for the new zone. |
| ASSERT(kind == ZX_RSRC_KIND_MMIO); |
| g_root_resource_filter.AddDenyRegion(base, size, kind); |
| } |
| |
| bool root_resource_filter_can_access_region(uintptr_t base, size_t size, zx_rsrc_kind_t kind) { |
| // Keep track of the number of regions that we end up denying. Typically, in |
| // a properly operating system (aside from explicit tests) this should be 0. |
| // Anything else probably indicates either malice or a bug somewhere. |
| if (!g_root_resource_filter.IsRegionAllowed(base, size, kind)) { |
| LTRACEF("WARNING - Denying range request [%016lx, %016lx) kind (%u)\n", base, base + size, |
| kind); |
| kcounter_add(resource_ranges_denied, 1); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Finalize the ZBI filter just before we start user mode. This will add the |
| // RAM regions described by the ZBI into the filter, and then subtract out |
| // the reserved RAM regions so that userspace can create MMIO resource ranges |
| // which target reserved RAM. |
| static void finalize_root_resource_filter(uint) { g_root_resource_filter.Finalize(); } |
| LK_INIT_HOOK(finalize_root_resource_filter, finalize_root_resource_filter, LK_INIT_LEVEL_USER - 1) |