123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2023 - Google LLC
- * Author: Will Deacon <[email protected]>
- */
- #include <asm/kvm_host.h>
- #include <asm/pgtable.h>
- #include <linux/init.h>
- #include <linux/jump_label.h>
- #include <linux/memblock.h>
- DEFINE_STATIC_KEY_FALSE(pkvm_force_nc);
- static int __init early_pkvm_force_nc_cfg(char *arg)
- {
- static_branch_enable(&pkvm_force_nc);
- return 0;
- }
- early_param("kvm-arm.force_nc", early_pkvm_force_nc_cfg);
- /*
- * Update the stage-2 memory attributes (cacheability) for a page, usually
- * in response to mapping or unmapping a normal non-cacheable region at stage-1.
- *
- * If 'force_nc' is set, the stage-2 entry is immediately made non-cacheable
- * (and cleaned+invalidated to the PoC) otherwise the entry is unmapped and the
- * cacheability determined based on the stage-1 attribute of the next access
- * (with no cache maintenance being performed).
- */
- struct pkvm_host_nc_region {
- phys_addr_t start;
- phys_addr_t end;
- };
- #define PKVM_HOST_MAX_EARLY_NC_REGIONS 8
- static struct pkvm_host_nc_region
- pkvm_host_early_nc_regions[PKVM_HOST_MAX_EARLY_NC_REGIONS];
- static void pkvm_host_track_early_nc_mapping(phys_addr_t addr)
- {
- static int idx /*= 0*/;
- struct pkvm_host_nc_region *reg = &pkvm_host_early_nc_regions[idx];
- if (reg->start == reg->end) {
- reg->start = addr;
- } else if (reg->end != addr) {
- if (WARN_ON(idx == PKVM_HOST_MAX_EARLY_NC_REGIONS - 1))
- return;
- reg = &pkvm_host_early_nc_regions[++idx];
- reg->start = addr;
- }
- reg->end = addr + PAGE_SIZE;
- }
- void pkvm_host_set_stage2_memattr(phys_addr_t addr, bool force_nc)
- {
- int err;
- if (kvm_get_mode() != KVM_MODE_PROTECTED)
- return;
- /*
- * Non-memory regions or carveouts marked as "no-map" are handled
- * entirely by their corresponding driver, which should avoid the
- * creation of a cacheable alias in the first place.
- */
- if (!memblock_is_map_memory(addr))
- return;
- if (!is_pkvm_initialized()) {
- if (!WARN_ON_ONCE(!force_nc))
- pkvm_host_track_early_nc_mapping(addr);
- return;
- }
- err = kvm_call_hyp_nvhe(__pkvm_host_set_stage2_memattr, addr, force_nc);
- WARN_ON(err && err != -EAGAIN);
- }
- EXPORT_SYMBOL_GPL(pkvm_host_set_stage2_memattr);
- int __init pkvm_register_early_nc_mappings(void)
- {
- int i;
- if (!is_pkvm_initialized())
- return 0;
- for (i = 0; i < PKVM_HOST_MAX_EARLY_NC_REGIONS; ++i) {
- struct pkvm_host_nc_region *reg = &pkvm_host_early_nc_regions[i];
- if (reg->start == reg->end)
- return 0;
- while (reg->start != reg->end) {
- int err;
- err = kvm_call_hyp_nvhe(__pkvm_host_set_stage2_memattr, reg->start, true);
- if (err)
- return err;
- reg->start += PAGE_SIZE;
- }
- }
- return 0;
- }
|