xref: /linux/arch/arm64/kvm/hyp/nvhe/trace.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1680a04c3SVincent Donnefort // SPDX-License-Identifier: GPL-2.0-only
2680a04c3SVincent Donnefort /*
3680a04c3SVincent Donnefort  * Copyright (C) 2025 Google LLC
4680a04c3SVincent Donnefort  * Author: Vincent Donnefort <vdonnefort@google.com>
5680a04c3SVincent Donnefort  */
6680a04c3SVincent Donnefort 
7680a04c3SVincent Donnefort #include <nvhe/clock.h>
8680a04c3SVincent Donnefort #include <nvhe/mem_protect.h>
9680a04c3SVincent Donnefort #include <nvhe/mm.h>
10680a04c3SVincent Donnefort #include <nvhe/trace.h>
11680a04c3SVincent Donnefort 
12680a04c3SVincent Donnefort #include <asm/percpu.h>
13680a04c3SVincent Donnefort #include <asm/kvm_mmu.h>
14680a04c3SVincent Donnefort #include <asm/local.h>
15680a04c3SVincent Donnefort 
16680a04c3SVincent Donnefort #include "simple_ring_buffer.c"
17680a04c3SVincent Donnefort 
18680a04c3SVincent Donnefort static DEFINE_PER_CPU(struct simple_rb_per_cpu, __simple_rbs);
19680a04c3SVincent Donnefort 
20680a04c3SVincent Donnefort static struct hyp_trace_buffer {
21680a04c3SVincent Donnefort 	struct simple_rb_per_cpu __percpu	*simple_rbs;
22680a04c3SVincent Donnefort 	void					*bpages_backing_start;
23680a04c3SVincent Donnefort 	size_t					bpages_backing_size;
24680a04c3SVincent Donnefort 	hyp_spinlock_t				lock;
25680a04c3SVincent Donnefort } trace_buffer = {
26680a04c3SVincent Donnefort 	.simple_rbs = &__simple_rbs,
27680a04c3SVincent Donnefort 	.lock = __HYP_SPIN_LOCK_UNLOCKED,
28680a04c3SVincent Donnefort };
29680a04c3SVincent Donnefort 
30680a04c3SVincent Donnefort static bool hyp_trace_buffer_loaded(struct hyp_trace_buffer *trace_buffer)
31680a04c3SVincent Donnefort {
32680a04c3SVincent Donnefort 	return trace_buffer->bpages_backing_size > 0;
33680a04c3SVincent Donnefort }
34680a04c3SVincent Donnefort 
35680a04c3SVincent Donnefort void *tracing_reserve_entry(unsigned long length)
36680a04c3SVincent Donnefort {
37680a04c3SVincent Donnefort 	return simple_ring_buffer_reserve(this_cpu_ptr(trace_buffer.simple_rbs), length,
38680a04c3SVincent Donnefort 					  trace_clock());
39680a04c3SVincent Donnefort }
40680a04c3SVincent Donnefort 
41680a04c3SVincent Donnefort void tracing_commit_entry(void)
42680a04c3SVincent Donnefort {
43680a04c3SVincent Donnefort 	simple_ring_buffer_commit(this_cpu_ptr(trace_buffer.simple_rbs));
44680a04c3SVincent Donnefort }
45680a04c3SVincent Donnefort 
46680a04c3SVincent Donnefort static int __admit_host_mem(void *start, u64 size)
47680a04c3SVincent Donnefort {
48680a04c3SVincent Donnefort 	if (!PAGE_ALIGNED(start) || !PAGE_ALIGNED(size) || !size)
49680a04c3SVincent Donnefort 		return -EINVAL;
50680a04c3SVincent Donnefort 
51680a04c3SVincent Donnefort 	if (!is_protected_kvm_enabled())
52680a04c3SVincent Donnefort 		return 0;
53680a04c3SVincent Donnefort 
54680a04c3SVincent Donnefort 	return __pkvm_host_donate_hyp(hyp_virt_to_pfn(start), size >> PAGE_SHIFT);
55680a04c3SVincent Donnefort }
56680a04c3SVincent Donnefort 
57680a04c3SVincent Donnefort static void __release_host_mem(void *start, u64 size)
58680a04c3SVincent Donnefort {
59680a04c3SVincent Donnefort 	if (!is_protected_kvm_enabled())
60680a04c3SVincent Donnefort 		return;
61680a04c3SVincent Donnefort 
62680a04c3SVincent Donnefort 	WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(start), size >> PAGE_SHIFT));
63680a04c3SVincent Donnefort }
64680a04c3SVincent Donnefort 
65680a04c3SVincent Donnefort static int hyp_trace_buffer_load_bpage_backing(struct hyp_trace_buffer *trace_buffer,
66680a04c3SVincent Donnefort 					       struct hyp_trace_desc *desc)
67680a04c3SVincent Donnefort {
68680a04c3SVincent Donnefort 	void *start = (void *)kern_hyp_va(desc->bpages_backing_start);
69680a04c3SVincent Donnefort 	size_t size = desc->bpages_backing_size;
70680a04c3SVincent Donnefort 	int ret;
71680a04c3SVincent Donnefort 
72680a04c3SVincent Donnefort 	ret = __admit_host_mem(start, size);
73680a04c3SVincent Donnefort 	if (ret)
74680a04c3SVincent Donnefort 		return ret;
75680a04c3SVincent Donnefort 
76680a04c3SVincent Donnefort 	memset(start, 0, size);
77680a04c3SVincent Donnefort 
78680a04c3SVincent Donnefort 	trace_buffer->bpages_backing_start = start;
79680a04c3SVincent Donnefort 	trace_buffer->bpages_backing_size = size;
80680a04c3SVincent Donnefort 
81680a04c3SVincent Donnefort 	return 0;
82680a04c3SVincent Donnefort }
83680a04c3SVincent Donnefort 
84680a04c3SVincent Donnefort static void hyp_trace_buffer_unload_bpage_backing(struct hyp_trace_buffer *trace_buffer)
85680a04c3SVincent Donnefort {
86680a04c3SVincent Donnefort 	void *start = trace_buffer->bpages_backing_start;
87680a04c3SVincent Donnefort 	size_t size = trace_buffer->bpages_backing_size;
88680a04c3SVincent Donnefort 
89680a04c3SVincent Donnefort 	if (!size)
90680a04c3SVincent Donnefort 		return;
91680a04c3SVincent Donnefort 
92680a04c3SVincent Donnefort 	memset(start, 0, size);
93680a04c3SVincent Donnefort 
94680a04c3SVincent Donnefort 	__release_host_mem(start, size);
95680a04c3SVincent Donnefort 
96680a04c3SVincent Donnefort 	trace_buffer->bpages_backing_start = 0;
97680a04c3SVincent Donnefort 	trace_buffer->bpages_backing_size = 0;
98680a04c3SVincent Donnefort }
99680a04c3SVincent Donnefort 
100680a04c3SVincent Donnefort static void *__pin_shared_page(unsigned long kern_va)
101680a04c3SVincent Donnefort {
102680a04c3SVincent Donnefort 	void *va = kern_hyp_va((void *)kern_va);
103680a04c3SVincent Donnefort 
104680a04c3SVincent Donnefort 	if (!is_protected_kvm_enabled())
105680a04c3SVincent Donnefort 		return va;
106680a04c3SVincent Donnefort 
107680a04c3SVincent Donnefort 	return hyp_pin_shared_mem(va, va + PAGE_SIZE) ? NULL : va;
108680a04c3SVincent Donnefort }
109680a04c3SVincent Donnefort 
110680a04c3SVincent Donnefort static void __unpin_shared_page(void *va)
111680a04c3SVincent Donnefort {
112680a04c3SVincent Donnefort 	if (!is_protected_kvm_enabled())
113680a04c3SVincent Donnefort 		return;
114680a04c3SVincent Donnefort 
115680a04c3SVincent Donnefort 	hyp_unpin_shared_mem(va, va + PAGE_SIZE);
116680a04c3SVincent Donnefort }
117680a04c3SVincent Donnefort 
118680a04c3SVincent Donnefort static void hyp_trace_buffer_unload(struct hyp_trace_buffer *trace_buffer)
119680a04c3SVincent Donnefort {
120680a04c3SVincent Donnefort 	int cpu;
121680a04c3SVincent Donnefort 
122680a04c3SVincent Donnefort 	hyp_assert_lock_held(&trace_buffer->lock);
123680a04c3SVincent Donnefort 
124680a04c3SVincent Donnefort 	if (!hyp_trace_buffer_loaded(trace_buffer))
125680a04c3SVincent Donnefort 		return;
126680a04c3SVincent Donnefort 
127680a04c3SVincent Donnefort 	for (cpu = 0; cpu < hyp_nr_cpus; cpu++)
128680a04c3SVincent Donnefort 		simple_ring_buffer_unload_mm(per_cpu_ptr(trace_buffer->simple_rbs, cpu),
129680a04c3SVincent Donnefort 					     __unpin_shared_page);
130680a04c3SVincent Donnefort 
131680a04c3SVincent Donnefort 	hyp_trace_buffer_unload_bpage_backing(trace_buffer);
132680a04c3SVincent Donnefort }
133680a04c3SVincent Donnefort 
134680a04c3SVincent Donnefort static int hyp_trace_buffer_load(struct hyp_trace_buffer *trace_buffer,
135680a04c3SVincent Donnefort 				 struct hyp_trace_desc *desc)
136680a04c3SVincent Donnefort {
137680a04c3SVincent Donnefort 	struct simple_buffer_page *bpages;
138680a04c3SVincent Donnefort 	struct ring_buffer_desc *rb_desc;
139680a04c3SVincent Donnefort 	int ret, cpu;
140680a04c3SVincent Donnefort 
141680a04c3SVincent Donnefort 	hyp_assert_lock_held(&trace_buffer->lock);
142680a04c3SVincent Donnefort 
143680a04c3SVincent Donnefort 	if (hyp_trace_buffer_loaded(trace_buffer))
144680a04c3SVincent Donnefort 		return -EINVAL;
145680a04c3SVincent Donnefort 
146680a04c3SVincent Donnefort 	ret = hyp_trace_buffer_load_bpage_backing(trace_buffer, desc);
147680a04c3SVincent Donnefort 	if (ret)
148680a04c3SVincent Donnefort 		return ret;
149680a04c3SVincent Donnefort 
150680a04c3SVincent Donnefort 	bpages = trace_buffer->bpages_backing_start;
151680a04c3SVincent Donnefort 	for_each_ring_buffer_desc(rb_desc, cpu, &desc->trace_buffer_desc) {
152680a04c3SVincent Donnefort 		ret = simple_ring_buffer_init_mm(per_cpu_ptr(trace_buffer->simple_rbs, cpu),
153680a04c3SVincent Donnefort 						 bpages, rb_desc, __pin_shared_page,
154680a04c3SVincent Donnefort 						__unpin_shared_page);
155680a04c3SVincent Donnefort 		if (ret)
156680a04c3SVincent Donnefort 			break;
157680a04c3SVincent Donnefort 
158680a04c3SVincent Donnefort 		bpages += rb_desc->nr_page_va;
159680a04c3SVincent Donnefort 	}
160680a04c3SVincent Donnefort 
161680a04c3SVincent Donnefort 	if (ret)
162680a04c3SVincent Donnefort 		hyp_trace_buffer_unload(trace_buffer);
163680a04c3SVincent Donnefort 
164680a04c3SVincent Donnefort 	return ret;
165680a04c3SVincent Donnefort }
166680a04c3SVincent Donnefort 
167680a04c3SVincent Donnefort static bool hyp_trace_desc_validate(struct hyp_trace_desc *desc, size_t desc_size)
168680a04c3SVincent Donnefort {
169680a04c3SVincent Donnefort 	struct ring_buffer_desc *rb_desc;
170680a04c3SVincent Donnefort 	unsigned int cpu;
171680a04c3SVincent Donnefort 	size_t nr_bpages;
172680a04c3SVincent Donnefort 	void *desc_end;
173680a04c3SVincent Donnefort 
174680a04c3SVincent Donnefort 	/*
175680a04c3SVincent Donnefort 	 * Both desc_size and bpages_backing_size are untrusted host-provided
176680a04c3SVincent Donnefort 	 * values. We rely on __pkvm_host_donate_hyp() to enforce their validity.
177680a04c3SVincent Donnefort 	 */
178680a04c3SVincent Donnefort 	desc_end = (void *)desc + desc_size;
179680a04c3SVincent Donnefort 	nr_bpages = desc->bpages_backing_size / sizeof(struct simple_buffer_page);
180680a04c3SVincent Donnefort 
181680a04c3SVincent Donnefort 	for_each_ring_buffer_desc(rb_desc, cpu, &desc->trace_buffer_desc) {
182680a04c3SVincent Donnefort 		/* Can we read nr_page_va? */
183680a04c3SVincent Donnefort 		if ((void *)rb_desc + struct_size(rb_desc, page_va, 0) > desc_end)
184680a04c3SVincent Donnefort 			return false;
185680a04c3SVincent Donnefort 
186680a04c3SVincent Donnefort 		/* Overflow desc? */
187680a04c3SVincent Donnefort 		if ((void *)rb_desc + struct_size(rb_desc, page_va, rb_desc->nr_page_va) > desc_end)
188680a04c3SVincent Donnefort 			return false;
189680a04c3SVincent Donnefort 
190680a04c3SVincent Donnefort 		/* Overflow bpages backing memory? */
191680a04c3SVincent Donnefort 		if (nr_bpages < rb_desc->nr_page_va)
192680a04c3SVincent Donnefort 			return false;
193680a04c3SVincent Donnefort 
194680a04c3SVincent Donnefort 		if (cpu >= hyp_nr_cpus)
195680a04c3SVincent Donnefort 			return false;
196680a04c3SVincent Donnefort 
197680a04c3SVincent Donnefort 		if (cpu != rb_desc->cpu)
198680a04c3SVincent Donnefort 			return false;
199680a04c3SVincent Donnefort 
200680a04c3SVincent Donnefort 		nr_bpages -= rb_desc->nr_page_va;
201680a04c3SVincent Donnefort 	}
202680a04c3SVincent Donnefort 
203680a04c3SVincent Donnefort 	return true;
204680a04c3SVincent Donnefort }
205680a04c3SVincent Donnefort 
206680a04c3SVincent Donnefort int __tracing_load(unsigned long desc_hva, size_t desc_size)
207680a04c3SVincent Donnefort {
208680a04c3SVincent Donnefort 	struct hyp_trace_desc *desc = (struct hyp_trace_desc *)kern_hyp_va(desc_hva);
209680a04c3SVincent Donnefort 	int ret;
210680a04c3SVincent Donnefort 
211680a04c3SVincent Donnefort 	ret = __admit_host_mem(desc, desc_size);
212680a04c3SVincent Donnefort 	if (ret)
213680a04c3SVincent Donnefort 		return ret;
214680a04c3SVincent Donnefort 
215680a04c3SVincent Donnefort 	if (!hyp_trace_desc_validate(desc, desc_size))
216680a04c3SVincent Donnefort 		goto err_release_desc;
217680a04c3SVincent Donnefort 
218680a04c3SVincent Donnefort 	hyp_spin_lock(&trace_buffer.lock);
219680a04c3SVincent Donnefort 
220680a04c3SVincent Donnefort 	ret = hyp_trace_buffer_load(&trace_buffer, desc);
221680a04c3SVincent Donnefort 
222680a04c3SVincent Donnefort 	hyp_spin_unlock(&trace_buffer.lock);
223680a04c3SVincent Donnefort 
224680a04c3SVincent Donnefort err_release_desc:
225680a04c3SVincent Donnefort 	__release_host_mem(desc, desc_size);
226680a04c3SVincent Donnefort 	return ret;
227680a04c3SVincent Donnefort }
228680a04c3SVincent Donnefort 
229680a04c3SVincent Donnefort void __tracing_unload(void)
230680a04c3SVincent Donnefort {
231680a04c3SVincent Donnefort 	hyp_spin_lock(&trace_buffer.lock);
232680a04c3SVincent Donnefort 	hyp_trace_buffer_unload(&trace_buffer);
233680a04c3SVincent Donnefort 	hyp_spin_unlock(&trace_buffer.lock);
234680a04c3SVincent Donnefort }
235680a04c3SVincent Donnefort 
236680a04c3SVincent Donnefort int __tracing_enable(bool enable)
237680a04c3SVincent Donnefort {
238680a04c3SVincent Donnefort 	int cpu, ret = enable ? -EINVAL : 0;
239680a04c3SVincent Donnefort 
240680a04c3SVincent Donnefort 	hyp_spin_lock(&trace_buffer.lock);
241680a04c3SVincent Donnefort 
242680a04c3SVincent Donnefort 	if (!hyp_trace_buffer_loaded(&trace_buffer))
243680a04c3SVincent Donnefort 		goto unlock;
244680a04c3SVincent Donnefort 
245680a04c3SVincent Donnefort 	for (cpu = 0; cpu < hyp_nr_cpus; cpu++)
246680a04c3SVincent Donnefort 		simple_ring_buffer_enable_tracing(per_cpu_ptr(trace_buffer.simple_rbs, cpu),
247680a04c3SVincent Donnefort 						  enable);
248680a04c3SVincent Donnefort 
249680a04c3SVincent Donnefort 	ret = 0;
250680a04c3SVincent Donnefort 
251680a04c3SVincent Donnefort unlock:
252680a04c3SVincent Donnefort 	hyp_spin_unlock(&trace_buffer.lock);
253680a04c3SVincent Donnefort 
254680a04c3SVincent Donnefort 	return ret;
255680a04c3SVincent Donnefort }
256680a04c3SVincent Donnefort 
257680a04c3SVincent Donnefort int __tracing_swap_reader(unsigned int cpu)
258680a04c3SVincent Donnefort {
259680a04c3SVincent Donnefort 	int ret = -ENODEV;
260680a04c3SVincent Donnefort 
261680a04c3SVincent Donnefort 	if (cpu >= hyp_nr_cpus)
262680a04c3SVincent Donnefort 		return -EINVAL;
263680a04c3SVincent Donnefort 
264680a04c3SVincent Donnefort 	hyp_spin_lock(&trace_buffer.lock);
265680a04c3SVincent Donnefort 
266680a04c3SVincent Donnefort 	if (hyp_trace_buffer_loaded(&trace_buffer))
267680a04c3SVincent Donnefort 		ret = simple_ring_buffer_swap_reader_page(
268680a04c3SVincent Donnefort 				per_cpu_ptr(trace_buffer.simple_rbs, cpu));
269680a04c3SVincent Donnefort 
270680a04c3SVincent Donnefort 	hyp_spin_unlock(&trace_buffer.lock);
271680a04c3SVincent Donnefort 
272680a04c3SVincent Donnefort 	return ret;
273680a04c3SVincent Donnefort }
274b2288891SVincent Donnefort 
275b2288891SVincent Donnefort void __tracing_update_clock(u32 mult, u32 shift, u64 epoch_ns, u64 epoch_cyc)
276b2288891SVincent Donnefort {
277b2288891SVincent Donnefort 	int cpu;
278b2288891SVincent Donnefort 
279b2288891SVincent Donnefort 	/* After this loop, all CPUs are observing the new bank... */
280b2288891SVincent Donnefort 	for (cpu = 0; cpu < hyp_nr_cpus; cpu++) {
281b2288891SVincent Donnefort 		struct simple_rb_per_cpu *simple_rb = per_cpu_ptr(trace_buffer.simple_rbs, cpu);
282b2288891SVincent Donnefort 
283b2288891SVincent Donnefort 		while (READ_ONCE(simple_rb->status) == SIMPLE_RB_WRITING)
284b2288891SVincent Donnefort 			;
285b2288891SVincent Donnefort 	}
286b2288891SVincent Donnefort 
287b2288891SVincent Donnefort 	/* ...we can now override the old one and swap. */
288b2288891SVincent Donnefort 	trace_clock_update(mult, shift, epoch_ns, epoch_cyc);
289b2288891SVincent Donnefort }
290*2194d317SVincent Donnefort 
291*2194d317SVincent Donnefort int __tracing_reset(unsigned int cpu)
292*2194d317SVincent Donnefort {
293*2194d317SVincent Donnefort 	int ret = -ENODEV;
294*2194d317SVincent Donnefort 
295*2194d317SVincent Donnefort 	if (cpu >= hyp_nr_cpus)
296*2194d317SVincent Donnefort 		return -EINVAL;
297*2194d317SVincent Donnefort 
298*2194d317SVincent Donnefort 	hyp_spin_lock(&trace_buffer.lock);
299*2194d317SVincent Donnefort 
300*2194d317SVincent Donnefort 	if (hyp_trace_buffer_loaded(&trace_buffer))
301*2194d317SVincent Donnefort 		ret = simple_ring_buffer_reset(per_cpu_ptr(trace_buffer.simple_rbs, cpu));
302*2194d317SVincent Donnefort 
303*2194d317SVincent Donnefort 	hyp_spin_unlock(&trace_buffer.lock);
304*2194d317SVincent Donnefort 
305*2194d317SVincent Donnefort 	return ret;
306*2194d317SVincent Donnefort }
307