xref: /linux/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c (revision fab183d632628381b466a41479489541ac0e29a0)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Support for the hypercall interface exposed to protected guests by
4  * pKVM.
5  *
6  * Author: Will Deacon <will@kernel.org>
7  * Copyright (C) 2024 Google LLC
8  */
9 
10 #include <linux/arm-smccc.h>
11 #include <linux/array_size.h>
12 #include <linux/io.h>
13 #include <linux/mem_encrypt.h>
14 #include <linux/mm.h>
15 #include <linux/pgtable.h>
16 
17 #include <asm/hypervisor.h>
18 
19 static size_t pkvm_granule;
20 DEFINE_STATIC_KEY_FALSE_RO(pkvm_guest);
21 
arm_smccc_do_one_page(u32 func_id,phys_addr_t phys)22 static int arm_smccc_do_one_page(u32 func_id, phys_addr_t phys)
23 {
24 	phys_addr_t end = phys + PAGE_SIZE;
25 
26 	while (phys < end) {
27 		struct arm_smccc_res res;
28 
29 		arm_smccc_1_1_invoke(func_id, phys, 0, 0, &res);
30 		if (res.a0 != SMCCC_RET_SUCCESS)
31 			return -EPERM;
32 
33 		phys += pkvm_granule;
34 	}
35 
36 	return 0;
37 }
38 
__set_memory_range(u32 func_id,unsigned long start,int numpages)39 static int __set_memory_range(u32 func_id, unsigned long start, int numpages)
40 {
41 	void *addr = (void *)start, *end = addr + numpages * PAGE_SIZE;
42 
43 	while (addr < end) {
44 		int err;
45 
46 		err = arm_smccc_do_one_page(func_id, virt_to_phys(addr));
47 		if (err)
48 			return err;
49 
50 		addr += PAGE_SIZE;
51 	}
52 
53 	return 0;
54 }
55 
pkvm_set_memory_encrypted(unsigned long addr,int numpages)56 static int pkvm_set_memory_encrypted(unsigned long addr, int numpages)
57 {
58 	return __set_memory_range(ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID,
59 				  addr, numpages);
60 }
61 
pkvm_set_memory_decrypted(unsigned long addr,int numpages)62 static int pkvm_set_memory_decrypted(unsigned long addr, int numpages)
63 {
64 	return __set_memory_range(ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID,
65 				  addr, numpages);
66 }
67 
68 static const struct arm64_mem_crypt_ops pkvm_crypt_ops = {
69 	.encrypt	= pkvm_set_memory_encrypted,
70 	.decrypt	= pkvm_set_memory_decrypted,
71 };
72 
mmio_guard_ioremap_hook(phys_addr_t phys,size_t size,pgprot_t * prot)73 static int mmio_guard_ioremap_hook(phys_addr_t phys, size_t size,
74 				   pgprot_t *prot)
75 {
76 	phys_addr_t end;
77 	pteval_t protval = pgprot_val(*prot);
78 
79 	/*
80 	 * We only expect MMIO emulation for regions mapped with device
81 	 * attributes.
82 	 */
83 	if (protval != PROT_DEVICE_nGnRE && protval != PROT_DEVICE_nGnRnE)
84 		return 0;
85 
86 	end = PAGE_ALIGN(phys + size);
87 	phys = PAGE_ALIGN_DOWN(phys);
88 
89 	while (phys < end) {
90 		const int func_id = ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_FUNC_ID;
91 
92 		WARN_ON_ONCE(arm_smccc_do_one_page(func_id, phys));
93 		phys += PAGE_SIZE;
94 	}
95 
96 	return 0;
97 }
98 
pkvm_init_hyp_services(void)99 void pkvm_init_hyp_services(void)
100 {
101 	int i;
102 	struct arm_smccc_res res;
103 	const u32 funcs[] = {
104 		ARM_SMCCC_KVM_FUNC_HYP_MEMINFO,
105 		ARM_SMCCC_KVM_FUNC_MEM_SHARE,
106 		ARM_SMCCC_KVM_FUNC_MEM_UNSHARE,
107 	};
108 
109 	for (i = 0; i < ARRAY_SIZE(funcs); ++i) {
110 		if (!kvm_arm_hyp_service_available(funcs[i]))
111 			return;
112 	}
113 
114 	arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_HYP_MEMINFO_FUNC_ID,
115 			     0, 0, 0, &res);
116 	if (res.a0 > PAGE_SIZE) /* Includes error codes */
117 		return;
118 
119 	pkvm_granule = res.a0;
120 	arm64_mem_crypt_ops_register(&pkvm_crypt_ops);
121 
122 	if (kvm_arm_hyp_service_available(ARM_SMCCC_KVM_FUNC_MMIO_GUARD))
123 		arm64_ioremap_prot_hook_register(&mmio_guard_ioremap_hook);
124 
125 	static_branch_enable(&pkvm_guest);
126 }
127