blob: ec73c49cdccb87fc0685d46b79426d5d6df1c824 [file] [log] [blame]
// 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_RANGE_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)