]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drivers/virt: pkvm: Intercept ioremap using pKVM MMIO_GUARD hypercall
authorWill Deacon <will@kernel.org>
Fri, 30 Aug 2024 13:01:49 +0000 (14:01 +0100)
committerWill Deacon <will@kernel.org>
Fri, 30 Aug 2024 15:30:41 +0000 (16:30 +0100)
Hook up pKVM's MMIO_GUARD hypercall so that ioremap() and friends will
register the target physical address as MMIO with the hypervisor,
allowing guest exits to that page to be emulated by the host with full
syndrome information.

Acked-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20240830130150.8568-7-will@kernel.org
Signed-off-by: Will Deacon <will@kernel.org>
Documentation/virt/kvm/arm/hypercalls.rst
drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
include/linux/arm-smccc.h

index c42580e71bf8be9f31645f948da78a437ef4b993..af7bc2c2e0cb41c4919ec5f50d2acfe8ebea9ebd 100644 (file)
@@ -116,3 +116,29 @@ memory protection granule advertised by ``ARM_SMCCC_KVM_FUNC_HYP_MEMINFO``.
 |                     |          |    +---------------------------------------------+
 |                     |          |    | ``INVALID_PARAMETER (-3)``                  |
 +---------------------+----------+----+---------------------------------------------+
+
+``ARM_SMCCC_KVM_FUNC_MMIO_GUARD``
+----------------------------------
+
+Request that a given memory region is handled as MMIO by the hypervisor,
+allowing accesses to this region to be emulated by the KVM host. The size of the
+region is equal to the memory protection granule advertised by
+``ARM_SMCCC_KVM_FUNC_HYP_MEMINFO``.
+
++---------------------+-------------------------------------------------------------+
+| Presence:           | Optional; pKVM protected guests only.                       |
++---------------------+-------------------------------------------------------------+
+| Calling convention: | HVC64                                                       |
++---------------------+----------+--------------------------------------------------+
+| Function ID:        | (uint32) | 0xC6000007                                       |
++---------------------+----------+----+---------------------------------------------+
+| Arguments:          | (uint64) | R1 | Base IPA of MMIO memory region              |
+|                     +----------+----+---------------------------------------------+
+|                     | (uint64) | R2 | Reserved / Must be zero                     |
+|                     +----------+----+---------------------------------------------+
+|                     | (uint64) | R3 | Reserved / Must be zero                     |
++---------------------+----------+----+---------------------------------------------+
+| Return Values:      | (int64)  | R0 | ``SUCCESS (0)``                             |
+|                     |          |    +---------------------------------------------+
+|                     |          |    | ``INVALID_PARAMETER (-3)``                  |
++---------------------+----------+----+---------------------------------------------+
index 8256cf68fd765f3ec1cc441292fd01a0c06c681b..56a3859dda8a153993367fa34fd4ee95587b8343 100644 (file)
@@ -9,8 +9,10 @@
 
 #include <linux/arm-smccc.h>
 #include <linux/array_size.h>
+#include <linux/io.h>
 #include <linux/mem_encrypt.h>
 #include <linux/mm.h>
+#include <linux/pgtable.h>
 
 #include <asm/hypervisor.h>
 
@@ -67,6 +69,36 @@ static const struct arm64_mem_crypt_ops pkvm_crypt_ops = {
        .decrypt        = pkvm_set_memory_decrypted,
 };
 
+static int mmio_guard_ioremap_hook(phys_addr_t phys, size_t size,
+                                  pgprot_t *prot)
+{
+       phys_addr_t end;
+       pteval_t protval = pgprot_val(*prot);
+
+       /*
+        * We only expect MMIO emulation for regions mapped with device
+        * attributes.
+        */
+       if (protval != PROT_DEVICE_nGnRE && protval != PROT_DEVICE_nGnRnE)
+               return 0;
+
+       phys = PAGE_ALIGN_DOWN(phys);
+       end = phys + PAGE_ALIGN(size);
+
+       while (phys < end) {
+               const int func_id = ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_FUNC_ID;
+               int err;
+
+               err = arm_smccc_do_one_page(func_id, phys);
+               if (err)
+                       return err;
+
+               phys += PAGE_SIZE;
+       }
+
+       return 0;
+}
+
 void pkvm_init_hyp_services(void)
 {
        int i;
@@ -89,4 +121,7 @@ void pkvm_init_hyp_services(void)
 
        pkvm_granule = res.a0;
        arm64_mem_crypt_ops_register(&pkvm_crypt_ops);
+
+       if (kvm_arm_hyp_service_available(ARM_SMCCC_KVM_FUNC_MMIO_GUARD))
+               arm64_ioremap_prot_hook_register(&mmio_guard_ioremap_hook);
 }
index 9cb7c95920b0aa9866fd4d5d59f781a100aca724..e93c1f7cea7068860696b93886a78dd254051e1e 100644 (file)
 #define ARM_SMCCC_KVM_FUNC_HYP_MEMINFO         2
 #define ARM_SMCCC_KVM_FUNC_MEM_SHARE           3
 #define ARM_SMCCC_KVM_FUNC_MEM_UNSHARE         4
+#define ARM_SMCCC_KVM_FUNC_MMIO_GUARD          7
 #define ARM_SMCCC_KVM_FUNC_FEATURES_2          127
 #define ARM_SMCCC_KVM_NUM_FUNCS                        128
 
                           ARM_SMCCC_OWNER_VENDOR_HYP,                  \
                           ARM_SMCCC_KVM_FUNC_MEM_UNSHARE)
 
+#define ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_FUNC_ID                    \
+       ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,                         \
+                          ARM_SMCCC_SMC_64,                            \
+                          ARM_SMCCC_OWNER_VENDOR_HYP,                  \
+                          ARM_SMCCC_KVM_FUNC_MMIO_GUARD)
+
 /* ptp_kvm counter type ID */
 #define KVM_PTP_VIRT_COUNTER                   0
 #define KVM_PTP_PHYS_COUNTER                   1