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