KVM/arm64 updates for Linux 5.16 - Simplification of the 'vcpu first run' by integrating it into KVM's 'pid change' flow - Refactoring of the FP and SVE state tracking, also leading to a simpler state and less shared data between EL1 and EL2 in the nVHE case - Tidy up the header file usage for the nvhe hyp object - New HYP unsharing mechanism, finally allowing pages to be unmapped from the Stage-1 EL2 page-tables - Various pKVM cleanups around refcounting and sharing - A couple of vgic fixes for bugs that would trigger once the vcpu xarray rework is merged, but not sooner - Add minimal support for ARMv8.7's PMU extension - Rework kvm_pgtable initialisation ahead of the NV work - New selftest for IRQ injection - Teach selftests about the lack of default IPA space and page sizes - Expand sysreg selftest to deal with Pointer Authentication - The usual bunch of cleanups and doc update
Unified · Split
| @@ -63,6 +63,7 @@ enum __kvm_host_smccc_func { · 62 unmodified lines | |||
| 63 | 63 | ||
| 64 | /* Hypercalls available after pKVM finalisation */ | 64 | /* Hypercalls available after pKVM finalisation */ |
| 65 | __KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp, | 65 | __KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp, |
| 66 | __KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_hyp, | ||
| 66 | __KVM_HOST_SMCCC_FUNC___kvm_adjust_pc, | 67 | __KVM_HOST_SMCCC_FUNC___kvm_adjust_pc, |
| 67 | __KVM_HOST_SMCCC_FUNC___kvm_vcpu_run, | 68 | __KVM_HOST_SMCCC_FUNC___kvm_vcpu_run, |
| 68 | __KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context, | 69 | __KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context, |
| @@ -388,7 +388,7 @@ static inline void kvm_vcpu_set_be(struct kvm_vcpu *vcpu) · 387 unmodified lines | |||
| 388 | *vcpu_cpsr(vcpu) |= PSR_AA32_E_BIT; | 388 | *vcpu_cpsr(vcpu) |= PSR_AA32_E_BIT; |
| 389 | } else { | 389 | } else { |
| 390 | u64 sctlr = vcpu_read_sys_reg(vcpu, SCTLR_EL1); | 390 | u64 sctlr = vcpu_read_sys_reg(vcpu, SCTLR_EL1); |
| 391 | sctlr |= (1 << 25); | 391 | sctlr |= SCTLR_ELx_EE; |
| 392 | vcpu_write_sys_reg(vcpu, sctlr, SCTLR_EL1); | 392 | vcpu_write_sys_reg(vcpu, sctlr, SCTLR_EL1); |
| 393 | } | 393 | } |
| 394 | } | 394 | } |
| @@ -26,7 +26,6 @@ · 25 unmodified lines | |||
| 26 | #include <asm/fpsimd.h> | 26 | #include <asm/fpsimd.h> |
| 27 | #include <asm/kvm.h> | 27 | #include <asm/kvm.h> |
| 28 | #include <asm/kvm_asm.h> | 28 | #include <asm/kvm_asm.h> |
| 29 | #include <asm/thread_info.h> | ||
| 30 | 29 | ||
| 31 | #define __KVM_HAVE_ARCH_INTC_INITIALIZED | 30 | #define __KVM_HAVE_ARCH_INTC_INITIALIZED |
| 32 | 31 | ||
| @@ -298,9 +297,6 @@ struct kvm_vcpu_arch { · 265 unmodified lines | |||
| 298 | /* Exception Information */ | 297 | /* Exception Information */ |
| 299 | struct kvm_vcpu_fault_info fault; | 298 | struct kvm_vcpu_fault_info fault; |
| 300 | 299 | ||
| 301 | /* State of various workarounds, see kvm_asm.h for bit assignment */ | ||
| 302 | u64 workaround_flags; | ||
| 303 | |||
| 304 | /* Miscellaneous vcpu state flags */ | 300 | /* Miscellaneous vcpu state flags */ |
| 305 | u64 flags; | 301 | u64 flags; |
| 306 | 302 | ||
| @@ -321,8 +317,8 @@ struct kvm_vcpu_arch { · 14 unmodified lines | |||
| 321 | struct kvm_guest_debug_arch vcpu_debug_state; | 317 | struct kvm_guest_debug_arch vcpu_debug_state; |
| 322 | struct kvm_guest_debug_arch external_debug_state; | 318 | struct kvm_guest_debug_arch external_debug_state; |
| 323 | 319 | ||
| 324 | struct thread_info *host_thread_info; /* hyp VA */ | ||
| 325 | struct user_fpsimd_state *host_fpsimd_state; /* hyp VA */ | 320 | struct user_fpsimd_state *host_fpsimd_state; /* hyp VA */ |
| 321 | struct task_struct *parent_task; | ||
| 326 | 322 | ||
| 327 | struct { | 323 | struct { |
| 328 | /* {Break,watch}point registers */ | 324 | /* {Break,watch}point registers */ |
| @@ -367,9 +363,6 @@ struct kvm_vcpu_arch { · 38 unmodified lines | |||
| 367 | int target; | 363 | int target; |
| 368 | DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES); | 364 | DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES); |
| 369 | 365 | ||
| 370 | /* Detect first run of a vcpu */ | ||
| 371 | bool has_run_once; | ||
| 372 | |||
| 373 | /* Virtual SError ESR to restore when HCR_EL2.VSE is set */ | 366 | /* Virtual SError ESR to restore when HCR_EL2.VSE is set */ |
| 374 | u64 vsesr_el2; | 367 | u64 vsesr_el2; |
| 375 | 368 | ||
| @@ -411,20 +404,17 @@ struct kvm_vcpu_arch { · 35 unmodified lines | |||
| 411 | #define KVM_ARM64_DEBUG_DIRTY (1 << 0) | 404 | #define KVM_ARM64_DEBUG_DIRTY (1 << 0) |
| 412 | #define KVM_ARM64_FP_ENABLED (1 << 1) /* guest FP regs loaded */ | 405 | #define KVM_ARM64_FP_ENABLED (1 << 1) /* guest FP regs loaded */ |
| 413 | #define KVM_ARM64_FP_HOST (1 << 2) /* host FP regs loaded */ | 406 | #define KVM_ARM64_FP_HOST (1 << 2) /* host FP regs loaded */ |
| 414 | #define KVM_ARM64_HOST_SVE_IN_USE (1 << 3) /* backup for host TIF_SVE */ | ||
| 415 | #define KVM_ARM64_HOST_SVE_ENABLED (1 << 4) /* SVE enabled for EL0 */ | 407 | #define KVM_ARM64_HOST_SVE_ENABLED (1 << 4) /* SVE enabled for EL0 */ |
| 416 | #define KVM_ARM64_GUEST_HAS_SVE (1 << 5) /* SVE exposed to guest */ | 408 | #define KVM_ARM64_GUEST_HAS_SVE (1 << 5) /* SVE exposed to guest */ |
| 417 | #define KVM_ARM64_VCPU_SVE_FINALIZED (1 << 6) /* SVE config completed */ | 409 | #define KVM_ARM64_VCPU_SVE_FINALIZED (1 << 6) /* SVE config completed */ |
| 418 | #define KVM_ARM64_GUEST_HAS_PTRAUTH (1 << 7) /* PTRAUTH exposed to guest */ | 410 | #define KVM_ARM64_GUEST_HAS_PTRAUTH (1 << 7) /* PTRAUTH exposed to guest */ |
| 419 | #define KVM_ARM64_PENDING_EXCEPTION (1 << 8) /* Exception pending */ | 411 | #define KVM_ARM64_PENDING_EXCEPTION (1 << 8) /* Exception pending */ |
| 412 | /* | ||
| 413 | * Overlaps with KVM_ARM64_EXCEPT_MASK on purpose so that it can't be | ||
| 414 | * set together with an exception... | ||
| 415 | */ | ||
| 416 | #define KVM_ARM64_INCREMENT_PC (1 << 9) /* Increment PC */ | ||
| 420 | #define KVM_ARM64_EXCEPT_MASK (7 << 9) /* Target EL/MODE */ | 417 | #define KVM_ARM64_EXCEPT_MASK (7 << 9) /* Target EL/MODE */ |
| 421 | #define KVM_ARM64_DEBUG_STATE_SAVE_SPE (1 << 12) /* Save SPE context if active */ | ||
| 422 | #define KVM_ARM64_DEBUG_STATE_SAVE_TRBE (1 << 13) /* Save TRBE context if active */ | ||
| 423 | |||
| 424 | #define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \ | ||
| 425 | KVM_GUESTDBG_USE_SW_BP | \ | ||
| 426 | KVM_GUESTDBG_USE_HW | \ | ||
| 427 | KVM_GUESTDBG_SINGLESTEP) | ||
| 428 | /* | 418 | /* |
| 429 | * When KVM_ARM64_PENDING_EXCEPTION is set, KVM_ARM64_EXCEPT_MASK can | 419 | * When KVM_ARM64_PENDING_EXCEPTION is set, KVM_ARM64_EXCEPT_MASK can |
| 430 | * take the following values: | 420 | * take the following values: |
| @@ -442,11 +432,14 @@ struct kvm_vcpu_arch { · 11 unmodified lines | |||
| 442 | #define KVM_ARM64_EXCEPT_AA64_EL1 (0 << 11) | 432 | #define KVM_ARM64_EXCEPT_AA64_EL1 (0 << 11) |
| 443 | #define KVM_ARM64_EXCEPT_AA64_EL2 (1 << 11) | 433 | #define KVM_ARM64_EXCEPT_AA64_EL2 (1 << 11) |
| 444 | 434 | ||
| 445 | /* | 435 | #define KVM_ARM64_DEBUG_STATE_SAVE_SPE (1 << 12) /* Save SPE context if active */ |
| 446 | * Overlaps with KVM_ARM64_EXCEPT_MASK on purpose so that it can't be | 436 | #define KVM_ARM64_DEBUG_STATE_SAVE_TRBE (1 << 13) /* Save TRBE context if active */ |
| 447 | * set together with an exception... | 437 | #define KVM_ARM64_FP_FOREIGN_FPSTATE (1 << 14) |
| 448 | */ | 438 | |
| 449 | #define KVM_ARM64_INCREMENT_PC (1 << 9) /* Increment PC */ | 439 | #define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \ |
| 440 | KVM_GUESTDBG_USE_SW_BP | \ | ||
| 441 | KVM_GUESTDBG_USE_HW | \ | ||
| 442 | KVM_GUESTDBG_SINGLESTEP) | ||
| 450 | 443 | ||
| 451 | #define vcpu_has_sve(vcpu) (system_supports_sve() && \ | 444 | #define vcpu_has_sve(vcpu) (system_supports_sve() && \ |
| 452 | ((vcpu)->arch.flags & KVM_ARM64_GUEST_HAS_SVE)) | 445 | ((vcpu)->arch.flags & KVM_ARM64_GUEST_HAS_SVE)) |
| @@ -606,6 +599,8 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu, · 153 unmodified lines | |||
| 606 | void kvm_arm_halt_guest(struct kvm *kvm); | 599 | void kvm_arm_halt_guest(struct kvm *kvm); |
| 607 | void kvm_arm_resume_guest(struct kvm *kvm); | 600 | void kvm_arm_resume_guest(struct kvm *kvm); |
| 608 | 601 | ||
| 602 | #define vcpu_has_run_once(vcpu) !!rcu_access_pointer((vcpu)->pid) | ||
| 603 | |||
| 609 | #ifndef __KVM_NVHE_HYPERVISOR__ | 604 | #ifndef __KVM_NVHE_HYPERVISOR__ |
| 610 | #define kvm_call_hyp_nvhe(f, ...) \ | 605 | #define kvm_call_hyp_nvhe(f, ...) \ |
| 611 | ({ \ | 606 | ({ \ |
| @@ -736,8 +731,10 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm, · 124 unmodified lines | |||
| 736 | /* Guest/host FPSIMD coordination helpers */ | 731 | /* Guest/host FPSIMD coordination helpers */ |
| 737 | int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu); | 732 | int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu); |
| 738 | void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu); | 733 | void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu); |
| 734 | void kvm_arch_vcpu_ctxflush_fp(struct kvm_vcpu *vcpu); | ||
| 739 | void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu); | 735 | void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu); |
| 740 | void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu); | 736 | void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu); |
| 737 | void kvm_vcpu_unshare_task_fp(struct kvm_vcpu *vcpu); | ||
| 741 | 738 | ||
| 742 | static inline bool kvm_pmu_counter_deferred(struct perf_event_attr *attr) | 739 | static inline bool kvm_pmu_counter_deferred(struct perf_event_attr *attr) |
| 743 | { | 740 | { |
| @@ -748,12 +745,7 @@ static inline bool kvm_pmu_counter_deferred(struct perf_event_attr *attr) · 4 unmodified lines | |||
| 748 | void kvm_arch_vcpu_load_debug_state_flags(struct kvm_vcpu *vcpu); | 745 | void kvm_arch_vcpu_load_debug_state_flags(struct kvm_vcpu *vcpu); |
| 749 | void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu); | 746 | void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu); |
| 750 | 747 | ||
| 751 | #ifdef CONFIG_KVM /* Avoid conflicts with core headers if CONFIG_KVM=n */ | 748 | #ifdef CONFIG_KVM |
| 752 | static inline int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) | ||
| 753 | { | ||
| 754 | return kvm_arch_vcpu_run_map_fp(vcpu); | ||
| 755 | } | ||
| 756 | |||
| 757 | void kvm_set_pmu_events(u32 set, struct perf_event_attr *attr); | 749 | void kvm_set_pmu_events(u32 set, struct perf_event_attr *attr); |
| 758 | void kvm_clr_pmu_events(u32 clr); | 750 | void kvm_clr_pmu_events(u32 clr); |
| 759 | 751 | ||
| @@ -90,7 +90,6 @@ void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu); · 89 unmodified lines | |||
| 90 | 90 | ||
| 91 | void __fpsimd_save_state(struct user_fpsimd_state *fp_regs); | 91 | void __fpsimd_save_state(struct user_fpsimd_state *fp_regs); |
| 92 | void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs); | 92 | void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs); |
| 93 | void __sve_save_state(void *sve_pffr, u32 *fpsr); | ||
| 94 | void __sve_restore_state(void *sve_pffr, u32 *fpsr); | 93 | void __sve_restore_state(void *sve_pffr, u32 *fpsr); |
| 95 | 94 | ||
| 96 | #ifndef __KVM_NVHE_HYPERVISOR__ | 95 | #ifndef __KVM_NVHE_HYPERVISOR__ |
| @@ -150,6 +150,8 @@ static __always_inline unsigned long __kern_hyp_va(unsigned long v) · 149 unmodified lines | |||
| 150 | #include <asm/kvm_pgtable.h> | 150 | #include <asm/kvm_pgtable.h> |
| 151 | #include <asm/stage2_pgtable.h> | 151 | #include <asm/stage2_pgtable.h> |
| 152 | 152 | ||
| 153 | int kvm_share_hyp(void *from, void *to); | ||
| 154 | void kvm_unshare_hyp(void *from, void *to); | ||
| 153 | int create_hyp_mappings(void *from, void *to, enum kvm_pgtable_prot prot); | 155 | int create_hyp_mappings(void *from, void *to, enum kvm_pgtable_prot prot); |
| 154 | int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size, | 156 | int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size, |
| 155 | void __iomem **kaddr, | 157 | void __iomem **kaddr, |
| @@ -251,6 +251,27 @@ void kvm_pgtable_hyp_destroy(struct kvm_pgtable *pgt); · 250 unmodified lines | |||
| 251 | int kvm_pgtable_hyp_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys, | 251 | int kvm_pgtable_hyp_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys, |
| 252 | enum kvm_pgtable_prot prot); | 252 | enum kvm_pgtable_prot prot); |
| 253 | 253 | ||
| 254 | /** | ||
| 255 | * kvm_pgtable_hyp_unmap() - Remove a mapping from a hypervisor stage-1 page-table. | ||
| 256 | * @pgt: Page-table structure initialised by kvm_pgtable_hyp_init(). | ||
| 257 | * @addr: Virtual address from which to remove the mapping. | ||
| 258 | * @size: Size of the mapping. | ||
| 259 | * | ||
| 260 | * The offset of @addr within a page is ignored, @size is rounded-up to | ||
| 261 | * the next page boundary and @phys is rounded-down to the previous page | ||
| 262 | * boundary. | ||
| 263 | * | ||
| 264 | * TLB invalidation is performed for each page-table entry cleared during the | ||
| 265 | * unmapping operation and the reference count for the page-table page | ||
| 266 | * containing the cleared entry is decremented, with unreferenced pages being | ||
| 267 | * freed. The unmapping operation will stop early if it encounters either an | ||
| 268 | * invalid page-table entry or a valid block mapping which maps beyond the range | ||
| 269 | * being unmapped. | ||
| 270 | * | ||
| 271 | * Return: Number of bytes unmapped, which may be 0. | ||
| 272 | */ | ||
| 273 | u64 kvm_pgtable_hyp_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size); | ||
| 274 | |||
| 254 | /** | 275 | /** |
| 255 | * kvm_get_vtcr() - Helper to construct VTCR_EL2 | 276 | * kvm_get_vtcr() - Helper to construct VTCR_EL2 |
| 256 | * @mmfr0: Sanitized value of SYS_ID_AA64MMFR0_EL1 register. | 277 | * @mmfr0: Sanitized value of SYS_ID_AA64MMFR0_EL1 register. |
| @@ -270,8 +291,7 @@ u64 kvm_get_vtcr(u64 mmfr0, u64 mmfr1, u32 phys_shift); · 13 unmodified lines | |||
| 270 | /** | 291 | /** |
| 271 | * __kvm_pgtable_stage2_init() - Initialise a guest stage-2 page-table. | 292 | * __kvm_pgtable_stage2_init() - Initialise a guest stage-2 page-table. |
| 272 | * @pgt: Uninitialised page-table structure to initialise. | 293 | * @pgt: Uninitialised page-table structure to initialise. |
| 273 | * @arch: Arch-specific KVM structure representing the guest virtual | 294 | * @mmu: S2 MMU context for this S2 translation |
| 274 | * machine. | ||
| 275 | * @mm_ops: Memory management callbacks. | 295 | * @mm_ops: Memory management callbacks. |
| 276 | * @flags: Stage-2 configuration flags. | 296 | * @flags: Stage-2 configuration flags. |
| 277 | * @force_pte_cb: Function that returns true if page level mappings must | 297 | * @force_pte_cb: Function that returns true if page level mappings must |
| @@ -279,13 +299,13 @@ u64 kvm_get_vtcr(u64 mmfr0, u64 mmfr1, u32 phys_shift); · 1 unmodified line | |||
| 279 | * | 299 | * |
| 280 | * Return: 0 on success, negative error code on failure. | 300 | * Return: 0 on success, negative error code on failure. |
| 281 | */ | 301 | */ |
| 282 | int __kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_arch *arch, | 302 | int __kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu, |
| 283 | struct kvm_pgtable_mm_ops *mm_ops, | 303 | struct kvm_pgtable_mm_ops *mm_ops, |
| 284 | enum kvm_pgtable_stage2_flags flags, | 304 | enum kvm_pgtable_stage2_flags flags, |
| 285 | kvm_pgtable_force_pte_cb_t force_pte_cb); | 305 | kvm_pgtable_force_pte_cb_t force_pte_cb); |
| 286 | 306 | ||
| 287 | #define kvm_pgtable_stage2_init(pgt, arch, mm_ops) \ | 307 | #define kvm_pgtable_stage2_init(pgt, mmu, mm_ops) \ |
| 288 | __kvm_pgtable_stage2_init(pgt, arch, mm_ops, 0, NULL) | 308 | __kvm_pgtable_stage2_init(pgt, mmu, mm_ops, 0, NULL) |
| 289 | 309 | ||
| 290 | /** | 310 | /** |
| 291 | * kvm_pgtable_stage2_destroy() - Destroy an unused guest stage-2 page-table. | 311 | * kvm_pgtable_stage2_destroy() - Destroy an unused guest stage-2 page-table. |
| @@ -0,0 +1,71 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2020 - Google LLC | ||
| 4 | * Author: Quentin Perret <qperret@google.com> | ||
| 5 | */ | ||
| 6 | #ifndef __ARM64_KVM_PKVM_H__ | ||
| 7 | #define __ARM64_KVM_PKVM_H__ | ||
| 8 | |||
| 9 | #include <linux/memblock.h> | ||
| 10 | #include <asm/kvm_pgtable.h> | ||
| 11 | |||
| 12 | #define HYP_MEMBLOCK_REGIONS 128 | ||
| 13 | |||
| 14 | extern struct memblock_region kvm_nvhe_sym(hyp_memory)[]; | ||
| 15 | extern unsigned int kvm_nvhe_sym(hyp_memblock_nr); | ||
| 16 | |||
| 17 | static inline unsigned long __hyp_pgtable_max_pages(unsigned long nr_pages) | ||
| 18 | { | ||
| 19 | unsigned long total = 0, i; | ||
| 20 | |||
| 21 | /* Provision the worst case scenario */ | ||
| 22 | for (i = 0; i < KVM_PGTABLE_MAX_LEVELS; i++) { | ||
| 23 | nr_pages = DIV_ROUND_UP(nr_pages, PTRS_PER_PTE); | ||
| 24 | total += nr_pages; | ||
| 25 | } | ||
| 26 | |||
| 27 | return total; | ||
| 28 | } | ||
| 29 | |||
| 30 | static inline unsigned long __hyp_pgtable_total_pages(void) | ||
| 31 | { | ||
| 32 | unsigned long res = 0, i; | ||
| 33 | |||
| 34 | /* Cover all of memory with page-granularity */ | ||
| 35 | for (i = 0; i < kvm_nvhe_sym(hyp_memblock_nr); i++) { | ||
| 36 | struct memblock_region *reg = &kvm_nvhe_sym(hyp_memory)[i]; | ||
| 37 | res += __hyp_pgtable_max_pages(reg->size >> PAGE_SHIFT); | ||
| 38 | } | ||
| 39 | |||
| 40 | return res; | ||
| 41 | } | ||
| 42 | |||
| 43 | static inline unsigned long hyp_s1_pgtable_pages(void) | ||
| 44 | { | ||
| 45 | unsigned long res; | ||
| 46 | |||
| 47 | res = __hyp_pgtable_total_pages(); | ||
| 48 | |||
| 49 | /* Allow 1 GiB for private mappings */ | ||
| 50 | res += __hyp_pgtable_max_pages(SZ_1G >> PAGE_SHIFT); | ||
| 51 | |||
| 52 | return res; | ||
| 53 | } | ||
| 54 | |||
| 55 | static inline unsigned long host_s2_pgtable_pages(void) | ||
| 56 | { | ||
| 57 | unsigned long res; | ||
| 58 | |||
| 59 | /* | ||
| 60 | * Include an extra 16 pages to safely upper-bound the worst case of | ||
| 61 | * concatenated pgds. | ||
| 62 | */ | ||
| 63 | res = __hyp_pgtable_total_pages() + 16; | ||
| 64 | |||
| 65 | /* Allow 1 GiB for MMIO mappings */ | ||
| 66 | res += __hyp_pgtable_max_pages(SZ_1G >> PAGE_SHIFT); | ||
| 67 | |||
| 68 | return res; | ||
| 69 | } | ||
| 70 | |||
| 71 | #endif /* __ARM64_KVM_PKVM_H__ */ | ||
| @@ -15,6 +15,7 @@ · 14 unmodified lines | |||
| 15 | #ifndef __ASSEMBLY__ | 15 | #ifndef __ASSEMBLY__ |
| 16 | 16 | ||
| 17 | #include <linux/refcount.h> | 17 | #include <linux/refcount.h> |
| 18 | #include <asm/cpufeature.h> | ||
| 18 | 19 | ||
| 19 | typedef struct { | 20 | typedef struct { |
| 20 | atomic64_t id; | 21 | atomic64_t id; |
| @@ -937,6 +937,7 @@ · 936 unmodified lines | |||
| 937 | #define ID_AA64DFR0_PMUVER_8_1 0x4 | 937 | #define ID_AA64DFR0_PMUVER_8_1 0x4 |
| 938 | #define ID_AA64DFR0_PMUVER_8_4 0x5 | 938 | #define ID_AA64DFR0_PMUVER_8_4 0x5 |
| 939 | #define ID_AA64DFR0_PMUVER_8_5 0x6 | 939 | #define ID_AA64DFR0_PMUVER_8_5 0x6 |
| 940 | #define ID_AA64DFR0_PMUVER_8_7 0x7 | ||
| 940 | #define ID_AA64DFR0_PMUVER_IMP_DEF 0xf | 941 | #define ID_AA64DFR0_PMUVER_IMP_DEF 0xf |
| 941 | 942 | ||
| 942 | #define ID_AA64DFR0_PMSVER_8_2 0x1 | 943 | #define ID_AA64DFR0_PMSVER_8_2 0x1 |
| @@ -111,7 +111,6 @@ int main(void) · 110 unmodified lines | |||
| 111 | #ifdef CONFIG_KVM | 111 | #ifdef CONFIG_KVM |
| 112 | DEFINE(VCPU_CONTEXT, offsetof(struct kvm_vcpu, arch.ctxt)); | 112 | DEFINE(VCPU_CONTEXT, offsetof(struct kvm_vcpu, arch.ctxt)); |
| 113 | DEFINE(VCPU_FAULT_DISR, offsetof(struct kvm_vcpu, arch.fault.disr_el1)); | 113 | DEFINE(VCPU_FAULT_DISR, offsetof(struct kvm_vcpu, arch.fault.disr_el1)); |
| 114 | DEFINE(VCPU_WORKAROUND_FLAGS, offsetof(struct kvm_vcpu, arch.workaround_flags)); | ||
| 115 | DEFINE(VCPU_HCR_EL2, offsetof(struct kvm_vcpu, arch.hcr_el2)); | 114 | DEFINE(VCPU_HCR_EL2, offsetof(struct kvm_vcpu, arch.hcr_el2)); |
| 116 | DEFINE(CPU_USER_PT_REGS, offsetof(struct kvm_cpu_context, regs)); | 115 | DEFINE(CPU_USER_PT_REGS, offsetof(struct kvm_cpu_context, regs)); |
| 117 | DEFINE(CPU_RGSR_EL1, offsetof(struct kvm_cpu_context, sys_regs[RGSR_EL1])); | 116 | DEFINE(CPU_RGSR_EL1, offsetof(struct kvm_cpu_context, sys_regs[RGSR_EL1])); |
| @@ -78,7 +78,11 @@ · 77 unmodified lines | |||
| 78 | * indicate whether or not the userland FPSIMD state of the current task is | 78 | * indicate whether or not the userland FPSIMD state of the current task is |
| 79 | * present in the registers. The flag is set unless the FPSIMD registers of this | 79 | * present in the registers. The flag is set unless the FPSIMD registers of this |
| 80 | * CPU currently contain the most recent userland FPSIMD state of the current | 80 | * CPU currently contain the most recent userland FPSIMD state of the current |
| 81 | * task. | 81 | * task. If the task is behaving as a VMM, then this is will be managed by |
| 82 | * KVM which will clear it to indicate that the vcpu FPSIMD state is currently | ||
| 83 | * loaded on the CPU, allowing the state to be saved if a FPSIMD-aware | ||
| 84 | * softirq kicks in. Upon vcpu_put(), KVM will save the vcpu FP state and | ||
| 85 | * flag the register state as invalid. | ||
| 82 | * | 86 | * |
| 83 | * In order to allow softirq handlers to use FPSIMD, kernel_neon_begin() may | 87 | * In order to allow softirq handlers to use FPSIMD, kernel_neon_begin() may |
| 84 | * save the task's FPSIMD context back to task_struct from softirq context. | 88 | * save the task's FPSIMD context back to task_struct from softirq context. |
| @@ -0,0 +1,2 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | hyp_constants.h | ||
| @@ -13,7 +13,7 @@ obj-$(CONFIG_KVM) += hyp/ · 12 unmodified lines | |||
| 13 | kvm-y += arm.o mmu.o mmio.o psci.o perf.o hypercalls.o pvtime.o \ | 13 | kvm-y += arm.o mmu.o mmio.o psci.o perf.o hypercalls.o pvtime.o \ |
| 14 | inject_fault.o va_layout.o handle_exit.o \ | 14 | inject_fault.o va_layout.o handle_exit.o \ |
| 15 | guest.o debug.o reset.o sys_regs.o \ | 15 | guest.o debug.o reset.o sys_regs.o \ |
| 16 | vgic-sys-reg-v3.o fpsimd.o pmu.o \ | 16 | vgic-sys-reg-v3.o fpsimd.o pmu.o pkvm.o \ |
| 17 | arch_timer.o trng.o\ | 17 | arch_timer.o trng.o\ |
| 18 | vgic/vgic.o vgic/vgic-init.o \ | 18 | vgic/vgic.o vgic/vgic-init.o \ |
| 19 | vgic/vgic-irqfd.o vgic/vgic-v2.o \ | 19 | vgic/vgic-irqfd.o vgic/vgic-v2.o \ |
| @@ -23,3 +23,19 @@ kvm-y += arm.o mmu.o mmio.o psci.o perf.o hypercalls.o pvtime.o \ · 3 unmodified lines | |||
| 23 | vgic/vgic-its.o vgic/vgic-debug.o | 23 | vgic/vgic-its.o vgic/vgic-debug.o |
| 24 | 24 | ||
| 25 | kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o | 25 | kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o |
| 26 | |||
| 27 | always-y := hyp_constants.h hyp-constants.s | ||
| 28 | |||
| 29 | define rule_gen_hyp_constants | ||
| 30 | $(call filechk,offsets,__HYP_CONSTANTS_H__) | ||
| 31 | endef | ||
| 32 | |||
| 33 | CFLAGS_hyp-constants.o = -I $(srctree)/$(src)/hyp/include | ||
| 34 | $(obj)/hyp-constants.s: $(src)/hyp/hyp-constants.c FORCE | ||
| 35 | $(call if_changed_dep,cc_s_c) | ||
| 36 | |||
| 37 | $(obj)/hyp_constants.h: $(obj)/hyp-constants.s FORCE | ||
| 38 | $(call if_changed_rule,gen_hyp_constants) | ||
| 39 | |||
| 40 | obj-kvm := $(addprefix $(obj)/, $(kvm-y)) | ||
| 41 | $(obj-kvm): $(obj)/hyp_constants.h | ||
| @@ -146,7 +146,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) · 145 unmodified lines | |||
| 146 | if (ret) | 146 | if (ret) |
| 147 | return ret; | 147 | return ret; |
| 148 | 148 | ||
| 149 | ret = create_hyp_mappings(kvm, kvm + 1, PAGE_HYP); | 149 | ret = kvm_share_hyp(kvm, kvm + 1); |
| 150 | if (ret) | 150 | if (ret) |
| 151 | goto out_free_stage2_pgd; | 151 | goto out_free_stage2_pgd; |
| 152 | 152 | ||
| @@ -180,6 +180,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm) · 27 unmodified lines | |||
| 180 | kvm_vgic_destroy(kvm); | 180 | kvm_vgic_destroy(kvm); |
| 181 | 181 | ||
| 182 | kvm_destroy_vcpus(kvm); | 182 | kvm_destroy_vcpus(kvm); |
| 183 | |||
| 184 | kvm_unshare_hyp(kvm, kvm + 1); | ||
| 183 | } | 185 | } |
| 184 | 186 | ||
| 185 | int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) | 187 | int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) |
| @@ -334,7 +336,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) · 148 unmodified lines | |||
| 334 | if (err) | 336 | if (err) |
| 335 | return err; | 337 | return err; |
| 336 | 338 | ||
| 337 | return create_hyp_mappings(vcpu, vcpu + 1, PAGE_HYP); | 339 | return kvm_share_hyp(vcpu, vcpu + 1); |
| 338 | } | 340 | } |
| 339 | 341 | ||
| 340 | void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) | 342 | void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) |
| @@ -343,7 +345,7 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) · 2 unmodified lines | |||
| 343 | 345 | ||
| 344 | void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) | 346 | void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) |
| 345 | { | 347 | { |
| 346 | if (vcpu->arch.has_run_once && unlikely(!irqchip_in_kernel(vcpu->kvm))) | 348 | if (vcpu_has_run_once(vcpu) && unlikely(!irqchip_in_kernel(vcpu->kvm))) |
| 347 | static_branch_dec(&userspace_irqchip_in_use); | 349 | static_branch_dec(&userspace_irqchip_in_use); |
| 348 | 350 | ||
| 349 | kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); | 351 | kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); |
| @@ -561,18 +563,33 @@ static void update_vmid(struct kvm_vmid *vmid) · 211 unmodified lines | |||
| 561 | spin_unlock(&kvm_vmid_lock); | 563 | spin_unlock(&kvm_vmid_lock); |
| 562 | } | 564 | } |
| 563 | 565 | ||
| 564 | static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) | 566 | static int kvm_vcpu_initialized(struct kvm_vcpu *vcpu) |
| 567 | { | ||
| 568 | return vcpu->arch.target >= 0; | ||
| 569 | } | ||
| 570 | |||
| 571 | /* | ||
| 572 | * Handle both the initialisation that is being done when the vcpu is | ||
| 573 | * run for the first time, as well as the updates that must be | ||
| 574 | * performed each time we get a new thread dealing with this vcpu. | ||
| 575 | */ | ||
| 576 | int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) | ||
| 565 | { | 577 | { |
| 566 | struct kvm *kvm = vcpu->kvm; | 578 | struct kvm *kvm = vcpu->kvm; |
| 567 | int ret = 0; | 579 | int ret; |
| 568 | 580 | ||
| 569 | if (likely(vcpu->arch.has_run_once)) | 581 | if (!kvm_vcpu_initialized(vcpu)) |
| 570 | return 0; | 582 | return -ENOEXEC; |
| 571 | 583 | ||
| 572 | if (!kvm_arm_vcpu_is_finalized(vcpu)) | 584 | if (!kvm_arm_vcpu_is_finalized(vcpu)) |
| 573 | return -EPERM; | 585 | return -EPERM; |
| 574 | 586 | ||
| 575 | vcpu->arch.has_run_once = true; | 587 | ret = kvm_arch_vcpu_run_map_fp(vcpu); |
| 588 | if (ret) | ||
| 589 | return ret; | ||
| 590 | |||
| 591 | if (likely(vcpu_has_run_once(vcpu))) | ||
| 592 | return 0; | ||
| 576 | 593 | ||
| 577 | kvm_arm_vcpu_init_debug(vcpu); | 594 | kvm_arm_vcpu_init_debug(vcpu); |
| 578 | 595 | ||
| @@ -584,12 +601,6 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) · 5 unmodified lines | |||
| 584 | ret = kvm_vgic_map_resources(kvm); | 601 | ret = kvm_vgic_map_resources(kvm); |
| 585 | if (ret) | 602 | if (ret) |
| 586 | return ret; | 603 | return ret; |
| 587 | } else { | ||
| 588 | /* | ||
| 589 | * Tell the rest of the code that there are userspace irqchip | ||
| 590 | * VMs in the wild. | ||
| 591 | */ | ||
| 592 | static_branch_inc(&userspace_irqchip_in_use); | ||
| 593 | } | 604 | } |
| 594 | 605 | ||
| 595 | ret = kvm_timer_enable(vcpu); | 606 | ret = kvm_timer_enable(vcpu); |
| @@ -597,6 +608,16 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) · 1 unmodified line | |||
| 597 | return ret; | 608 | return ret; |
| 598 | 609 | ||
| 599 | ret = kvm_arm_pmu_v3_enable(vcpu); | 610 | ret = kvm_arm_pmu_v3_enable(vcpu); |
| 611 | if (ret) | ||
| 612 | return ret; | ||
| 613 | |||
| 614 | if (!irqchip_in_kernel(kvm)) { | ||
| 615 | /* | ||
| 616 | * Tell the rest of the code that there are userspace irqchip | ||
| 617 | * VMs in the wild. | ||
| 618 | */ | ||
| 619 | static_branch_inc(&userspace_irqchip_in_use); | ||
| 620 | } | ||
| 600 | 621 | ||
| 601 | /* | 622 | /* |
| 602 | * Initialize traps for protected VMs. | 623 | * Initialize traps for protected VMs. |
| @@ -689,11 +710,6 @@ void kvm_vcpu_wfi(struct kvm_vcpu *vcpu) · 86 unmodified lines | |||
| 689 | preempt_enable(); | 710 | preempt_enable(); |
| 690 | } | 711 | } |
| 691 | 712 | ||
| 692 | static int kvm_vcpu_initialized(struct kvm_vcpu *vcpu) | ||
| 693 | { | ||
| 694 | return vcpu->arch.target >= 0; | ||
| 695 | } | ||
| 696 | |||
| 697 | static void check_vcpu_requests(struct kvm_vcpu *vcpu) | 713 | static void check_vcpu_requests(struct kvm_vcpu *vcpu) |
| 698 | { | 714 | { |
| 699 | if (kvm_request_pending(vcpu)) { | 715 | if (kvm_request_pending(vcpu)) { |
| @@ -789,13 +805,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) · 89 unmodified lines | |||
| 789 | struct kvm_run *run = vcpu->run; | 805 | struct kvm_run *run = vcpu->run; |
| 790 | int ret; | 806 | int ret; |
| 791 | 807 | ||
| 792 | if (unlikely(!kvm_vcpu_initialized(vcpu))) | ||
| 793 | return -ENOEXEC; | ||
| 794 | |||
| 795 | ret = kvm_vcpu_first_run_init(vcpu); | ||
| 796 | if (ret) | ||
| 797 | return ret; | ||
| 798 | |||
| 799 | if (run->exit_reason == KVM_EXIT_MMIO) { | 808 | if (run->exit_reason == KVM_EXIT_MMIO) { |
| 800 | ret = kvm_handle_mmio_return(vcpu); | 809 | ret = kvm_handle_mmio_return(vcpu); |
| 801 | if (ret) | 810 | if (ret) |
| @@ -859,6 +868,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) · 57 unmodified lines | |||
| 859 | } | 868 | } |
| 860 | 869 | ||
| 861 | kvm_arm_setup_debug(vcpu); | 870 | kvm_arm_setup_debug(vcpu); |
| 871 | kvm_arch_vcpu_ctxflush_fp(vcpu); | ||
| 862 | 872 | ||
| 863 | /************************************************************** | 873 | /************************************************************** |
| 864 | * Enter the guest | 874 | * Enter the guest |
| @@ -1133,7 +1143,7 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu, · 268 unmodified lines | |||
| 1133 | * need to invalidate the I-cache though, as FWB does *not* | 1143 | * need to invalidate the I-cache though, as FWB does *not* |
| 1134 | * imply CTR_EL0.DIC. | 1144 | * imply CTR_EL0.DIC. |
| 1135 | */ | 1145 | */ |
| 1136 | if (vcpu->arch.has_run_once) { | 1146 | if (vcpu_has_run_once(vcpu)) { |
| 1137 | if (!cpus_have_final_cap(ARM64_HAS_STAGE2_FWB)) | 1147 | if (!cpus_have_final_cap(ARM64_HAS_STAGE2_FWB)) |
| 1138 | stage2_unmap_vm(vcpu->kvm); | 1148 | stage2_unmap_vm(vcpu->kvm); |
| 1139 | else | 1149 | else |
| @@ -7,7 +7,6 @@ · 6 unmodified lines | |||
| 7 | */ | 7 | */ |
| 8 | #include <linux/irqflags.h> | 8 | #include <linux/irqflags.h> |
| 9 | #include <linux/sched.h> | 9 | #include <linux/sched.h> |
| 10 | #include <linux/thread_info.h> | ||
| 11 | #include <linux/kvm_host.h> | 10 | #include <linux/kvm_host.h> |
| 12 | #include <asm/fpsimd.h> | 11 | #include <asm/fpsimd.h> |
| 13 | #include <asm/kvm_asm.h> | 12 | #include <asm/kvm_asm.h> |
| @@ -15,6 +14,19 @@ · 1 unmodified line | |||
| 15 | #include <asm/kvm_mmu.h> | 14 | #include <asm/kvm_mmu.h> |
| 16 | #include <asm/sysreg.h> | 15 | #include <asm/sysreg.h> |
| 17 | 16 | ||
| 17 | void kvm_vcpu_unshare_task_fp(struct kvm_vcpu *vcpu) | ||
| 18 | { | ||
| 19 | struct task_struct *p = vcpu->arch.parent_task; | ||
| 20 | struct user_fpsimd_state *fpsimd; | ||
| 21 | |||
| 22 | if (!is_protected_kvm_enabled() || !p) | ||
| 23 | return; | ||
| 24 | |||
| 25 | fpsimd = &p->thread.uw.fpsimd_state; | ||
| 26 | kvm_unshare_hyp(fpsimd, fpsimd + 1); | ||
| 27 | put_task_struct(p); | ||
| 28 | } | ||
| 29 | |||
| 18 | /* | 30 | /* |
| 19 | * Called on entry to KVM_RUN unless this vcpu previously ran at least | 31 | * Called on entry to KVM_RUN unless this vcpu previously ran at least |
| 20 | * once and the most recent prior KVM_RUN for this vcpu was called from | 32 | * once and the most recent prior KVM_RUN for this vcpu was called from |
| @@ -28,36 +40,29 @@ int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu) · 7 unmodified lines | |||
| 28 | { | 40 | { |
| 29 | int ret; | 41 | int ret; |
| 30 | 42 | ||
| 31 | struct thread_info *ti = ¤t->thread_info; | ||
| 32 | struct user_fpsimd_state *fpsimd = ¤t->thread.uw.fpsimd_state; | 43 | struct user_fpsimd_state *fpsimd = ¤t->thread.uw.fpsimd_state; |
| 33 | 44 | ||
| 34 | /* | 45 | kvm_vcpu_unshare_task_fp(vcpu); |
| 35 | * Make sure the host task thread flags and fpsimd state are | ||
| 36 | * visible to hyp: | ||
| 37 | */ | ||
| 38 | ret = create_hyp_mappings(ti, ti + 1, PAGE_HYP); | ||
| 39 | if (ret) | ||
| 40 | goto error; | ||
| 41 | 46 | ||
| 42 | ret = create_hyp_mappings(fpsimd, fpsimd + 1, PAGE_HYP); | 47 | /* Make sure the host task fpsimd state is visible to hyp: */ |
| 48 | ret = kvm_share_hyp(fpsimd, fpsimd + 1); | ||
| 43 | if (ret) | 49 | if (ret) |
| 44 | goto error; | 50 | return ret; |
| 45 | |||
| 46 | if (vcpu->arch.sve_state) { | ||
| 47 | void *sve_end; | ||
| 48 | 51 | ||
| 49 | sve_end = vcpu->arch.sve_state + vcpu_sve_state_size(vcpu); | 52 | vcpu->arch.host_fpsimd_state = kern_hyp_va(fpsimd); |
| 50 | 53 | ||
| 51 | ret = create_hyp_mappings(vcpu->arch.sve_state, sve_end, | 54 | /* |
| 52 | PAGE_HYP); | 55 | * We need to keep current's task_struct pinned until its data has been |
| 53 | if (ret) | 56 | * unshared with the hypervisor to make sure it is not re-used by the |
| 54 | goto error; | 57 | * kernel and donated to someone else while already shared -- see |
| 58 | * kvm_vcpu_unshare_task_fp() for the matching put_task_struct(). | ||
| 59 | */ | ||
| 60 | if (is_protected_kvm_enabled()) { | ||
| 61 | get_task_struct(current); | ||
| 62 | vcpu->arch.parent_task = current; | ||
| 55 | } | 63 | } |
| 56 | 64 | ||
| 57 | vcpu->arch.host_thread_info = kern_hyp_va(ti); | 65 | return 0; |
| 58 | vcpu->arch.host_fpsimd_state = kern_hyp_va(fpsimd); | ||
| 59 | error: | ||
| 60 | return ret; | ||
| 61 | } | 66 | } |
| 62 | 67 | ||
| 63 | /* | 68 | /* |
| @@ -66,26 +71,27 @@ error: · 2 unmodified lines | |||
| 66 | * | 71 | * |
| 67 | * Here, we just set the correct metadata to indicate that the FPSIMD | 72 | * Here, we just set the correct metadata to indicate that the FPSIMD |
| 68 | * state in the cpu regs (if any) belongs to current on the host. | 73 | * state in the cpu regs (if any) belongs to current on the host. |
| 69 | * | ||
| 70 | * TIF_SVE is backed up here, since it may get clobbered with guest state. | ||
| 71 | * This flag is restored by kvm_arch_vcpu_put_fp(vcpu). | ||
| 72 | */ | 74 | */ |
| 73 | void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu) | 75 | void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu) |
| 74 | { | 76 | { |
| 75 | BUG_ON(!current->mm); | 77 | BUG_ON(!current->mm); |
| 78 | BUG_ON(test_thread_flag(TIF_SVE)); | ||
| 76 | 79 | ||
| 77 | vcpu->arch.flags &= ~(KVM_ARM64_FP_ENABLED | | 80 | vcpu->arch.flags &= ~KVM_ARM64_FP_ENABLED; |
| 78 | KVM_ARM64_HOST_SVE_IN_USE | | ||
| 79 | KVM_ARM64_HOST_SVE_ENABLED); | ||
| 80 | vcpu->arch.flags |= KVM_ARM64_FP_HOST; | 81 | vcpu->arch.flags |= KVM_ARM64_FP_HOST; |
| 81 | 82 | ||
| 82 | if (test_thread_flag(TIF_SVE)) | ||
| 83 | vcpu->arch.flags |= KVM_ARM64_HOST_SVE_IN_USE; | ||
| 84 | |||
| 85 | if (read_sysreg(cpacr_el1) & CPACR_EL1_ZEN_EL0EN) | 83 | if (read_sysreg(cpacr_el1) & CPACR_EL1_ZEN_EL0EN) |
| 86 | vcpu->arch.flags |= KVM_ARM64_HOST_SVE_ENABLED; | 84 | vcpu->arch.flags |= KVM_ARM64_HOST_SVE_ENABLED; |
| 87 | } | 85 | } |
| 88 | 86 | ||
| 87 | void kvm_arch_vcpu_ctxflush_fp(struct kvm_vcpu *vcpu) | ||
| 88 | { | ||
| 89 | if (test_thread_flag(TIF_FOREIGN_FPSTATE)) | ||
| 90 | vcpu->arch.flags |= KVM_ARM64_FP_FOREIGN_FPSTATE; | ||
| 91 | else | ||
| 92 | vcpu->arch.flags &= ~KVM_ARM64_FP_FOREIGN_FPSTATE; | ||
| 93 | } | ||
| 94 | |||
| 89 | /* | 95 | /* |
| 90 | * If the guest FPSIMD state was loaded, update the host's context | 96 | * If the guest FPSIMD state was loaded, update the host's context |
| 91 | * tracking data mark the CPU FPSIMD regs as dirty and belonging to vcpu | 97 | * tracking data mark the CPU FPSIMD regs as dirty and belonging to vcpu |
| @@ -115,13 +121,11 @@ void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu) · 23 unmodified lines | |||
| 115 | void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu) | 121 | void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu) |
| 116 | { | 122 | { |
| 117 | unsigned long flags; | 123 | unsigned long flags; |
| 118 | bool host_has_sve = system_supports_sve(); | ||
| 119 | bool guest_has_sve = vcpu_has_sve(vcpu); | ||
| 120 | 124 | ||
| 121 | local_irq_save(flags); | 125 | local_irq_save(flags); |
| 122 | 126 | ||
| 123 | if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) { | 127 | if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) { |
| 124 | if (guest_has_sve) { | 128 | if (vcpu_has_sve(vcpu)) { |
| 125 | __vcpu_sys_reg(vcpu, ZCR_EL1) = read_sysreg_el1(SYS_ZCR); | 129 | __vcpu_sys_reg(vcpu, ZCR_EL1) = read_sysreg_el1(SYS_ZCR); |
| 126 | 130 | ||
| 127 | /* Restore the VL that was saved when bound to the CPU */ | 131 | /* Restore the VL that was saved when bound to the CPU */ |
| @@ -131,7 +135,7 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu) · 3 unmodified lines | |||
| 131 | } | 135 | } |
| 132 | 136 | ||
| 133 | fpsimd_save_and_flush_cpu_state(); | 137 | fpsimd_save_and_flush_cpu_state(); |
| 134 | } else if (has_vhe() && host_has_sve) { | 138 | } else if (has_vhe() && system_supports_sve()) { |
| 135 | /* | 139 | /* |
| 136 | * The FPSIMD/SVE state in the CPU has not been touched, and we | 140 | * The FPSIMD/SVE state in the CPU has not been touched, and we |
| 137 | * have SVE (and VHE): CPACR_EL1 (alias CPTR_EL2) has been | 141 | * have SVE (and VHE): CPACR_EL1 (alias CPTR_EL2) has been |
| @@ -145,8 +149,7 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu) · 7 unmodified lines | |||
| 145 | sysreg_clear_set(CPACR_EL1, CPACR_EL1_ZEN_EL0EN, 0); | 149 | sysreg_clear_set(CPACR_EL1, CPACR_EL1_ZEN_EL0EN, 0); |
| 146 | } | 150 | } |
| 147 | 151 | ||
| 148 | update_thread_flag(TIF_SVE, | 152 | update_thread_flag(TIF_SVE, 0); |
| 149 | vcpu->arch.flags & KVM_ARM64_HOST_SVE_IN_USE); | ||
| 150 | 153 | ||
| 151 | local_irq_restore(flags); | 154 | local_irq_restore(flags); |
| 152 | } | 155 | } |
| @@ -10,4 +10,4 @@ subdir-ccflags-y := -I$(incdir) \ · 9 unmodified lines | |||
| 10 | -DDISABLE_BRANCH_PROFILING \ | 10 | -DDISABLE_BRANCH_PROFILING \ |
| 11 | $(DISABLE_STACKLEAK_PLUGIN) | 11 | $(DISABLE_STACKLEAK_PLUGIN) |
| 12 | 12 | ||
| 13 | obj-$(CONFIG_KVM) += vhe/ nvhe/ pgtable.o reserved_mem.o | 13 | obj-$(CONFIG_KVM) += vhe/ nvhe/ pgtable.o |
| @@ -25,9 +25,3 @@ SYM_FUNC_START(__sve_restore_state) · 24 unmodified lines | |||
| 25 | sve_load 0, x1, x2, 3 | 25 | sve_load 0, x1, x2, 3 |
| 26 | ret | 26 | ret |
| 27 | SYM_FUNC_END(__sve_restore_state) | 27 | SYM_FUNC_END(__sve_restore_state) |
| 28 | |||
| 29 | SYM_FUNC_START(__sve_save_state) | ||
| 30 | mov x2, #1 | ||
| 31 | sve_save 0, x1, x2, 3 | ||
| 32 | ret | ||
| 33 | SYM_FUNC_END(__sve_save_state) | ||
| @@ -0,0 +1,10 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | |||
| 3 | #include <linux/kbuild.h> | ||
| 4 | #include <nvhe/memory.h> | ||
| 5 | |||
| 6 | int main(void) | ||
| 7 | { | ||
| 8 | DEFINE(STRUCT_HYP_PAGE_SIZE, sizeof(struct hyp_page)); | ||
| 9 | return 0; | ||
| 10 | } | ||
| @@ -29,7 +29,6 @@ · 28 unmodified lines | |||
| 29 | #include <asm/fpsimd.h> | 29 | #include <asm/fpsimd.h> |
| 30 | #include <asm/debug-monitors.h> | 30 | #include <asm/debug-monitors.h> |
| 31 | #include <asm/processor.h> | 31 | #include <asm/processor.h> |
| 32 | #include <asm/thread_info.h> | ||
| 33 | 32 | ||
| 34 | struct kvm_exception_table_entry { | 33 | struct kvm_exception_table_entry { |
| 35 | int insn, fixup; | 34 | int insn, fixup; |
| @@ -49,7 +48,7 @@ static inline bool update_fp_enabled(struct kvm_vcpu *vcpu) · 13 unmodified lines | |||
| 49 | * trap the accesses. | 48 | * trap the accesses. |
| 50 | */ | 49 | */ |
| 51 | if (!system_supports_fpsimd() || | 50 | if (!system_supports_fpsimd() || |
| 52 | vcpu->arch.host_thread_info->flags & _TIF_FOREIGN_FPSTATE) | 51 | vcpu->arch.flags & KVM_ARM64_FP_FOREIGN_FPSTATE) |
| 53 | vcpu->arch.flags &= ~(KVM_ARM64_FP_ENABLED | | 52 | vcpu->arch.flags &= ~(KVM_ARM64_FP_ENABLED | |
| 54 | KVM_ARM64_FP_HOST); | 53 | KVM_ARM64_FP_HOST); |
| 55 | 54 | ||
| @@ -143,16 +142,6 @@ static inline bool __populate_fault_info(struct kvm_vcpu *vcpu) · 87 unmodified lines | |||
| 143 | return __get_fault_info(vcpu->arch.fault.esr_el2, &vcpu->arch.fault); | 142 | return __get_fault_info(vcpu->arch.fault.esr_el2, &vcpu->arch.fault); |
| 144 | } | 143 | } |
| 145 | 144 | ||
| 146 | static inline void __hyp_sve_save_host(struct kvm_vcpu *vcpu) | ||
| 147 | { | ||
| 148 | struct thread_struct *thread; | ||
| 149 | |||
| 150 | thread = container_of(vcpu->arch.host_fpsimd_state, struct thread_struct, | ||
| 151 | uw.fpsimd_state); | ||
| 152 | |||
| 153 | __sve_save_state(sve_pffr(thread), &vcpu->arch.host_fpsimd_state->fpsr); | ||
| 154 | } | ||
| 155 | |||
| 156 | static inline void __hyp_sve_restore_guest(struct kvm_vcpu *vcpu) | 145 | static inline void __hyp_sve_restore_guest(struct kvm_vcpu *vcpu) |
| 157 | { | 146 | { |
| 158 | sve_cond_update_zcr_vq(vcpu_sve_max_vq(vcpu) - 1, SYS_ZCR_EL2); | 147 | sve_cond_update_zcr_vq(vcpu_sve_max_vq(vcpu) - 1, SYS_ZCR_EL2); |
| @@ -169,21 +158,14 @@ static inline void __hyp_sve_restore_guest(struct kvm_vcpu *vcpu) · 10 unmodified lines | |||
| 169 | */ | 158 | */ |
| 170 | static bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) | 159 | static bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) |
| 171 | { | 160 | { |
| 172 | bool sve_guest, sve_host; | 161 | bool sve_guest; |
| 173 | u8 esr_ec; | 162 | u8 esr_ec; |
| 174 | u64 reg; | 163 | u64 reg; |
| 175 | 164 | ||
| 176 | if (!system_supports_fpsimd()) | 165 | if (!system_supports_fpsimd()) |
| 177 | return false; | 166 | return false; |
| 178 | 167 | ||
| 179 | if (system_supports_sve()) { | 168 | sve_guest = vcpu_has_sve(vcpu); |
| 180 | sve_guest = vcpu_has_sve(vcpu); | ||
| 181 | sve_host = vcpu->arch.flags & KVM_ARM64_HOST_SVE_IN_USE; | ||
| 182 | } else { | ||
| 183 | sve_guest = false; | ||
| 184 | sve_host = false; | ||
| 185 | } | ||
| 186 | |||
| 187 | esr_ec = kvm_vcpu_trap_get_class(vcpu); | 169 | esr_ec = kvm_vcpu_trap_get_class(vcpu); |
| 188 | 170 | ||
| 189 | /* Don't handle SVE traps for non-SVE vcpus here: */ | 171 | /* Don't handle SVE traps for non-SVE vcpus here: */ |
| @@ -207,11 +189,7 @@ static bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) · 17 unmodified lines | |||
| 207 | isb(); | 189 | isb(); |
| 208 | 190 | ||
| 209 | if (vcpu->arch.flags & KVM_ARM64_FP_HOST) { | 191 | if (vcpu->arch.flags & KVM_ARM64_FP_HOST) { |
| 210 | if (sve_host) | 192 | __fpsimd_save_state(vcpu->arch.host_fpsimd_state); |
| 211 | __hyp_sve_save_host(vcpu); | ||
| 212 | else | ||
| 213 | __fpsimd_save_state(vcpu->arch.host_fpsimd_state); | ||
| 214 | |||
| 215 | vcpu->arch.flags &= ~KVM_ARM64_FP_HOST; | 193 | vcpu->arch.flags &= ~KVM_ARM64_FP_HOST; |
| 216 | } | 194 | } |
| 217 | 195 | ||
| @@ -24,6 +24,11 @@ enum pkvm_page_state { · 23 unmodified lines | |||
| 24 | PKVM_PAGE_OWNED = 0ULL, | 24 | PKVM_PAGE_OWNED = 0ULL, |
| 25 | PKVM_PAGE_SHARED_OWNED = KVM_PGTABLE_PROT_SW0, | 25 | PKVM_PAGE_SHARED_OWNED = KVM_PGTABLE_PROT_SW0, |
| 26 | PKVM_PAGE_SHARED_BORROWED = KVM_PGTABLE_PROT_SW1, | 26 | PKVM_PAGE_SHARED_BORROWED = KVM_PGTABLE_PROT_SW1, |
| 27 | __PKVM_PAGE_RESERVED = KVM_PGTABLE_PROT_SW0 | | ||
| 28 | KVM_PGTABLE_PROT_SW1, | ||
| 29 | |||
| 30 | /* Meta-states which aren't encoded directly in the PTE's SW bits */ | ||
| 31 | PKVM_NOPAGE, | ||
| 27 | }; | 32 | }; |
| 28 | 33 | ||
| 29 | #define PKVM_PAGE_STATE_PROT_MASK (KVM_PGTABLE_PROT_SW0 | KVM_PGTABLE_PROT_SW1) | 34 | #define PKVM_PAGE_STATE_PROT_MASK (KVM_PGTABLE_PROT_SW0 | KVM_PGTABLE_PROT_SW1) |
| @@ -50,6 +55,7 @@ extern const u8 pkvm_hyp_id; · 20 unmodified lines | |||
| 50 | 55 | ||
| 51 | int __pkvm_prot_finalize(void); | 56 | int __pkvm_prot_finalize(void); |
| 52 | int __pkvm_host_share_hyp(u64 pfn); | 57 | int __pkvm_host_share_hyp(u64 pfn); |
| 58 | int __pkvm_host_unshare_hyp(u64 pfn); | ||
| 53 | 59 | ||
| 54 | bool addr_is_memory(phys_addr_t phys); | 60 | bool addr_is_memory(phys_addr_t phys); |
| 55 | int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot prot); | 61 | int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot prot); |
| @@ -10,13 +10,8 @@ · 9 unmodified lines | |||
| 10 | #include <nvhe/memory.h> | 10 | #include <nvhe/memory.h> |
| 11 | #include <nvhe/spinlock.h> | 11 | #include <nvhe/spinlock.h> |
| 12 | 12 | ||
| 13 | #define HYP_MEMBLOCK_REGIONS 128 | ||
| 14 | extern struct memblock_region kvm_nvhe_sym(hyp_memory)[]; | ||
| 15 | extern unsigned int kvm_nvhe_sym(hyp_memblock_nr); | ||
| 16 | extern struct kvm_pgtable pkvm_pgtable; | 13 | extern struct kvm_pgtable pkvm_pgtable; |
| 17 | extern hyp_spinlock_t pkvm_pgd_lock; | 14 | extern hyp_spinlock_t pkvm_pgd_lock; |
| 18 | extern struct hyp_pool hpool; | ||
| 19 | extern u64 __io_map_base; | ||
| 20 | 15 | ||
| 21 | int hyp_create_idmap(u32 hyp_va_bits); | 16 | int hyp_create_idmap(u32 hyp_va_bits); |
| 22 | int hyp_map_vectors(void); | 17 | int hyp_map_vectors(void); |
| @@ -39,58 +34,4 @@ static inline void hyp_vmemmap_range(phys_addr_t phys, unsigned long size, · 16 unmodified lines | |||
| 39 | *end = ALIGN(*end, PAGE_SIZE); | 34 | *end = ALIGN(*end, PAGE_SIZE); |
| 40 | } | 35 | } |
| 41 | 36 | ||
| 42 | static inline unsigned long __hyp_pgtable_max_pages(unsigned long nr_pages) | ||
| 43 | { | ||
| 44 | unsigned long total = 0, i; | ||
| 45 | |||
| 46 | /* Provision the worst case scenario */ | ||
| 47 | for (i = 0; i < KVM_PGTABLE_MAX_LEVELS; i++) { | ||
| 48 | nr_pages = DIV_ROUND_UP(nr_pages, PTRS_PER_PTE); | ||
| 49 | total += nr_pages; | ||
| 50 | } | ||
| 51 | |||
| 52 | return total; | ||
| 53 | } | ||
| 54 | |||
| 55 | static inline unsigned long __hyp_pgtable_total_pages(void) | ||
| 56 | { | ||
| 57 | unsigned long res = 0, i; | ||
| 58 | |||
| 59 | /* Cover all of memory with page-granularity */ | ||
| 60 | for (i = 0; i < kvm_nvhe_sym(hyp_memblock_nr); i++) { | ||
| 61 | struct memblock_region *reg = &kvm_nvhe_sym(hyp_memory)[i]; | ||
| 62 | res += __hyp_pgtable_max_pages(reg->size >> PAGE_SHIFT); | ||
| 63 | } | ||
| 64 | |||
| 65 | return res; | ||
| 66 | } | ||
| 67 | |||
| 68 | static inline unsigned long hyp_s1_pgtable_pages(void) | ||
| 69 | { | ||
| 70 | unsigned long res; | ||
| 71 | |||
| 72 | res = __hyp_pgtable_total_pages(); | ||
| 73 | |||
| 74 | /* Allow 1 GiB for private mappings */ | ||
| 75 | res += __hyp_pgtable_max_pages(SZ_1G >> PAGE_SHIFT); | ||
| 76 | |||
| 77 | return res; | ||
| 78 | } | ||
| 79 | |||
| 80 | static inline unsigned long host_s2_pgtable_pages(void) | ||
| 81 | { | ||
| 82 | unsigned long res; | ||
| 83 | |||
| 84 | /* | ||
| 85 | * Include an extra 16 pages to safely upper-bound the worst case of | ||
| 86 | * concatenated pgds. | ||
| 87 | */ | ||
| 88 | res = __hyp_pgtable_total_pages() + 16; | ||
| 89 | |||
| 90 | /* Allow 1 GiB for MMIO mappings */ | ||
| 91 | res += __hyp_pgtable_max_pages(SZ_1G >> PAGE_SHIFT); | ||
| 92 | |||
| 93 | return res; | ||
| 94 | } | ||
| 95 | |||
| 96 | #endif /* __KVM_HYP_MM_H */ | 37 | #endif /* __KVM_HYP_MM_H */ |
| @@ -43,6 +43,9 @@ void *hyp_early_alloc_page(void *arg) · 42 unmodified lines | |||
| 43 | return hyp_early_alloc_contig(1); | 43 | return hyp_early_alloc_contig(1); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | static void hyp_early_alloc_get_page(void *addr) { } | ||
| 47 | static void hyp_early_alloc_put_page(void *addr) { } | ||
| 48 | |||
| 46 | void hyp_early_alloc_init(void *virt, unsigned long size) | 49 | void hyp_early_alloc_init(void *virt, unsigned long size) |
| 47 | { | 50 | { |
| 48 | base = cur = (unsigned long)virt; | 51 | base = cur = (unsigned long)virt; |
| @@ -51,4 +54,6 @@ void hyp_early_alloc_init(void *virt, unsigned long size) · 2 unmodified lines | |||
| 51 | hyp_early_alloc_mm_ops.zalloc_page = hyp_early_alloc_page; | 54 | hyp_early_alloc_mm_ops.zalloc_page = hyp_early_alloc_page; |
| 52 | hyp_early_alloc_mm_ops.phys_to_virt = hyp_phys_to_virt; | 55 | hyp_early_alloc_mm_ops.phys_to_virt = hyp_phys_to_virt; |
| 53 | hyp_early_alloc_mm_ops.virt_to_phys = hyp_virt_to_phys; | 56 | hyp_early_alloc_mm_ops.virt_to_phys = hyp_virt_to_phys; |
| 57 | hyp_early_alloc_mm_ops.get_page = hyp_early_alloc_get_page; | ||
| 58 | hyp_early_alloc_mm_ops.put_page = hyp_early_alloc_put_page; | ||
| 54 | } | 59 | } |
| @@ -147,6 +147,13 @@ static void handle___pkvm_host_share_hyp(struct kvm_cpu_context *host_ctxt) · 146 unmodified lines | |||
| 147 | cpu_reg(host_ctxt, 1) = __pkvm_host_share_hyp(pfn); | 147 | cpu_reg(host_ctxt, 1) = __pkvm_host_share_hyp(pfn); |
| 148 | } | 148 | } |
| 149 | 149 | ||
| 150 | static void handle___pkvm_host_unshare_hyp(struct kvm_cpu_context *host_ctxt) | ||
| 151 | { | ||
| 152 | DECLARE_REG(u64, pfn, host_ctxt, 1); | ||
| 153 | |||
| 154 | cpu_reg(host_ctxt, 1) = __pkvm_host_unshare_hyp(pfn); | ||
| 155 | } | ||
| 156 | |||
| 150 | static void handle___pkvm_create_private_mapping(struct kvm_cpu_context *host_ctxt) | 157 | static void handle___pkvm_create_private_mapping(struct kvm_cpu_context *host_ctxt) |
| 151 | { | 158 | { |
| 152 | DECLARE_REG(phys_addr_t, phys, host_ctxt, 1); | 159 | DECLARE_REG(phys_addr_t, phys, host_ctxt, 1); |
| @@ -184,6 +191,7 @@ static const hcall_t host_hcall[] = { · 31 unmodified lines | |||
| 184 | HANDLE_FUNC(__pkvm_prot_finalize), | 191 | HANDLE_FUNC(__pkvm_prot_finalize), |
| 185 | 192 | ||
| 186 | HANDLE_FUNC(__pkvm_host_share_hyp), | 193 | HANDLE_FUNC(__pkvm_host_share_hyp), |
| 194 | HANDLE_FUNC(__pkvm_host_unshare_hyp), | ||
| 187 | HANDLE_FUNC(__kvm_adjust_pc), | 195 | HANDLE_FUNC(__kvm_adjust_pc), |
| 188 | HANDLE_FUNC(__kvm_vcpu_run), | 196 | HANDLE_FUNC(__kvm_vcpu_run), |
| 189 | HANDLE_FUNC(__kvm_flush_vm_context), | 197 | HANDLE_FUNC(__kvm_flush_vm_context), |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | |||
| 9 | #include <asm/kvm_hyp.h> | 9 | #include <asm/kvm_hyp.h> |
| 10 | #include <asm/kvm_mmu.h> | 10 | #include <asm/kvm_mmu.h> |
| 11 | #include <asm/kvm_pgtable.h> | 11 | #include <asm/kvm_pgtable.h> |
| 12 | #include <asm/kvm_pkvm.h> | ||
| 12 | #include <asm/stage2_pgtable.h> | 13 | #include <asm/stage2_pgtable.h> |
| 13 | 14 | ||
| 14 | #include <hyp/fault.h> | 15 | #include <hyp/fault.h> |
| @@ -27,6 +28,26 @@ static struct hyp_pool host_s2_pool; · 12 unmodified lines | |||
| 27 | 28 | ||
| 28 | const u8 pkvm_hyp_id = 1; | 29 | const u8 pkvm_hyp_id = 1; |
| 29 | 30 | ||
| 31 | static void host_lock_component(void) | ||
| 32 | { | ||
| 33 | hyp_spin_lock(&host_kvm.lock); | ||
| 34 | } | ||
| 35 | |||
| 36 | static void host_unlock_component(void) | ||
| 37 | { | ||
| 38 | hyp_spin_unlock(&host_kvm.lock); | ||
| 39 | } | ||
| 40 | |||
| 41 | static void hyp_lock_component(void) | ||
| 42 | { | ||
| 43 | hyp_spin_lock(&pkvm_pgd_lock); | ||
| 44 | } | ||
| 45 | |||
| 46 | static void hyp_unlock_component(void) | ||
| 47 | { | ||
| 48 | hyp_spin_unlock(&pkvm_pgd_lock); | ||
| 49 | } | ||
| 50 | |||
| 30 | static void *host_s2_zalloc_pages_exact(size_t size) | 51 | static void *host_s2_zalloc_pages_exact(size_t size) |
| 31 | { | 52 | { |
| 32 | void *addr = hyp_alloc_pages(&host_s2_pool, get_order(size)); | 53 | void *addr = hyp_alloc_pages(&host_s2_pool, get_order(size)); |
| @@ -103,19 +124,19 @@ int kvm_host_prepare_stage2(void *pgt_pool_base) · 70 unmodified lines | |||
| 103 | 124 | ||
| 104 | prepare_host_vtcr(); | 125 | prepare_host_vtcr(); |
| 105 | hyp_spin_lock_init(&host_kvm.lock); | 126 | hyp_spin_lock_init(&host_kvm.lock); |
| 127 | mmu->arch = &host_kvm.arch; | ||
| 106 | 128 | ||
| 107 | ret = prepare_s2_pool(pgt_pool_base); | 129 | ret = prepare_s2_pool(pgt_pool_base); |
| 108 | if (ret) | 130 | if (ret) |
| 109 | return ret; | 131 | return ret; |
| 110 | 132 | ||
| 111 | ret = __kvm_pgtable_stage2_init(&host_kvm.pgt, &host_kvm.arch, | 133 | ret = __kvm_pgtable_stage2_init(&host_kvm.pgt, mmu, |
| 112 | &host_kvm.mm_ops, KVM_HOST_S2_FLAGS, | 134 | &host_kvm.mm_ops, KVM_HOST_S2_FLAGS, |
| 113 | host_stage2_force_pte_cb); | 135 | host_stage2_force_pte_cb); |
| 114 | if (ret) | 136 | if (ret) |
| 115 | return ret; | 137 | return ret; |
| 116 | 138 | ||
| 117 | mmu->pgd_phys = __hyp_pa(host_kvm.pgt.pgd); | 139 | mmu->pgd_phys = __hyp_pa(host_kvm.pgt.pgd); |
| 118 | mmu->arch = &host_kvm.arch; | ||
| 119 | mmu->pgt = &host_kvm.pgt; | 140 | mmu->pgt = &host_kvm.pgt; |
| 120 | WRITE_ONCE(mmu->vmid.vmid_gen, 0); | 141 | WRITE_ONCE(mmu->vmid.vmid_gen, 0); |
| 121 | WRITE_ONCE(mmu->vmid.vmid, 0); | 142 | WRITE_ONCE(mmu->vmid.vmid, 0); |
| @@ -338,116 +359,446 @@ static int host_stage2_idmap(u64 addr) · 216 unmodified lines | |||
| 338 | 359 | ||
| 339 | prot = is_memory ? PKVM_HOST_MEM_PROT : PKVM_HOST_MMIO_PROT; | 360 | prot = is_memory ? PKVM_HOST_MEM_PROT : PKVM_HOST_MMIO_PROT; |
| 340 | 361 | ||
| 341 | hyp_spin_lock(&host_kvm.lock); | 362 | host_lock_component(); |
| 342 | ret = host_stage2_adjust_range(addr, &range); | 363 | ret = host_stage2_adjust_range(addr, &range); |
| 343 | if (ret) | 364 | if (ret) |
| 344 | goto unlock; | 365 | goto unlock; |
| 345 | 366 | ||
| 346 | ret = host_stage2_idmap_locked(range.start, range.end - range.start, prot); | 367 | ret = host_stage2_idmap_locked(range.start, range.end - range.start, prot); |
| 347 | unlock: | 368 | unlock: |
| 348 | hyp_spin_unlock(&host_kvm.lock); | 369 | host_unlock_component(); |
| 349 | 370 | ||
| 350 | return ret; | 371 | return ret; |
| 351 | } | 372 | } |
| 352 | 373 | ||
| 353 | static inline bool check_prot(enum kvm_pgtable_prot prot, | 374 | void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt) |
| 354 | enum kvm_pgtable_prot required, | ||
| 355 | enum kvm_pgtable_prot denied) | ||
| 356 | { | 375 | { |
| 357 | return (prot & (required | denied)) == required; | 376 | struct kvm_vcpu_fault_info fault; |
| 377 | u64 esr, addr; | ||
| 378 | int ret = 0; | ||
| 379 | |||
| 380 | esr = read_sysreg_el2(SYS_ESR); | ||
| 381 | BUG_ON(!__get_fault_info(esr, &fault)); | ||
| 382 | |||
| 383 | addr = (fault.hpfar_el2 & HPFAR_MASK) << 8; | ||
| 384 | ret = host_stage2_idmap(addr); | ||
| 385 | BUG_ON(ret && ret != -EAGAIN); | ||
| 358 | } | 386 | } |
| 359 | 387 | ||
| 360 | int __pkvm_host_share_hyp(u64 pfn) | 388 | /* This corresponds to locking order */ |
| 389 | enum pkvm_component_id { | ||
| 390 | PKVM_ID_HOST, | ||
| 391 | PKVM_ID_HYP, | ||
| 392 | }; | ||
| 393 | |||
| 394 | struct pkvm_mem_transition { | ||
| 395 | u64 nr_pages; | ||
| 396 | |||
| 397 | struct { | ||
| 398 | enum pkvm_component_id id; | ||
| 399 | /* Address in the initiator's address space */ | ||
| 400 | u64 addr; | ||
| 401 | |||
| 402 | union { | ||
| 403 | struct { | ||
| 404 | /* Address in the completer's address space */ | ||
| 405 | u64 completer_addr; | ||
| 406 | } host; | ||
| 407 | }; | ||
| 408 | } initiator; | ||
| 409 | |||
| 410 | struct { | ||
| 411 | enum pkvm_component_id id; | ||
| 412 | } completer; | ||
| 413 | }; | ||
| 414 | |||
| 415 | struct pkvm_mem_share { | ||
| 416 | const struct pkvm_mem_transition tx; | ||
| 417 | const enum kvm_pgtable_prot completer_prot; | ||
| 418 | }; | ||
| 419 | |||
| 420 | struct check_walk_data { | ||
| 421 | enum pkvm_page_state desired; | ||
| 422 | enum pkvm_page_state (*get_page_state)(kvm_pte_t pte); | ||
| 423 | }; | ||
| 424 | |||
| 425 | static int __check_page_state_visitor(u64 addr, u64 end, u32 level, | ||
| 426 | kvm_pte_t *ptep, | ||
| 427 | enum kvm_pgtable_walk_flags flag, | ||
| 428 | void * const arg) | ||
| 361 | { | 429 | { |
| 362 | phys_addr_t addr = hyp_pfn_to_phys(pfn); | 430 | struct check_walk_data *d = arg; |
| 363 | enum kvm_pgtable_prot prot, cur; | 431 | kvm_pte_t pte = *ptep; |
| 364 | void *virt = __hyp_va(addr); | ||
| 365 | enum pkvm_page_state state; | ||
| 366 | kvm_pte_t pte; | ||
| 367 | int ret; | ||
| 368 | 432 | ||
| 369 | if (!addr_is_memory(addr)) | 433 | if (kvm_pte_valid(pte) && !addr_is_memory(kvm_pte_to_phys(pte))) |
| 370 | return -EINVAL; | 434 | return -EINVAL; |
| 371 | 435 | ||
| 372 | hyp_spin_lock(&host_kvm.lock); | 436 | return d->get_page_state(pte) == d->desired ? 0 : -EPERM; |
| 373 | hyp_spin_lock(&pkvm_pgd_lock); | 437 | } |
| 438 | |||
| 439 | static int check_page_state_range(struct kvm_pgtable *pgt, u64 addr, u64 size, | ||
| 440 | struct check_walk_data *data) | ||
| 441 | { | ||
| 442 | struct kvm_pgtable_walker walker = { | ||
| 443 | .cb = __check_page_state_visitor, | ||
| 444 | .arg = data, | ||
| 445 | .flags = KVM_PGTABLE_WALK_LEAF, | ||
| 446 | }; | ||
| 447 | |||
| 448 | return kvm_pgtable_walk(pgt, addr, size, &walker); | ||
| 449 | } | ||
| 450 | |||
| 451 | static enum pkvm_page_state host_get_page_state(kvm_pte_t pte) | ||
| 452 | { | ||
| 453 | if (!kvm_pte_valid(pte) && pte) | ||
| 454 | return PKVM_NOPAGE; | ||
| 455 | |||
| 456 | return pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte)); | ||
| 457 | } | ||
| 458 | |||
| 459 | static int __host_check_page_state_range(u64 addr, u64 size, | ||
| 460 | enum pkvm_page_state state) | ||
| 461 | { | ||
| 462 | struct check_walk_data d = { | ||
| 463 | .desired = state, | ||
| 464 | .get_page_state = host_get_page_state, | ||
| 465 | }; | ||
| 466 | |||
| 467 | hyp_assert_lock_held(&host_kvm.lock); | ||
| 468 | return check_page_state_range(&host_kvm.pgt, addr, size, &d); | ||
| 469 | } | ||
| 470 | |||
| 471 | static int __host_set_page_state_range(u64 addr, u64 size, | ||
| 472 | enum pkvm_page_state state) | ||
| 473 | { | ||
| 474 | enum kvm_pgtable_prot prot = pkvm_mkstate(PKVM_HOST_MEM_PROT, state); | ||
| 475 | |||
| 476 | return host_stage2_idmap_locked(addr, size, prot); | ||
| 477 | } | ||
| 478 | |||
| 479 | static int host_request_owned_transition(u64 *completer_addr, | ||
| 480 | const struct pkvm_mem_transition *tx) | ||
| 481 | { | ||
| 482 | u64 size = tx->nr_pages * PAGE_SIZE; | ||
| 483 | u64 addr = tx->initiator.addr; | ||
| 484 | |||
| 485 | *completer_addr = tx->initiator.host.completer_addr; | ||
| 486 | return __host_check_page_state_range(addr, size, PKVM_PAGE_OWNED); | ||
| 487 | } | ||
| 488 | |||
| 489 | static int host_request_unshare(u64 *completer_addr, | ||
| 490 | const struct pkvm_mem_transition *tx) | ||
| 491 | { | ||
| 492 | u64 size = tx->nr_pages * PAGE_SIZE; | ||
| 493 | u64 addr = tx->initiator.addr; | ||
| 494 | |||
| 495 | *completer_addr = tx->initiator.host.completer_addr; | ||
| 496 | return __host_check_page_state_range(addr, size, PKVM_PAGE_SHARED_OWNED); | ||
| 497 | } | ||
| 498 | |||
| 499 | static int host_initiate_share(u64 *completer_addr, | ||
| 500 | const struct pkvm_mem_transition *tx) | ||
| 501 | { | ||
| 502 | u64 size = tx->nr_pages * PAGE_SIZE; | ||
| 503 | u64 addr = tx->initiator.addr; | ||
| 504 | |||
| 505 | *completer_addr = tx->initiator.host.completer_addr; | ||
| 506 | return __host_set_page_state_range(addr, size, PKVM_PAGE_SHARED_OWNED); | ||
| 507 | } | ||
| 508 | |||
| 509 | static int host_initiate_unshare(u64 *completer_addr, | ||
| 510 | const struct pkvm_mem_transition *tx) | ||
| 511 | { | ||
| 512 | u64 size = tx->nr_pages * PAGE_SIZE; | ||
| 513 | u64 addr = tx->initiator.addr; | ||
| 514 | |||
| 515 | *completer_addr = tx->initiator.host.completer_addr; | ||
| 516 | return __host_set_page_state_range(addr, size, PKVM_PAGE_OWNED); | ||
| 517 | } | ||
| 518 | |||
| 519 | static enum pkvm_page_state hyp_get_page_state(kvm_pte_t pte) | ||
| 520 | { | ||
| 521 | if (!kvm_pte_valid(pte)) | ||
| 522 | return PKVM_NOPAGE; | ||
| 523 | |||
| 524 | return pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte)); | ||
| 525 | } | ||
| 526 | |||
| 527 | static int __hyp_check_page_state_range(u64 addr, u64 size, | ||
| 528 | enum pkvm_page_state state) | ||
| 529 | { | ||
| 530 | struct check_walk_data d = { | ||
| 531 | .desired = state, | ||
| 532 | .get_page_state = hyp_get_page_state, | ||
| 533 | }; | ||
| 534 | |||
| 535 | hyp_assert_lock_held(&pkvm_pgd_lock); | ||
| 536 | return check_page_state_range(&pkvm_pgtable, addr, size, &d); | ||
| 537 | } | ||
| 538 | |||
| 539 | static bool __hyp_ack_skip_pgtable_check(const struct pkvm_mem_transition *tx) | ||
| 540 | { | ||
| 541 | return !(IS_ENABLED(CONFIG_NVHE_EL2_DEBUG) || | ||
| 542 | tx->initiator.id != PKVM_ID_HOST); | ||
| 543 | } | ||
| 544 | |||
| 545 | static int hyp_ack_share(u64 addr, const struct pkvm_mem_transition *tx, | ||
| 546 | enum kvm_pgtable_prot perms) | ||
| 547 | { | ||
| 548 | u64 size = tx->nr_pages * PAGE_SIZE; | ||
| 549 | |||
| 550 | if (perms != PAGE_HYP) | ||
| 551 | return -EPERM; | ||
| 552 | |||
| 553 | if (__hyp_ack_skip_pgtable_check(tx)) | ||
| 554 | return 0; | ||
| 555 | |||
| 556 | return __hyp_check_page_state_range(addr, size, PKVM_NOPAGE); | ||
| 557 | } | ||
| 558 | |||
| 559 | static int hyp_ack_unshare(u64 addr, const struct pkvm_mem_transition *tx) | ||
| 560 | { | ||
| 561 | u64 size = tx->nr_pages * PAGE_SIZE; | ||
| 562 | |||
| 563 | if (__hyp_ack_skip_pgtable_check(tx)) | ||
| 564 | return 0; | ||
| 565 | |||
| 566 | return __hyp_check_page_state_range(addr, size, | ||
| 567 | PKVM_PAGE_SHARED_BORROWED); | ||
| 568 | } | ||
| 569 | |||
| 570 | static int hyp_complete_share(u64 addr, const struct pkvm_mem_transition *tx, | ||
| 571 | enum kvm_pgtable_prot perms) | ||
| 572 | { | ||
| 573 | void *start = (void *)addr, *end = start + (tx->nr_pages * PAGE_SIZE); | ||
| 574 | enum kvm_pgtable_prot prot; | ||
| 575 | |||
| 576 | prot = pkvm_mkstate(perms, PKVM_PAGE_SHARED_BORROWED); | ||
| 577 | return pkvm_create_mappings_locked(start, end, prot); | ||
| 578 | } | ||
| 579 | |||
| 580 | static int hyp_complete_unshare(u64 addr, const struct pkvm_mem_transition *tx) | ||
| 581 | { | ||
| 582 | u64 size = tx->nr_pages * PAGE_SIZE; | ||
| 583 | int ret = kvm_pgtable_hyp_unmap(&pkvm_pgtable, addr, size); | ||
| 584 | |||
| 585 | return (ret != size) ? -EFAULT : 0; | ||
| 586 | } | ||
| 587 | |||
| 588 | static int check_share(struct pkvm_mem_share *share) | ||
| 589 | { | ||
| 590 | const struct pkvm_mem_transition *tx = &share->tx; | ||
| 591 | u64 completer_addr; | ||
| 592 | int ret; | ||
| 593 | |||
| 594 | switch (tx->initiator.id) { | ||
| 595 | case PKVM_ID_HOST: | ||
| 596 | ret = host_request_owned_transition(&completer_addr, tx); | ||
| 597 | break; | ||
| 598 | default: | ||
| 599 | ret = -EINVAL; | ||
| 600 | } | ||
| 374 | 601 | ||
| 375 | ret = kvm_pgtable_get_leaf(&host_kvm.pgt, addr, &pte, NULL); | ||
| 376 | if (ret) | 602 | if (ret) |
| 377 | goto unlock; | 603 | return ret; |
| 378 | if (!pte) | ||
| 379 | goto map_shared; | ||
| 380 | 604 | ||
| 381 | /* | 605 | switch (tx->completer.id) { |
| 382 | * Check attributes in the host stage-2 PTE. We need the page to be: | 606 | case PKVM_ID_HYP: |
| 383 | * - mapped RWX as we're sharing memory; | 607 | ret = hyp_ack_share(completer_addr, tx, share->completer_prot); |
| 384 | * - not borrowed, as that implies absence of ownership. | 608 | break; |
| 385 | * Otherwise, we can't let it got through | 609 | default: |
| 386 | */ | 610 | ret = -EINVAL; |
| 387 | cur = kvm_pgtable_stage2_pte_prot(pte); | ||
| 388 | prot = pkvm_mkstate(0, PKVM_PAGE_SHARED_BORROWED); | ||
| 389 | if (!check_prot(cur, PKVM_HOST_MEM_PROT, prot)) { | ||
| 390 | ret = -EPERM; | ||
| 391 | goto unlock; | ||
| 392 | } | 611 | } |
| 393 | 612 | ||
| 394 | state = pkvm_getstate(cur); | 613 | return ret; |
| 395 | if (state == PKVM_PAGE_OWNED) | 614 | } |
| 396 | goto map_shared; | ||
| 397 | 615 | ||
| 398 | /* | 616 | static int __do_share(struct pkvm_mem_share *share) |
| 399 | * Tolerate double-sharing the same page, but this requires | 617 | { |
| 400 | * cross-checking the hypervisor stage-1. | 618 | const struct pkvm_mem_transition *tx = &share->tx; |
| 401 | */ | 619 | u64 completer_addr; |
| 402 | if (state != PKVM_PAGE_SHARED_OWNED) { | 620 | int ret; |
| 403 | ret = -EPERM; | 621 | |
| 404 | goto unlock; | 622 | switch (tx->initiator.id) { |
| 623 | case PKVM_ID_HOST: | ||
| 624 | ret = host_initiate_share(&completer_addr, tx); | ||
| 625 | break; | ||
| 626 | default: | ||
| 627 | ret = -EINVAL; | ||
| 405 | } | 628 | } |
| 406 | 629 | ||
| 407 | ret = kvm_pgtable_get_leaf(&pkvm_pgtable, (u64)virt, &pte, NULL); | ||
| 408 | if (ret) | 630 | if (ret) |
| 409 | goto unlock; | 631 | return ret; |
| 410 | 632 | ||
| 411 | /* | 633 | switch (tx->completer.id) { |
| 412 | * If the page has been shared with the hypervisor, it must be | 634 | case PKVM_ID_HYP: |
| 413 | * already mapped as SHARED_BORROWED in its stage-1. | 635 | ret = hyp_complete_share(completer_addr, tx, share->completer_prot); |
| 414 | */ | 636 | break; |
| 415 | cur = kvm_pgtable_hyp_pte_prot(pte); | 637 | default: |
| 416 | prot = pkvm_mkstate(PAGE_HYP, PKVM_PAGE_SHARED_BORROWED); | 638 | ret = -EINVAL; |
| 417 | if (!check_prot(cur, prot, ~prot)) | 639 | } |
| 418 | ret = -EPERM; | ||
| 419 | goto unlock; | ||
| 420 | 640 | ||
| 421 | map_shared: | 641 | return ret; |
| 422 | /* | 642 | } |
| 423 | * If the page is not yet shared, adjust mappings in both page-tables | 643 | |
| 424 | * while both locks are held. | 644 | /* |
| 425 | */ | 645 | * do_share(): |
| 426 | prot = pkvm_mkstate(PAGE_HYP, PKVM_PAGE_SHARED_BORROWED); | 646 | * |
| 427 | ret = pkvm_create_mappings_locked(virt, virt + PAGE_SIZE, prot); | 647 | * The page owner grants access to another component with a given set |
| 428 | BUG_ON(ret); | 648 | * of permissions. |
| 649 | * | ||
| 650 | * Initiator: OWNED => SHARED_OWNED | ||
| 651 | * Completer: NOPAGE => SHARED_BORROWED | ||
| 652 | */ | ||
| 653 | static int do_share(struct pkvm_mem_share *share) | ||
| 654 | { | ||
| 655 | int ret; | ||
| 429 | 656 | ||
| 430 | prot = pkvm_mkstate(PKVM_HOST_MEM_PROT, PKVM_PAGE_SHARED_OWNED); | 657 | ret = check_share(share); |
| 431 | ret = host_stage2_idmap_locked(addr, PAGE_SIZE, prot); | 658 | if (ret) |
| 432 | BUG_ON(ret); | 659 | return ret; |
| 433 | 660 | ||
| 434 | unlock: | 661 | return WARN_ON(__do_share(share)); |
| 435 | hyp_spin_unlock(&pkvm_pgd_lock); | 662 | } |
| 436 | hyp_spin_unlock(&host_kvm.lock); | 663 | |
| 664 | static int check_unshare(struct pkvm_mem_share *share) | ||
| 665 | { | ||
| 666 | const struct pkvm_mem_transition *tx = &share->tx; | ||
| 667 | u64 completer_addr; | ||
| 668 | int ret; | ||
| 669 | |||
| 670 | switch (tx->initiator.id) { | ||
| 671 | case PKVM_ID_HOST: | ||
| 672 | ret = host_request_unshare(&completer_addr, tx); | ||
| 673 | break; | ||
| 674 | default: | ||
| 675 | ret = -EINVAL; | ||
| 676 | } | ||
| 677 | |||
| 678 | if (ret) | ||
| 679 | return ret; | ||
| 680 | |||
| 681 | switch (tx->completer.id) { | ||
| 682 | case PKVM_ID_HYP: | ||
| 683 | ret = hyp_ack_unshare(completer_addr, tx); | ||
| 684 | break; | ||
| 685 | default: | ||
| 686 | ret = -EINVAL; | ||
| 687 | } | ||
| 437 | 688 | ||
| 438 | return ret; | 689 | return ret; |
| 439 | } | 690 | } |
| 440 | 691 | ||
| 441 | void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt) | 692 | static int __do_unshare(struct pkvm_mem_share *share) |
| 442 | { | 693 | { |
| 443 | struct kvm_vcpu_fault_info fault; | 694 | const struct pkvm_mem_transition *tx = &share->tx; |
| 444 | u64 esr, addr; | 695 | u64 completer_addr; |
| 445 | int ret = 0; | 696 | int ret; |
| 446 | 697 | ||
| 447 | esr = read_sysreg_el2(SYS_ESR); | 698 | switch (tx->initiator.id) { |
| 448 | BUG_ON(!__get_fault_info(esr, &fault)); | 699 | case PKVM_ID_HOST: |
| 700 | ret = host_initiate_unshare(&completer_addr, tx); | ||
| 701 | break; | ||
| 702 | default: | ||
| 703 | ret = -EINVAL; | ||
| 704 | } | ||
| 449 | 705 | ||
| 450 | addr = (fault.hpfar_el2 & HPFAR_MASK) << 8; | 706 | if (ret) |
| 451 | ret = host_stage2_idmap(addr); | 707 | return ret; |
| 452 | BUG_ON(ret && ret != -EAGAIN); | 708 | |
| 709 | switch (tx->completer.id) { | ||
| 710 | case PKVM_ID_HYP: | ||
| 711 | ret = hyp_complete_unshare(completer_addr, tx); | ||
| 712 | break; | ||
| 713 | default: | ||
| 714 | ret = -EINVAL; | ||
| 715 | } | ||
| 716 | |||
| 717 | return ret; | ||
| 718 | } | ||
| 719 | |||
| 720 | /* | ||
| 721 | * do_unshare(): | ||
| 722 | * | ||
| 723 | * The page owner revokes access from another component for a range of | ||
| 724 | * pages which were previously shared using do_share(). | ||
| 725 | * | ||
| 726 | * Initiator: SHARED_OWNED => OWNED | ||
| 727 | * Completer: SHARED_BORROWED => NOPAGE | ||
| 728 | */ | ||
| 729 | static int do_unshare(struct pkvm_mem_share *share) | ||
| 730 | { | ||
| 731 | int ret; | ||
| 732 | |||
| 733 | ret = check_unshare(share); | ||
| 734 | if (ret) | ||
| 735 | return ret; | ||
| 736 | |||
| 737 | return WARN_ON(__do_unshare(share)); | ||
| 738 | } | ||
| 739 | |||
| 740 | int __pkvm_host_share_hyp(u64 pfn) | ||
| 741 | { | ||
| 742 | int ret; | ||
| 743 | u64 host_addr = hyp_pfn_to_phys(pfn); | ||
| 744 | u64 hyp_addr = (u64)__hyp_va(host_addr); | ||
| 745 | struct pkvm_mem_share share = { | ||
| 746 | .tx = { | ||
| 747 | .nr_pages = 1, | ||
| 748 | .initiator = { | ||
| 749 | .id = PKVM_ID_HOST, | ||
| 750 | .addr = host_addr, | ||
| 751 | .host = { | ||
| 752 | .completer_addr = hyp_addr, | ||
| 753 | }, | ||
| 754 | }, | ||
| 755 | .completer = { | ||
| 756 | .id = PKVM_ID_HYP, | ||
| 757 | }, | ||
| 758 | }, | ||
| 759 | .completer_prot = PAGE_HYP, | ||
| 760 | }; | ||
| 761 | |||
| 762 | host_lock_component(); | ||
| 763 | hyp_lock_component(); | ||
| 764 | |||
| 765 | ret = do_share(&share); | ||
| 766 | |||
| 767 | hyp_unlock_component(); | ||
| 768 | host_unlock_component(); | ||
| 769 | |||
| 770 | return ret; | ||
| 771 | } | ||
| 772 | |||
| 773 | int __pkvm_host_unshare_hyp(u64 pfn) | ||
| 774 | { | ||
| 775 | int ret; | ||
| 776 | u64 host_addr = hyp_pfn_to_phys(pfn); | ||
| 777 | u64 hyp_addr = (u64)__hyp_va(host_addr); | ||
| 778 | struct pkvm_mem_share share = { | ||
| 779 | .tx = { | ||
| 780 | .nr_pages = 1, | ||
| 781 | .initiator = { | ||
| 782 | .id = PKVM_ID_HOST, | ||
| 783 | .addr = host_addr, | ||
| 784 | .host = { | ||
| 785 | .completer_addr = hyp_addr, | ||
| 786 | }, | ||
| 787 | }, | ||
| 788 | .completer = { | ||
| 789 | .id = PKVM_ID_HYP, | ||
| 790 | }, | ||
| 791 | }, | ||
| 792 | .completer_prot = PAGE_HYP, | ||
| 793 | }; | ||
| 794 | |||
| 795 | host_lock_component(); | ||
| 796 | hyp_lock_component(); | ||
| 797 | |||
| 798 | ret = do_unshare(&share); | ||
| 799 | |||
| 800 | hyp_unlock_component(); | ||
| 801 | host_unlock_component(); | ||
| 802 | |||
| 803 | return ret; | ||
| 453 | } | 804 | } |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | |||
| 8 | #include <asm/kvm_hyp.h> | 8 | #include <asm/kvm_hyp.h> |
| 9 | #include <asm/kvm_mmu.h> | 9 | #include <asm/kvm_mmu.h> |
| 10 | #include <asm/kvm_pgtable.h> | 10 | #include <asm/kvm_pgtable.h> |
| 11 | #include <asm/kvm_pkvm.h> | ||
| 11 | #include <asm/spectre.h> | 12 | #include <asm/spectre.h> |
| 12 | 13 | ||
| 13 | #include <nvhe/early_alloc.h> | 14 | #include <nvhe/early_alloc.h> |
| @@ -18,11 +19,12 @@ · 4 unmodified lines | |||
| 18 | 19 | ||
| 19 | struct kvm_pgtable pkvm_pgtable; | 20 | struct kvm_pgtable pkvm_pgtable; |
| 20 | hyp_spinlock_t pkvm_pgd_lock; | 21 | hyp_spinlock_t pkvm_pgd_lock; |
| 21 | u64 __io_map_base; | ||
| 22 | 22 | ||
| 23 | struct memblock_region hyp_memory[HYP_MEMBLOCK_REGIONS]; | 23 | struct memblock_region hyp_memory[HYP_MEMBLOCK_REGIONS]; |
| 24 | unsigned int hyp_memblock_nr; | 24 | unsigned int hyp_memblock_nr; |
| 25 | 25 | ||
| 26 | static u64 __io_map_base; | ||
| 27 | |||
| 26 | static int __pkvm_create_mappings(unsigned long start, unsigned long size, | 28 | static int __pkvm_create_mappings(unsigned long start, unsigned long size, |
| 27 | unsigned long phys, enum kvm_pgtable_prot prot) | 29 | unsigned long phys, enum kvm_pgtable_prot prot) |
| 28 | { | 30 | { |
| @@ -241,7 +241,7 @@ int hyp_pool_init(struct hyp_pool *pool, u64 pfn, unsigned int nr_pages, · 240 unmodified lines | |||
| 241 | int i; | 241 | int i; |
| 242 | 242 | ||
| 243 | hyp_spin_lock_init(&pool->lock); | 243 | hyp_spin_lock_init(&pool->lock); |
| 244 | pool->max_order = min(MAX_ORDER, get_order(nr_pages << PAGE_SHIFT)); | 244 | pool->max_order = min(MAX_ORDER, get_order((nr_pages + 1) << PAGE_SHIFT)); |
| 245 | for (i = 0; i < pool->max_order; i++) | 245 | for (i = 0; i < pool->max_order; i++) |
| 246 | INIT_LIST_HEAD(&pool->free_area[i]); | 246 | INIT_LIST_HEAD(&pool->free_area[i]); |
| 247 | pool->range_start = phys; | 247 | pool->range_start = phys; |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | |||
| 8 | #include <asm/kvm_hyp.h> | 8 | #include <asm/kvm_hyp.h> |
| 9 | #include <asm/kvm_mmu.h> | 9 | #include <asm/kvm_mmu.h> |
| 10 | #include <asm/kvm_pgtable.h> | 10 | #include <asm/kvm_pgtable.h> |
| 11 | #include <asm/kvm_pkvm.h> | ||
| 11 | 12 | ||
| 12 | #include <nvhe/early_alloc.h> | 13 | #include <nvhe/early_alloc.h> |
| 13 | #include <nvhe/fixed_config.h> | 14 | #include <nvhe/fixed_config.h> |
| @@ -17,7 +18,6 @@ · 3 unmodified lines | |||
| 17 | #include <nvhe/mm.h> | 18 | #include <nvhe/mm.h> |
| 18 | #include <nvhe/trap_handler.h> | 19 | #include <nvhe/trap_handler.h> |
| 19 | 20 | ||
| 20 | struct hyp_pool hpool; | ||
| 21 | unsigned long hyp_nr_cpus; | 21 | unsigned long hyp_nr_cpus; |
| 22 | 22 | ||
| 23 | #define hyp_percpu_size ((unsigned long)__per_cpu_end - \ | 23 | #define hyp_percpu_size ((unsigned long)__per_cpu_end - \ |
| @@ -27,6 +27,7 @@ static void *vmemmap_base; · 3 unmodified lines | |||
| 27 | static void *hyp_pgt_base; | 27 | static void *hyp_pgt_base; |
| 28 | static void *host_s2_pgt_base; | 28 | static void *host_s2_pgt_base; |
| 29 | static struct kvm_pgtable_mm_ops pkvm_pgtable_mm_ops; | 29 | static struct kvm_pgtable_mm_ops pkvm_pgtable_mm_ops; |
| 30 | static struct hyp_pool hpool; | ||
| 30 | 31 | ||
| 31 | static int divide_memory_pool(void *virt, unsigned long size) | 32 | static int divide_memory_pool(void *virt, unsigned long size) |
| 32 | { | 33 | { |
| @@ -165,6 +166,7 @@ static int finalize_host_mappings_walker(u64 addr, u64 end, u32 level, · 132 unmodified lines | |||
| 165 | enum kvm_pgtable_walk_flags flag, | 166 | enum kvm_pgtable_walk_flags flag, |
| 166 | void * const arg) | 167 | void * const arg) |
| 167 | { | 168 | { |
| 169 | struct kvm_pgtable_mm_ops *mm_ops = arg; | ||
| 168 | enum kvm_pgtable_prot prot; | 170 | enum kvm_pgtable_prot prot; |
| 169 | enum pkvm_page_state state; | 171 | enum pkvm_page_state state; |
| 170 | kvm_pte_t pte = *ptep; | 172 | kvm_pte_t pte = *ptep; |
| @@ -173,6 +175,15 @@ static int finalize_host_mappings_walker(u64 addr, u64 end, u32 level, · 2 unmodified lines | |||
| 173 | if (!kvm_pte_valid(pte)) | 175 | if (!kvm_pte_valid(pte)) |
| 174 | return 0; | 176 | return 0; |
| 175 | 177 | ||
| 178 | /* | ||
| 179 | * Fix-up the refcount for the page-table pages as the early allocator | ||
| 180 | * was unable to access the hyp_vmemmap and so the buddy allocator has | ||
| 181 | * initialised the refcount to '1'. | ||
| 182 | */ | ||
| 183 | mm_ops->get_page(ptep); | ||
| 184 | if (flag != KVM_PGTABLE_WALK_LEAF) | ||
| 185 | return 0; | ||
| 186 | |||
| 176 | if (level != (KVM_PGTABLE_MAX_LEVELS - 1)) | 187 | if (level != (KVM_PGTABLE_MAX_LEVELS - 1)) |
| 177 | return -EINVAL; | 188 | return -EINVAL; |
| 178 | 189 | ||
| @@ -205,7 +216,8 @@ static int finalize_host_mappings(void) · 26 unmodified lines | |||
| 205 | { | 216 | { |
| 206 | struct kvm_pgtable_walker walker = { | 217 | struct kvm_pgtable_walker walker = { |
| 207 | .cb = finalize_host_mappings_walker, | 218 | .cb = finalize_host_mappings_walker, |
| 208 | .flags = KVM_PGTABLE_WALK_LEAF, | 219 | .flags = KVM_PGTABLE_WALK_LEAF | KVM_PGTABLE_WALK_TABLE_POST, |
| 220 | .arg = pkvm_pgtable.mm_ops, | ||
| 209 | }; | 221 | }; |
| 210 | int i, ret; | 222 | int i, ret; |
| 211 | 223 | ||
| @@ -240,19 +252,20 @@ void __noreturn __pkvm_init_finalise(void) · 28 unmodified lines | |||
| 240 | if (ret) | 252 | if (ret) |
| 241 | goto out; | 253 | goto out; |
| 242 | 254 | ||
| 243 | ret = finalize_host_mappings(); | ||
| 244 | if (ret) | ||
| 245 | goto out; | ||
| 246 | |||
| 247 | pkvm_pgtable_mm_ops = (struct kvm_pgtable_mm_ops) { | 255 | pkvm_pgtable_mm_ops = (struct kvm_pgtable_mm_ops) { |
| 248 | .zalloc_page = hyp_zalloc_hyp_page, | 256 | .zalloc_page = hyp_zalloc_hyp_page, |
| 249 | .phys_to_virt = hyp_phys_to_virt, | 257 | .phys_to_virt = hyp_phys_to_virt, |
| 250 | .virt_to_phys = hyp_virt_to_phys, | 258 | .virt_to_phys = hyp_virt_to_phys, |
| 251 | .get_page = hpool_get_page, | 259 | .get_page = hpool_get_page, |
| 252 | .put_page = hpool_put_page, | 260 | .put_page = hpool_put_page, |
| 261 | .page_count = hyp_page_count, | ||
| 253 | }; | 262 | }; |
| 254 | pkvm_pgtable.mm_ops = &pkvm_pgtable_mm_ops; | 263 | pkvm_pgtable.mm_ops = &pkvm_pgtable_mm_ops; |
| 255 | 264 | ||
| 265 | ret = finalize_host_mappings(); | ||
| 266 | if (ret) | ||
| 267 | goto out; | ||
| 268 | |||
| 256 | out: | 269 | out: |
| 257 | /* | 270 | /* |
| 258 | * We tail-called to here from handle___pkvm_init() and will not return, | 271 | * We tail-called to here from handle___pkvm_init() and will not return, |
| @@ -25,7 +25,6 @@ · 24 unmodified lines | |||
| 25 | #include <asm/fpsimd.h> | 25 | #include <asm/fpsimd.h> |
| 26 | #include <asm/debug-monitors.h> | 26 | #include <asm/debug-monitors.h> |
| 27 | #include <asm/processor.h> | 27 | #include <asm/processor.h> |
| 28 | #include <asm/thread_info.h> | ||
| 29 | 28 | ||
| 30 | #include <nvhe/fixed_config.h> | 29 | #include <nvhe/fixed_config.h> |
| 31 | #include <nvhe/mem_protect.h> | 30 | #include <nvhe/mem_protect.h> |
| @@ -383,21 +383,6 @@ enum kvm_pgtable_prot kvm_pgtable_hyp_pte_prot(kvm_pte_t pte) · 382 unmodified lines | |||
| 383 | return prot; | 383 | return prot; |
| 384 | } | 384 | } |
| 385 | 385 | ||
| 386 | static bool hyp_pte_needs_update(kvm_pte_t old, kvm_pte_t new) | ||
| 387 | { | ||
| 388 | /* | ||
| 389 | * Tolerate KVM recreating the exact same mapping, or changing software | ||
| 390 | * bits if the existing mapping was valid. | ||
| 391 | */ | ||
| 392 | if (old == new) | ||
| 393 | return false; | ||
| 394 | |||
| 395 | if (!kvm_pte_valid(old)) | ||
| 396 | return true; | ||
| 397 | |||
| 398 | return !WARN_ON((old ^ new) & ~KVM_PTE_LEAF_ATTR_HI_SW); | ||
| 399 | } | ||
| 400 | |||
| 401 | static bool hyp_map_walker_try_leaf(u64 addr, u64 end, u32 level, | 386 | static bool hyp_map_walker_try_leaf(u64 addr, u64 end, u32 level, |
| 402 | kvm_pte_t *ptep, struct hyp_map_data *data) | 387 | kvm_pte_t *ptep, struct hyp_map_data *data) |
| 403 | { | 388 | { |
| @@ -407,11 +392,16 @@ static bool hyp_map_walker_try_leaf(u64 addr, u64 end, u32 level, · 3 unmodified lines | |||
| 407 | if (!kvm_block_mapping_supported(addr, end, phys, level)) | 392 | if (!kvm_block_mapping_supported(addr, end, phys, level)) |
| 408 | return false; | 393 | return false; |
| 409 | 394 | ||
| 395 | data->phys += granule; | ||
| 410 | new = kvm_init_valid_leaf_pte(phys, data->attr, level); | 396 | new = kvm_init_valid_leaf_pte(phys, data->attr, level); |
| 411 | if (hyp_pte_needs_update(old, new)) | 397 | if (old == new) |
| 412 | smp_store_release(ptep, new); | 398 | return true; |
| 399 | if (!kvm_pte_valid(old)) | ||
| 400 | data->mm_ops->get_page(ptep); | ||
| 401 | else if (WARN_ON((old ^ new) & ~KVM_PTE_LEAF_ATTR_HI_SW)) | ||
| 402 | return false; | ||
| 413 | 403 | ||
| 414 | data->phys += granule; | 404 | smp_store_release(ptep, new); |
| 415 | return true; | 405 | return true; |
| 416 | } | 406 | } |
| 417 | 407 | ||
| @@ -433,6 +423,7 @@ static int hyp_map_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep, · 15 unmodified lines | |||
| 433 | return -ENOMEM; | 423 | return -ENOMEM; |
| 434 | 424 | ||
| 435 | kvm_set_table_pte(ptep, childp, mm_ops); | 425 | kvm_set_table_pte(ptep, childp, mm_ops); |
| 426 | mm_ops->get_page(ptep); | ||
| 436 | return 0; | 427 | return 0; |
| 437 | } | 428 | } |
| 438 | 429 | ||
| @@ -460,6 +451,69 @@ int kvm_pgtable_hyp_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys, · 21 unmodified lines | |||
| 460 | return ret; | 451 | return ret; |
| 461 | } | 452 | } |
| 462 | 453 | ||
| 454 | struct hyp_unmap_data { | ||
| 455 | u64 unmapped; | ||
| 456 | struct kvm_pgtable_mm_ops *mm_ops; | ||
| 457 | }; | ||
| 458 | |||
| 459 | static int hyp_unmap_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep, | ||
| 460 | enum kvm_pgtable_walk_flags flag, void * const arg) | ||
| 461 | { | ||
| 462 | kvm_pte_t pte = *ptep, *childp = NULL; | ||
| 463 | u64 granule = kvm_granule_size(level); | ||
| 464 | struct hyp_unmap_data *data = arg; | ||
| 465 | struct kvm_pgtable_mm_ops *mm_ops = data->mm_ops; | ||
| 466 | |||
| 467 | if (!kvm_pte_valid(pte)) | ||
| 468 | return -EINVAL; | ||
| 469 | |||
| 470 | if (kvm_pte_table(pte, level)) { | ||
| 471 | childp = kvm_pte_follow(pte, mm_ops); | ||
| 472 | |||
| 473 | if (mm_ops->page_count(childp) != 1) | ||
| 474 | return 0; | ||
| 475 | |||
| 476 | kvm_clear_pte(ptep); | ||
| 477 | dsb(ishst); | ||
| 478 | __tlbi_level(vae2is, __TLBI_VADDR(addr, 0), level); | ||
| 479 | } else { | ||
| 480 | if (end - addr < granule) | ||
| 481 | return -EINVAL; | ||
| 482 | |||
| 483 | kvm_clear_pte(ptep); | ||
| 484 | dsb(ishst); | ||
| 485 | __tlbi_level(vale2is, __TLBI_VADDR(addr, 0), level); | ||
| 486 | data->unmapped += granule; | ||
| 487 | } | ||
| 488 | |||
| 489 | dsb(ish); | ||
| 490 | isb(); | ||
| 491 | mm_ops->put_page(ptep); | ||
| 492 | |||
| 493 | if (childp) | ||
| 494 | mm_ops->put_page(childp); | ||
| 495 | |||
| 496 | return 0; | ||
| 497 | } | ||
| 498 | |||
| 499 | u64 kvm_pgtable_hyp_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size) | ||
| 500 | { | ||
| 501 | struct hyp_unmap_data unmap_data = { | ||
| 502 | .mm_ops = pgt->mm_ops, | ||
| 503 | }; | ||
| 504 | struct kvm_pgtable_walker walker = { | ||
| 505 | .cb = hyp_unmap_walker, | ||
| 506 | .arg = &unmap_data, | ||
| 507 | .flags = KVM_PGTABLE_WALK_LEAF | KVM_PGTABLE_WALK_TABLE_POST, | ||
| 508 | }; | ||
| 509 | |||
| 510 | if (!pgt->mm_ops->page_count) | ||
| 511 | return 0; | ||
| 512 | |||
| 513 | kvm_pgtable_walk(pgt, addr, size, &walker); | ||
| 514 | return unmap_data.unmapped; | ||
| 515 | } | ||
| 516 | |||
| 463 | int kvm_pgtable_hyp_init(struct kvm_pgtable *pgt, u32 va_bits, | 517 | int kvm_pgtable_hyp_init(struct kvm_pgtable *pgt, u32 va_bits, |
| 464 | struct kvm_pgtable_mm_ops *mm_ops) | 518 | struct kvm_pgtable_mm_ops *mm_ops) |
| 465 | { | 519 | { |
| @@ -482,8 +536,16 @@ static int hyp_free_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep, · 16 unmodified lines | |||
| 482 | enum kvm_pgtable_walk_flags flag, void * const arg) | 536 | enum kvm_pgtable_walk_flags flag, void * const arg) |
| 483 | { | 537 | { |
| 484 | struct kvm_pgtable_mm_ops *mm_ops = arg; | 538 | struct kvm_pgtable_mm_ops *mm_ops = arg; |
| 539 | kvm_pte_t pte = *ptep; | ||
| 540 | |||
| 541 | if (!kvm_pte_valid(pte)) | ||
| 542 | return 0; | ||
| 543 | |||
| 544 | mm_ops->put_page(ptep); | ||
| 545 | |||
| 546 | if (kvm_pte_table(pte, level)) | ||
| 547 | mm_ops->put_page(kvm_pte_follow(pte, mm_ops)); | ||
| 485 | 548 | ||
| 486 | mm_ops->put_page((void *)kvm_pte_follow(*ptep, mm_ops)); | ||
| 487 | return 0; | 549 | return 0; |
| 488 | } | 550 | } |
| 489 | 551 | ||
| @@ -491,7 +553,7 @@ void kvm_pgtable_hyp_destroy(struct kvm_pgtable *pgt) · 1 unmodified line | |||
| 491 | { | 553 | { |
| 492 | struct kvm_pgtable_walker walker = { | 554 | struct kvm_pgtable_walker walker = { |
| 493 | .cb = hyp_free_walker, | 555 | .cb = hyp_free_walker, |
| 494 | .flags = KVM_PGTABLE_WALK_TABLE_POST, | 556 | .flags = KVM_PGTABLE_WALK_LEAF | KVM_PGTABLE_WALK_TABLE_POST, |
| 495 | .arg = pgt->mm_ops, | 557 | .arg = pgt->mm_ops, |
| 496 | }; | 558 | }; |
| 497 | 559 | ||
| @@ -1116,13 +1178,13 @@ int kvm_pgtable_stage2_flush(struct kvm_pgtable *pgt, u64 addr, u64 size) · 618 unmodified lines | |||
| 1116 | } | 1178 | } |
| 1117 | 1179 | ||
| 1118 | 1180 | ||
| 1119 | int __kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_arch *arch, | 1181 | int __kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu, |
| 1120 | struct kvm_pgtable_mm_ops *mm_ops, | 1182 | struct kvm_pgtable_mm_ops *mm_ops, |
| 1121 | enum kvm_pgtable_stage2_flags flags, | 1183 | enum kvm_pgtable_stage2_flags flags, |
| 1122 | kvm_pgtable_force_pte_cb_t force_pte_cb) | 1184 | kvm_pgtable_force_pte_cb_t force_pte_cb) |
| 1123 | { | 1185 | { |
| 1124 | size_t pgd_sz; | 1186 | size_t pgd_sz; |
| 1125 | u64 vtcr = arch->vtcr; | 1187 | u64 vtcr = mmu->arch->vtcr; |
| 1126 | u32 ia_bits = VTCR_EL2_IPA(vtcr); | 1188 | u32 ia_bits = VTCR_EL2_IPA(vtcr); |
| 1127 | u32 sl0 = FIELD_GET(VTCR_EL2_SL0_MASK, vtcr); | 1189 | u32 sl0 = FIELD_GET(VTCR_EL2_SL0_MASK, vtcr); |
| 1128 | u32 start_level = VTCR_EL2_TGRAN_SL0_BASE - sl0; | 1190 | u32 start_level = VTCR_EL2_TGRAN_SL0_BASE - sl0; |
| @@ -1135,7 +1197,7 @@ int __kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_arch *arch, · 6 unmodified lines | |||
| 1135 | pgt->ia_bits = ia_bits; | 1197 | pgt->ia_bits = ia_bits; |
| 1136 | pgt->start_level = start_level; | 1198 | pgt->start_level = start_level; |
| 1137 | pgt->mm_ops = mm_ops; | 1199 | pgt->mm_ops = mm_ops; |
| 1138 | pgt->mmu = &arch->mmu; | 1200 | pgt->mmu = mmu; |
| 1139 | pgt->flags = flags; | 1201 | pgt->flags = flags; |
| 1140 | pgt->force_pte_cb = force_pte_cb; | 1202 | pgt->force_pte_cb = force_pte_cb; |
| 1141 | 1203 | ||
| @@ -24,7 +24,6 @@ · 23 unmodified lines | |||
| 24 | #include <asm/fpsimd.h> | 24 | #include <asm/fpsimd.h> |
| 25 | #include <asm/debug-monitors.h> | 25 | #include <asm/debug-monitors.h> |
| 26 | #include <asm/processor.h> | 26 | #include <asm/processor.h> |
| 27 | #include <asm/thread_info.h> | ||
| 28 | 27 | ||
| 29 | /* VHE specific context */ | 28 | /* VHE specific context */ |
| 30 | DEFINE_PER_CPU(struct kvm_host_data, kvm_host_data); | 29 | DEFINE_PER_CPU(struct kvm_host_data, kvm_host_data); |
| @@ -239,6 +239,9 @@ void free_hyp_pgds(void) · 238 unmodified lines | |||
| 239 | 239 | ||
| 240 | static bool kvm_host_owns_hyp_mappings(void) | 240 | static bool kvm_host_owns_hyp_mappings(void) |
| 241 | { | 241 | { |
| 242 | if (is_kernel_in_hyp_mode()) | ||
| 243 | return false; | ||
| 244 | |||
| 242 | if (static_branch_likely(&kvm_protected_mode_initialized)) | 245 | if (static_branch_likely(&kvm_protected_mode_initialized)) |
| 243 | return false; | 246 | return false; |
| 244 | 247 | ||
| @@ -281,14 +284,117 @@ static phys_addr_t kvm_kaddr_to_phys(void *kaddr) · 36 unmodified lines | |||
| 281 | } | 284 | } |
| 282 | } | 285 | } |
| 283 | 286 | ||
| 284 | static int pkvm_share_hyp(phys_addr_t start, phys_addr_t end) | 287 | struct hyp_shared_pfn { |
| 288 | u64 pfn; | ||
| 289 | int count; | ||
| 290 | struct rb_node node; | ||
| 291 | }; | ||
| 292 | |||
| 293 | static DEFINE_MUTEX(hyp_shared_pfns_lock); | ||
| 294 | static struct rb_root hyp_shared_pfns = RB_ROOT; | ||
| 295 | |||
| 296 | static struct hyp_shared_pfn *find_shared_pfn(u64 pfn, struct rb_node ***node, | ||
| 297 | struct rb_node **parent) | ||
| 285 | { | 298 | { |
| 286 | phys_addr_t addr; | 299 | struct hyp_shared_pfn *this; |
| 300 | |||
| 301 | *node = &hyp_shared_pfns.rb_node; | ||
| 302 | *parent = NULL; | ||
| 303 | while (**node) { | ||
| 304 | this = container_of(**node, struct hyp_shared_pfn, node); | ||
| 305 | *parent = **node; | ||
| 306 | if (this->pfn < pfn) | ||
| 307 | *node = &((**node)->rb_left); | ||
| 308 | else if (this->pfn > pfn) | ||
| 309 | *node = &((**node)->rb_right); | ||
| 310 | else | ||
| 311 | return this; | ||
| 312 | } | ||
| 313 | |||
| 314 | return NULL; | ||
| 315 | } | ||
| 316 | |||
| 317 | static int share_pfn_hyp(u64 pfn) | ||
| 318 | { | ||
| 319 | struct rb_node **node, *parent; | ||
| 320 | struct hyp_shared_pfn *this; | ||
| 321 | int ret = 0; | ||
| 322 | |||
| 323 | mutex_lock(&hyp_shared_pfns_lock); | ||
| 324 | this = find_shared_pfn(pfn, &node, &parent); | ||
| 325 | if (this) { | ||
| 326 | this->count++; | ||
| 327 | goto unlock; | ||
| 328 | } | ||
| 329 | |||
| 330 | this = kzalloc(sizeof(*this), GFP_KERNEL); | ||
| 331 | if (!this) { | ||
| 332 | ret = -ENOMEM; | ||
| 333 | goto unlock; | ||
| 334 | } | ||
| 335 | |||
| 336 | this->pfn = pfn; | ||
| 337 | this->count = 1; | ||
| 338 | rb_link_node(&this->node, parent, node); | ||
| 339 | rb_insert_color(&this->node, &hyp_shared_pfns); | ||
| 340 | ret = kvm_call_hyp_nvhe(__pkvm_host_share_hyp, pfn, 1); | ||
| 341 | unlock: | ||
| 342 | mutex_unlock(&hyp_shared_pfns_lock); | ||
| 343 | |||
| 344 | return ret; | ||
| 345 | } | ||
| 346 | |||
| 347 | static int unshare_pfn_hyp(u64 pfn) | ||
| 348 | { | ||
| 349 | struct rb_node **node, *parent; | ||
| 350 | struct hyp_shared_pfn *this; | ||
| 351 | int ret = 0; | ||
| 352 | |||
| 353 | mutex_lock(&hyp_shared_pfns_lock); | ||
| 354 | this = find_shared_pfn(pfn, &node, &parent); | ||
| 355 | if (WARN_ON(!this)) { | ||
| 356 | ret = -ENOENT; | ||
| 357 | goto unlock; | ||
| 358 | } | ||
| 359 | |||
| 360 | this->count--; | ||
| 361 | if (this->count) | ||
| 362 | goto unlock; | ||
| 363 | |||
| 364 | rb_erase(&this->node, &hyp_shared_pfns); | ||
| 365 | kfree(this); | ||
| 366 | ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_hyp, pfn, 1); | ||
| 367 | unlock: | ||
| 368 | mutex_unlock(&hyp_shared_pfns_lock); | ||
| 369 | |||
| 370 | return ret; | ||
| 371 | } | ||
| 372 | |||
| 373 | int kvm_share_hyp(void *from, void *to) | ||
| 374 | { | ||
| 375 | phys_addr_t start, end, cur; | ||
| 376 | u64 pfn; | ||
| 287 | int ret; | 377 | int ret; |
| 288 | 378 | ||
| 289 | for (addr = ALIGN_DOWN(start, PAGE_SIZE); addr < end; addr += PAGE_SIZE) { | 379 | if (is_kernel_in_hyp_mode()) |
| 290 | ret = kvm_call_hyp_nvhe(__pkvm_host_share_hyp, | 380 | return 0; |
| 291 | __phys_to_pfn(addr)); | 381 | |
| 382 | /* | ||
| 383 | * The share hcall maps things in the 'fixed-offset' region of the hyp | ||
| 384 | * VA space, so we can only share physically contiguous data-structures | ||
| 385 | * for now. | ||
| 386 | */ | ||
| 387 | if (is_vmalloc_or_module_addr(from) || is_vmalloc_or_module_addr(to)) | ||
| 388 | return -EINVAL; | ||
| 389 | |||
| 390 | if (kvm_host_owns_hyp_mappings()) | ||
| 391 | return create_hyp_mappings(from, to, PAGE_HYP); | ||
| 392 | |||
| 393 | start = ALIGN_DOWN(__pa(from), PAGE_SIZE); | ||
| 394 | end = PAGE_ALIGN(__pa(to)); | ||
| 395 | for (cur = start; cur < end; cur += PAGE_SIZE) { | ||
| 396 | pfn = __phys_to_pfn(cur); | ||
| 397 | ret = share_pfn_hyp(pfn); | ||
| 292 | if (ret) | 398 | if (ret) |
| 293 | return ret; | 399 | return ret; |
| 294 | } | 400 | } |
| @@ -296,6 +402,22 @@ static int pkvm_share_hyp(phys_addr_t start, phys_addr_t end) · 1 unmodified line | |||
| 296 | return 0; | 402 | return 0; |
| 297 | } | 403 | } |
| 298 | 404 | ||
| 405 | void kvm_unshare_hyp(void *from, void *to) | ||
| 406 | { | ||
| 407 | phys_addr_t start, end, cur; | ||
| 408 | u64 pfn; | ||
| 409 | |||
| 410 | if (is_kernel_in_hyp_mode() || kvm_host_owns_hyp_mappings() || !from) | ||
| 411 | return; | ||
| 412 | |||
| 413 | start = ALIGN_DOWN(__pa(from), PAGE_SIZE); | ||
| 414 | end = PAGE_ALIGN(__pa(to)); | ||
| 415 | for (cur = start; cur < end; cur += PAGE_SIZE) { | ||
| 416 | pfn = __phys_to_pfn(cur); | ||
| 417 | WARN_ON(unshare_pfn_hyp(pfn)); | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 299 | /** | 421 | /** |
| 300 | * create_hyp_mappings - duplicate a kernel virtual address range in Hyp mode | 422 | * create_hyp_mappings - duplicate a kernel virtual address range in Hyp mode |
| 301 | * @from: The virtual kernel start address of the range | 423 | * @from: The virtual kernel start address of the range |
| @@ -316,12 +438,8 @@ int create_hyp_mappings(void *from, void *to, enum kvm_pgtable_prot prot) · 14 unmodified lines | |||
| 316 | if (is_kernel_in_hyp_mode()) | 438 | if (is_kernel_in_hyp_mode()) |
| 317 | return 0; | 439 | return 0; |
| 318 | 440 | ||
| 319 | if (!kvm_host_owns_hyp_mappings()) { | 441 | if (!kvm_host_owns_hyp_mappings()) |
| 320 | if (WARN_ON(prot != PAGE_HYP)) | 442 | return -EPERM; |
| 321 | return -EPERM; | ||
| 322 | return pkvm_share_hyp(kvm_kaddr_to_phys(from), | ||
| 323 | kvm_kaddr_to_phys(to)); | ||
| 324 | } | ||
| 325 | 443 | ||
| 326 | start = start & PAGE_MASK; | 444 | start = start & PAGE_MASK; |
| 327 | end = PAGE_ALIGN(end); | 445 | end = PAGE_ALIGN(end); |
| @@ -407,6 +525,9 @@ int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size, · 79 unmodified lines | |||
| 407 | unsigned long addr; | 525 | unsigned long addr; |
| 408 | int ret; | 526 | int ret; |
| 409 | 527 | ||
| 528 | if (is_protected_kvm_enabled()) | ||
| 529 | return -EPERM; | ||
| 530 | |||
| 410 | *kaddr = ioremap(phys_addr, size); | 531 | *kaddr = ioremap(phys_addr, size); |
| 411 | if (!*kaddr) | 532 | if (!*kaddr) |
| 412 | return -ENOMEM; | 533 | return -ENOMEM; |
| @@ -516,7 +637,8 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu) · 103 unmodified lines | |||
| 516 | if (!pgt) | 637 | if (!pgt) |
| 517 | return -ENOMEM; | 638 | return -ENOMEM; |
| 518 | 639 | ||
| 519 | err = kvm_pgtable_stage2_init(pgt, &kvm->arch, &kvm_s2_mm_ops); | 640 | mmu->arch = &kvm->arch; |
| 641 | err = kvm_pgtable_stage2_init(pgt, mmu, &kvm_s2_mm_ops); | ||
| 520 | if (err) | 642 | if (err) |
| 521 | goto out_free_pgtable; | 643 | goto out_free_pgtable; |
| 522 | 644 | ||
| @@ -529,7 +651,6 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu) · 6 unmodified lines | |||
| 529 | for_each_possible_cpu(cpu) | 651 | for_each_possible_cpu(cpu) |
| 530 | *per_cpu_ptr(mmu->last_vcpu_ran, cpu) = -1; | 652 | *per_cpu_ptr(mmu->last_vcpu_ran, cpu) = -1; |
| 531 | 653 | ||
| 532 | mmu->arch = &kvm->arch; | ||
| 533 | mmu->pgt = pgt; | 654 | mmu->pgt = pgt; |
| 534 | mmu->pgd_phys = __pa(pgt->pgd); | 655 | mmu->pgd_phys = __pa(pgt->pgd); |
| 535 | WRITE_ONCE(mmu->vmid.vmid_gen, 0); | 656 | WRITE_ONCE(mmu->vmid.vmid_gen, 0); |
| @@ -650,6 +771,9 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, · 114 unmodified lines | |||
| 650 | KVM_PGTABLE_PROT_R | | 771 | KVM_PGTABLE_PROT_R | |
| 651 | (writable ? KVM_PGTABLE_PROT_W : 0); | 772 | (writable ? KVM_PGTABLE_PROT_W : 0); |
| 652 | 773 | ||
| 774 | if (is_protected_kvm_enabled()) | ||
| 775 | return -EPERM; | ||
| 776 | |||
| 653 | size += offset_in_page(guest_ipa); | 777 | size += offset_in_page(guest_ipa); |
| 654 | guest_ipa &= PAGE_MASK; | 778 | guest_ipa &= PAGE_MASK; |
| 655 | 779 | ||
| @@ -8,10 +8,9 @@ · 7 unmodified lines | |||
| 8 | #include <linux/memblock.h> | 8 | #include <linux/memblock.h> |
| 9 | #include <linux/sort.h> | 9 | #include <linux/sort.h> |
| 10 | 10 | ||
| 11 | #include <asm/kvm_host.h> | 11 | #include <asm/kvm_pkvm.h> |
| 12 | 12 | ||
| 13 | #include <nvhe/memory.h> | 13 | #include "hyp_constants.h" |
| 14 | #include <nvhe/mm.h> | ||
| 15 | 14 | ||
| 16 | static struct memblock_region *hyp_memory = kvm_nvhe_sym(hyp_memory); | 15 | static struct memblock_region *hyp_memory = kvm_nvhe_sym(hyp_memory); |
| 17 | static unsigned int *hyp_memblock_nr_ptr = &kvm_nvhe_sym(hyp_memblock_nr); | 16 | static unsigned int *hyp_memblock_nr_ptr = &kvm_nvhe_sym(hyp_memblock_nr); |
| @@ -82,7 +81,8 @@ void __init kvm_hyp_reserve(void) · 64 unmodified lines | |||
| 82 | do { | 81 | do { |
| 83 | prev = nr_pages; | 82 | prev = nr_pages; |
| 84 | nr_pages = hyp_mem_pages + prev; | 83 | nr_pages = hyp_mem_pages + prev; |
| 85 | nr_pages = DIV_ROUND_UP(nr_pages * sizeof(struct hyp_page), PAGE_SIZE); | 84 | nr_pages = DIV_ROUND_UP(nr_pages * STRUCT_HYP_PAGE_SIZE, |
| 85 | PAGE_SIZE); | ||
| 86 | nr_pages += __hyp_pgtable_max_pages(nr_pages); | 86 | nr_pages += __hyp_pgtable_max_pages(nr_pages); |
| 87 | } while (nr_pages != prev); | 87 | } while (nr_pages != prev); |
| 88 | hyp_mem_pages += nr_pages; | 88 | hyp_mem_pages += nr_pages; |
| @@ -28,6 +28,7 @@ static u32 kvm_pmu_event_mask(struct kvm *kvm) · 27 unmodified lines | |||
| 28 | case ID_AA64DFR0_PMUVER_8_1: | 28 | case ID_AA64DFR0_PMUVER_8_1: |
| 29 | case ID_AA64DFR0_PMUVER_8_4: | 29 | case ID_AA64DFR0_PMUVER_8_4: |
| 30 | case ID_AA64DFR0_PMUVER_8_5: | 30 | case ID_AA64DFR0_PMUVER_8_5: |
| 31 | case ID_AA64DFR0_PMUVER_8_7: | ||
| 31 | return GENMASK(15, 0); | 32 | return GENMASK(15, 0); |
| 32 | default: /* Shouldn't be here, just for sanity */ | 33 | default: /* Shouldn't be here, just for sanity */ |
| 33 | WARN_ONCE(1, "Unknown PMU version %d\n", kvm->arch.pmuver); | 34 | WARN_ONCE(1, "Unknown PMU version %d\n", kvm->arch.pmuver); |
| @@ -109,7 +109,7 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu) · 108 unmodified lines | |||
| 109 | 109 | ||
| 110 | /* | 110 | /* |
| 111 | * Make sure the reset request is observed if the change to | 111 | * Make sure the reset request is observed if the change to |
| 112 | * power_state is observed. | 112 | * power_off is observed. |
| 113 | */ | 113 | */ |
| 114 | smp_wmb(); | 114 | smp_wmb(); |
| 115 | 115 | ||
| @@ -94,22 +94,31 @@ static int kvm_vcpu_finalize_sve(struct kvm_vcpu *vcpu) · 93 unmodified lines | |||
| 94 | { | 94 | { |
| 95 | void *buf; | 95 | void *buf; |
| 96 | unsigned int vl; | 96 | unsigned int vl; |
| 97 | size_t reg_sz; | ||
| 98 | int ret; | ||
| 97 | 99 | ||
| 98 | vl = vcpu->arch.sve_max_vl; | 100 | vl = vcpu->arch.sve_max_vl; |
| 99 | 101 | ||
| 100 | /* | 102 | /* |
| 101 | * Responsibility for these properties is shared between | 103 | * Responsibility for these properties is shared between |
| 102 | * kvm_arm_init_arch_resources(), kvm_vcpu_enable_sve() and | 104 | * kvm_arm_init_sve(), kvm_vcpu_enable_sve() and |
| 103 | * set_sve_vls(). Double-check here just to be sure: | 105 | * set_sve_vls(). Double-check here just to be sure: |
| 104 | */ | 106 | */ |
| 105 | if (WARN_ON(!sve_vl_valid(vl) || vl > sve_max_virtualisable_vl() || | 107 | if (WARN_ON(!sve_vl_valid(vl) || vl > sve_max_virtualisable_vl() || |
| 106 | vl > SVE_VL_ARCH_MAX)) | 108 | vl > SVE_VL_ARCH_MAX)) |
| 107 | return -EIO; | 109 | return -EIO; |
| 108 | 110 | ||
| 109 | buf = kzalloc(SVE_SIG_REGS_SIZE(sve_vq_from_vl(vl)), GFP_KERNEL_ACCOUNT); | 111 | reg_sz = vcpu_sve_state_size(vcpu); |
| 112 | buf = kzalloc(reg_sz, GFP_KERNEL_ACCOUNT); | ||
| 110 | if (!buf) | 113 | if (!buf) |
| 111 | return -ENOMEM; | 114 | return -ENOMEM; |
| 112 | 115 | ||
| 116 | ret = kvm_share_hyp(buf, buf + reg_sz); | ||
| 117 | if (ret) { | ||
| 118 | kfree(buf); | ||
| 119 | return ret; | ||
| 120 | } | ||
| 121 | |||
| 113 | vcpu->arch.sve_state = buf; | 122 | vcpu->arch.sve_state = buf; |
| 114 | vcpu->arch.flags |= KVM_ARM64_VCPU_SVE_FINALIZED; | 123 | vcpu->arch.flags |= KVM_ARM64_VCPU_SVE_FINALIZED; |
| 115 | return 0; | 124 | return 0; |
| @@ -141,7 +150,13 @@ bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu) · 25 unmodified lines | |||
| 141 | 150 | ||
| 142 | void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu) | 151 | void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu) |
| 143 | { | 152 | { |
| 144 | kfree(vcpu->arch.sve_state); | 153 | void *sve_state = vcpu->arch.sve_state; |
| 154 | |||
| 155 | kvm_vcpu_unshare_task_fp(vcpu); | ||
| 156 | kvm_unshare_hyp(vcpu, vcpu + 1); | ||
| 157 | if (sve_state) | ||
| 158 | kvm_unshare_hyp(sve_state, sve_state + vcpu_sve_state_size(vcpu)); | ||
| 159 | kfree(sve_state); | ||
| 145 | } | 160 | } |
| 146 | 161 | ||
| 147 | static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu) | 162 | static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu) |
| @@ -193,10 +208,9 @@ static bool vcpu_allowed_register_width(struct kvm_vcpu *vcpu) · 45 unmodified lines | |||
| 193 | * kvm_reset_vcpu - sets core registers and sys_regs to reset value | 208 | * kvm_reset_vcpu - sets core registers and sys_regs to reset value |
| 194 | * @vcpu: The VCPU pointer | 209 | * @vcpu: The VCPU pointer |
| 195 | * | 210 | * |
| 196 | * This function finds the right table above and sets the registers on | 211 | * This function sets the registers on the virtual CPU struct to their |
| 197 | * the virtual CPU struct to their architecturally defined reset | 212 | * architecturally defined reset values, except for registers whose reset is |
| 198 | * values, except for registers whose reset is deferred until | 213 | * deferred until kvm_arm_vcpu_finalize(). |
| 199 | * kvm_arm_vcpu_finalize(). | ||
| 200 | * | 214 | * |
| 201 | * Note: This function can be called from two paths: The KVM_ARM_VCPU_INIT | 215 | * Note: This function can be called from two paths: The KVM_ARM_VCPU_INIT |
| 202 | * ioctl or as part of handling a request issued by another VCPU in the PSCI | 216 | * ioctl or as part of handling a request issued by another VCPU in the PSCI |
| @@ -92,7 +92,7 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) · 91 unmodified lines | |||
| 92 | return ret; | 92 | return ret; |
| 93 | 93 | ||
| 94 | kvm_for_each_vcpu(i, vcpu, kvm) { | 94 | kvm_for_each_vcpu(i, vcpu, kvm) { |
| 95 | if (vcpu->arch.has_run_once) | 95 | if (vcpu_has_run_once(vcpu)) |
| 96 | goto out_unlock; | 96 | goto out_unlock; |
| 97 | } | 97 | } |
| 98 | ret = 0; | 98 | ret = 0; |
| @@ -764,10 +764,12 @@ static int vgic_register_all_redist_iodevs(struct kvm *kvm) · 763 unmodified lines | |||
| 764 | } | 764 | } |
| 765 | 765 | ||
| 766 | if (ret) { | 766 | if (ret) { |
| 767 | /* The current c failed, so we start with the previous one. */ | 767 | /* The current c failed, so iterate over the previous ones. */ |
| 768 | int i; | ||
| 769 | |||
| 768 | mutex_lock(&kvm->slots_lock); | 770 | mutex_lock(&kvm->slots_lock); |
| 769 | for (c--; c >= 0; c--) { | 771 | for (i = 0; i < c; i++) { |
| 770 | vcpu = kvm_get_vcpu(kvm, c); | 772 | vcpu = kvm_get_vcpu(kvm, i); |
| 771 | vgic_unregister_redist_iodev(vcpu); | 773 | vgic_unregister_redist_iodev(vcpu); |
| 772 | } | 774 | } |
| 773 | mutex_unlock(&kvm->slots_lock); | 775 | mutex_unlock(&kvm->slots_lock); |
| @@ -1050,7 +1050,7 @@ static int dispatch_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev, · 1049 unmodified lines | |||
| 1050 | return 0; | 1050 | return 0; |
| 1051 | } | 1051 | } |
| 1052 | 1052 | ||
| 1053 | struct kvm_io_device_ops kvm_io_gic_ops = { | 1053 | const struct kvm_io_device_ops kvm_io_gic_ops = { |
| 1054 | .read = dispatch_mmio_read, | 1054 | .read = dispatch_mmio_read, |
| 1055 | .write = dispatch_mmio_write, | 1055 | .write = dispatch_mmio_write, |
| 1056 | }; | 1056 | }; |
| @@ -34,7 +34,7 @@ struct vgic_register_region { · 33 unmodified lines | |||
| 34 | }; | 34 | }; |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | extern struct kvm_io_device_ops kvm_io_gic_ops; | 37 | extern const struct kvm_io_device_ops kvm_io_gic_ops; |
| 38 | 38 | ||
| 39 | #define VGIC_ACCESS_8bit 1 | 39 | #define VGIC_ACCESS_8bit 1 |
| 40 | #define VGIC_ACCESS_32bit 2 | 40 | #define VGIC_ACCESS_32bit 2 |
| @@ -293,12 +293,12 @@ int vgic_v2_map_resources(struct kvm *kvm) · 292 unmodified lines | |||
| 293 | 293 | ||
| 294 | if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) || | 294 | if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) || |
| 295 | IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)) { | 295 | IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)) { |
| 296 | kvm_err("Need to set vgic cpu and dist addresses first\n"); | 296 | kvm_debug("Need to set vgic cpu and dist addresses first\n"); |
| 297 | return -ENXIO; | 297 | return -ENXIO; |
| 298 | } | 298 | } |
| 299 | 299 | ||
| 300 | if (!vgic_v2_check_base(dist->vgic_dist_base, dist->vgic_cpu_base)) { | 300 | if (!vgic_v2_check_base(dist->vgic_dist_base, dist->vgic_cpu_base)) { |
| 301 | kvm_err("VGIC CPU and dist frames overlap\n"); | 301 | kvm_debug("VGIC CPU and dist frames overlap\n"); |
| 302 | return -EINVAL; | 302 | return -EINVAL; |
| 303 | } | 303 | } |
| 304 | 304 | ||
| @@ -345,6 +345,11 @@ int vgic_v2_probe(const struct gic_kvm_info *info) · 40 unmodified lines | |||
| 345 | int ret; | 345 | int ret; |
| 346 | u32 vtr; | 346 | u32 vtr; |
| 347 | 347 | ||
| 348 | if (is_protected_kvm_enabled()) { | ||
| 349 | kvm_err("GICv2 not supported in protected mode\n"); | ||
| 350 | return -ENXIO; | ||
| 351 | } | ||
| 352 | |||
| 348 | if (!info->vctrl.start) { | 353 | if (!info->vctrl.start) { |
| 349 | kvm_err("GICH not present in the firmware table\n"); | 354 | kvm_err("GICH not present in the firmware table\n"); |
| 350 | return -ENXIO; | 355 | return -ENXIO; |
| @@ -554,12 +554,12 @@ int vgic_v3_map_resources(struct kvm *kvm) · 553 unmodified lines | |||
| 554 | } | 554 | } |
| 555 | 555 | ||
| 556 | if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base)) { | 556 | if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base)) { |
| 557 | kvm_err("Need to set vgic distributor addresses first\n"); | 557 | kvm_debug("Need to set vgic distributor addresses first\n"); |
| 558 | return -ENXIO; | 558 | return -ENXIO; |
| 559 | } | 559 | } |
| 560 | 560 | ||
| 561 | if (!vgic_v3_check_base(kvm)) { | 561 | if (!vgic_v3_check_base(kvm)) { |
| 562 | kvm_err("VGIC redist and dist frames overlap\n"); | 562 | kvm_debug("VGIC redist and dist frames overlap\n"); |
| 563 | return -EINVAL; | 563 | return -EINVAL; |
| 564 | } | 564 | } |
| 565 | 565 | ||
| @@ -651,7 +651,7 @@ int vgic_v3_probe(const struct gic_kvm_info *info) · 85 unmodified lines | |||
| 651 | } else if (!PAGE_ALIGNED(info->vcpu.start)) { | 651 | } else if (!PAGE_ALIGNED(info->vcpu.start)) { |
| 652 | pr_warn("GICV physical address 0x%llx not page aligned\n", | 652 | pr_warn("GICV physical address 0x%llx not page aligned\n", |
| 653 | (unsigned long long)info->vcpu.start); | 653 | (unsigned long long)info->vcpu.start); |
| 654 | } else { | 654 | } else if (kvm_get_mode() != KVM_MODE_PROTECTED) { |
| 655 | kvm_vgic_global_state.vcpu_base = info->vcpu.start; | 655 | kvm_vgic_global_state.vcpu_base = info->vcpu.start; |
| 656 | kvm_vgic_global_state.can_emulate_gicv2 = true; | 656 | kvm_vgic_global_state.can_emulate_gicv2 = true; |
| 657 | ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2); | 657 | ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2); |
| @@ -5,9 +5,11 @@ · 4 unmodified lines | |||
| 5 | #ifndef __KVM_ARM_VGIC_H | 5 | #ifndef __KVM_ARM_VGIC_H |
| 6 | #define __KVM_ARM_VGIC_H | 6 | #define __KVM_ARM_VGIC_H |
| 7 | 7 | ||
| 8 | #include <linux/kernel.h> | 8 | #include <linux/bits.h> |
| 9 | #include <linux/kvm.h> | 9 | #include <linux/kvm.h> |
| 10 | #include <linux/irqreturn.h> | 10 | #include <linux/irqreturn.h> |
| 11 | #include <linux/kref.h> | ||
| 12 | #include <linux/mutex.h> | ||
| 11 | #include <linux/spinlock.h> | 13 | #include <linux/spinlock.h> |
| 12 | #include <linux/static_key.h> | 14 | #include <linux/static_key.h> |
| 13 | #include <linux/types.h> | 15 | #include <linux/types.h> |
| @@ -1322,7 +1322,6 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu); · 1321 unmodified lines | |||
| 1322 | 1322 | ||
| 1323 | bool kvm_is_reserved_pfn(kvm_pfn_t pfn); | 1323 | bool kvm_is_reserved_pfn(kvm_pfn_t pfn); |
| 1324 | bool kvm_is_zone_device_pfn(kvm_pfn_t pfn); | 1324 | bool kvm_is_zone_device_pfn(kvm_pfn_t pfn); |
| 1325 | bool kvm_is_transparent_hugepage(kvm_pfn_t pfn); | ||
| 1326 | 1325 | ||
| 1327 | struct kvm_irq_ack_notifier { | 1326 | struct kvm_irq_ack_notifier { |
| 1328 | struct hlist_node link; | 1327 | struct hlist_node link; |
| @@ -4,6 +4,7 @@ · 3 unmodified lines | |||
| 4 | /aarch64/get-reg-list | 4 | /aarch64/get-reg-list |
| 5 | /aarch64/psci_cpu_on_test | 5 | /aarch64/psci_cpu_on_test |
| 6 | /aarch64/vgic_init | 6 | /aarch64/vgic_init |
| 7 | /aarch64/vgic_irq | ||
| 7 | /s390x/memop | 8 | /s390x/memop |
| 8 | /s390x/resets | 9 | /s390x/resets |
| 9 | /s390x/sync_regs_test | 10 | /s390x/sync_regs_test |
| @@ -96,6 +96,7 @@ TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions · 95 unmodified lines | |||
| 96 | TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list | 96 | TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list |
| 97 | TEST_GEN_PROGS_aarch64 += aarch64/psci_cpu_on_test | 97 | TEST_GEN_PROGS_aarch64 += aarch64/psci_cpu_on_test |
| 98 | TEST_GEN_PROGS_aarch64 += aarch64/vgic_init | 98 | TEST_GEN_PROGS_aarch64 += aarch64/vgic_init |
| 99 | TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq | ||
| 99 | TEST_GEN_PROGS_aarch64 += demand_paging_test | 100 | TEST_GEN_PROGS_aarch64 += demand_paging_test |
| 100 | TEST_GEN_PROGS_aarch64 += dirty_log_test | 101 | TEST_GEN_PROGS_aarch64 += dirty_log_test |
| 101 | TEST_GEN_PROGS_aarch64 += dirty_log_perf_test | 102 | TEST_GEN_PROGS_aarch64 += dirty_log_perf_test |
| @@ -382,7 +382,7 @@ static struct kvm_vm *test_vm_create(void) · 381 unmodified lines | |||
| 382 | 382 | ||
| 383 | ucall_init(vm, NULL); | 383 | ucall_init(vm, NULL); |
| 384 | test_init_timer_irq(vm); | 384 | test_init_timer_irq(vm); |
| 385 | vgic_v3_setup(vm, nr_vcpus, GICD_BASE_GPA, GICR_BASE_GPA); | 385 | vgic_v3_setup(vm, nr_vcpus, 64, GICD_BASE_GPA, GICR_BASE_GPA); |
| 386 | 386 | ||
| 387 | /* Make all the test's cmdline args visible to the guest */ | 387 | /* Make all the test's cmdline args visible to the guest */ |
| 388 | sync_global_to_guest(vm, test_args); | 388 | sync_global_to_guest(vm, test_args); |
| @@ -1014,6 +1014,22 @@ static __u64 sve_rejects_set[] = { · 1013 unmodified lines | |||
| 1014 | KVM_REG_ARM64_SVE_VLS, | 1014 | KVM_REG_ARM64_SVE_VLS, |
| 1015 | }; | 1015 | }; |
| 1016 | 1016 | ||
| 1017 | static __u64 pauth_addr_regs[] = { | ||
| 1018 | ARM64_SYS_REG(3, 0, 2, 1, 0), /* APIAKEYLO_EL1 */ | ||
| 1019 | ARM64_SYS_REG(3, 0, 2, 1, 1), /* APIAKEYHI_EL1 */ | ||
| 1020 | ARM64_SYS_REG(3, 0, 2, 1, 2), /* APIBKEYLO_EL1 */ | ||
| 1021 | ARM64_SYS_REG(3, 0, 2, 1, 3), /* APIBKEYHI_EL1 */ | ||
| 1022 | ARM64_SYS_REG(3, 0, 2, 2, 0), /* APDAKEYLO_EL1 */ | ||
| 1023 | ARM64_SYS_REG(3, 0, 2, 2, 1), /* APDAKEYHI_EL1 */ | ||
| 1024 | ARM64_SYS_REG(3, 0, 2, 2, 2), /* APDBKEYLO_EL1 */ | ||
| 1025 | ARM64_SYS_REG(3, 0, 2, 2, 3) /* APDBKEYHI_EL1 */ | ||
| 1026 | }; | ||
| 1027 | |||
| 1028 | static __u64 pauth_generic_regs[] = { | ||
| 1029 | ARM64_SYS_REG(3, 0, 2, 3, 0), /* APGAKEYLO_EL1 */ | ||
| 1030 | ARM64_SYS_REG(3, 0, 2, 3, 1), /* APGAKEYHI_EL1 */ | ||
| 1031 | }; | ||
| 1032 | |||
| 1017 | #define BASE_SUBLIST \ | 1033 | #define BASE_SUBLIST \ |
| 1018 | { "base", .regs = base_regs, .regs_n = ARRAY_SIZE(base_regs), } | 1034 | { "base", .regs = base_regs, .regs_n = ARRAY_SIZE(base_regs), } |
| 1019 | #define VREGS_SUBLIST \ | 1035 | #define VREGS_SUBLIST \ |
| @@ -1025,6 +1041,21 @@ static __u64 sve_rejects_set[] = { · 5 unmodified lines | |||
| 1025 | { "sve", .capability = KVM_CAP_ARM_SVE, .feature = KVM_ARM_VCPU_SVE, .finalize = true, \ | 1041 | { "sve", .capability = KVM_CAP_ARM_SVE, .feature = KVM_ARM_VCPU_SVE, .finalize = true, \ |
| 1026 | .regs = sve_regs, .regs_n = ARRAY_SIZE(sve_regs), \ | 1042 | .regs = sve_regs, .regs_n = ARRAY_SIZE(sve_regs), \ |
| 1027 | .rejects_set = sve_rejects_set, .rejects_set_n = ARRAY_SIZE(sve_rejects_set), } | 1043 | .rejects_set = sve_rejects_set, .rejects_set_n = ARRAY_SIZE(sve_rejects_set), } |
| 1044 | #define PAUTH_SUBLIST \ | ||
| 1045 | { \ | ||
| 1046 | .name = "pauth_address", \ | ||
| 1047 | .capability = KVM_CAP_ARM_PTRAUTH_ADDRESS, \ | ||
| 1048 | .feature = KVM_ARM_VCPU_PTRAUTH_ADDRESS, \ | ||
| 1049 | .regs = pauth_addr_regs, \ | ||
| 1050 | .regs_n = ARRAY_SIZE(pauth_addr_regs), \ | ||
| 1051 | }, \ | ||
| 1052 | { \ | ||
| 1053 | .name = "pauth_generic", \ | ||
| 1054 | .capability = KVM_CAP_ARM_PTRAUTH_GENERIC, \ | ||
| 1055 | .feature = KVM_ARM_VCPU_PTRAUTH_GENERIC, \ | ||
| 1056 | .regs = pauth_generic_regs, \ | ||
| 1057 | .regs_n = ARRAY_SIZE(pauth_generic_regs), \ | ||
| 1058 | } | ||
| 1028 | 1059 | ||
| 1029 | static struct vcpu_config vregs_config = { | 1060 | static struct vcpu_config vregs_config = { |
| 1030 | .sublists = { | 1061 | .sublists = { |
| @@ -1056,11 +1087,30 @@ static struct vcpu_config sve_pmu_config = { · 25 unmodified lines | |||
| 1056 | {0}, | 1087 | {0}, |
| 1057 | }, | 1088 | }, |
| 1058 | }; | 1089 | }; |
| 1090 | static struct vcpu_config pauth_config = { | ||
| 1091 | .sublists = { | ||
| 1092 | BASE_SUBLIST, | ||
| 1093 | VREGS_SUBLIST, | ||
| 1094 | PAUTH_SUBLIST, | ||
| 1095 | {0}, | ||
| 1096 | }, | ||
| 1097 | }; | ||
| 1098 | static struct vcpu_config pauth_pmu_config = { | ||
| 1099 | .sublists = { | ||
| 1100 | BASE_SUBLIST, | ||
| 1101 | VREGS_SUBLIST, | ||
| 1102 | PAUTH_SUBLIST, | ||
| 1103 | PMU_SUBLIST, | ||
| 1104 | {0}, | ||
| 1105 | }, | ||
| 1106 | }; | ||
| 1059 | 1107 | ||
| 1060 | static struct vcpu_config *vcpu_configs[] = { | 1108 | static struct vcpu_config *vcpu_configs[] = { |
| 1061 | &vregs_config, | 1109 | &vregs_config, |
| 1062 | &vregs_pmu_config, | 1110 | &vregs_pmu_config, |
| 1063 | &sve_config, | 1111 | &sve_config, |
| 1064 | &sve_pmu_config, | 1112 | &sve_pmu_config, |
| 1113 | &pauth_config, | ||
| 1114 | &pauth_pmu_config, | ||
| 1065 | }; | 1115 | }; |
| 1066 | static int vcpu_configs_n = ARRAY_SIZE(vcpu_configs); | 1116 | static int vcpu_configs_n = ARRAY_SIZE(vcpu_configs); |
| @@ -0,0 +1,853 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * vgic_irq.c - Test userspace injection of IRQs | ||
| 4 | * | ||
| 5 | * This test validates the injection of IRQs from userspace using various | ||
| 6 | * methods (e.g., KVM_IRQ_LINE) and modes (e.g., EOI). The guest "asks" the | ||
| 7 | * host to inject a specific intid via a GUEST_SYNC call, and then checks that | ||
| 8 | * it received it. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <asm/kvm.h> | ||
| 12 | #include <asm/kvm_para.h> | ||
| 13 | #include <sys/eventfd.h> | ||
| 14 | #include <linux/sizes.h> | ||
| 15 | |||
| 16 | #include "processor.h" | ||
| 17 | #include "test_util.h" | ||
| 18 | #include "kvm_util.h" | ||
| 19 | #include "gic.h" | ||
| 20 | #include "gic_v3.h" | ||
| 21 | #include "vgic.h" | ||
| 22 | |||
| 23 | #define GICD_BASE_GPA 0x08000000ULL | ||
| 24 | #define GICR_BASE_GPA 0x080A0000ULL | ||
| 25 | #define VCPU_ID 0 | ||
| 26 | |||
| 27 | /* | ||
| 28 | * Stores the user specified args; it's passed to the guest and to every test | ||
| 29 | * function. | ||
| 30 | */ | ||
| 31 | struct test_args { | ||
| 32 | uint32_t nr_irqs; /* number of KVM supported IRQs. */ | ||
| 33 | bool eoi_split; /* 1 is eoir+dir, 0 is eoir only */ | ||
| 34 | bool level_sensitive; /* 1 is level, 0 is edge */ | ||
| 35 | int kvm_max_routes; /* output of KVM_CAP_IRQ_ROUTING */ | ||
| 36 | bool kvm_supports_irqfd; /* output of KVM_CAP_IRQFD */ | ||
| 37 | }; | ||
| 38 | |||
| 39 | /* | ||
| 40 | * KVM implements 32 priority levels: | ||
| 41 | * 0x00 (highest priority) - 0xF8 (lowest priority), in steps of 8 | ||
| 42 | * | ||
| 43 | * Note that these macros will still be correct in the case that KVM implements | ||
| 44 | * more priority levels. Also note that 32 is the minimum for GICv3 and GICv2. | ||
| 45 | */ | ||
| 46 | #define KVM_NUM_PRIOS 32 | ||
| 47 | #define KVM_PRIO_SHIFT 3 /* steps of 8 = 1 << 3 */ | ||
| 48 | #define KVM_PRIO_STEPS (1 << KVM_PRIO_SHIFT) /* 8 */ | ||
| 49 | #define LOWEST_PRIO (KVM_NUM_PRIOS - 1) | ||
| 50 | #define CPU_PRIO_MASK (LOWEST_PRIO << KVM_PRIO_SHIFT) /* 0xf8 */ | ||
| 51 | #define IRQ_DEFAULT_PRIO (LOWEST_PRIO - 1) | ||
| 52 | #define IRQ_DEFAULT_PRIO_REG (IRQ_DEFAULT_PRIO << KVM_PRIO_SHIFT) /* 0xf0 */ | ||
| 53 | |||
| 54 | static void *dist = (void *)GICD_BASE_GPA; | ||
| 55 | static void *redist = (void *)GICR_BASE_GPA; | ||
| 56 | |||
| 57 | /* | ||
| 58 | * The kvm_inject_* utilities are used by the guest to ask the host to inject | ||
| 59 | * interrupts (e.g., using the KVM_IRQ_LINE ioctl). | ||
| 60 | */ | ||
| 61 | |||
| 62 | typedef enum { | ||
| 63 | KVM_INJECT_EDGE_IRQ_LINE = 1, | ||
| 64 | KVM_SET_IRQ_LINE, | ||
| 65 | KVM_SET_IRQ_LINE_HIGH, | ||
| 66 | KVM_SET_LEVEL_INFO_HIGH, | ||
| 67 | KVM_INJECT_IRQFD, | ||
| 68 | KVM_WRITE_ISPENDR, | ||
| 69 | KVM_WRITE_ISACTIVER, | ||
| 70 | } kvm_inject_cmd; | ||
| 71 | |||
| 72 | struct kvm_inject_args { | ||
| 73 | kvm_inject_cmd cmd; | ||
| 74 | uint32_t first_intid; | ||
| 75 | uint32_t num; | ||
| 76 | int level; | ||
| 77 | bool expect_failure; | ||
| 78 | }; | ||
| 79 | |||
| 80 | /* Used on the guest side to perform the hypercall. */ | ||
| 81 | static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, | ||
| 82 | uint32_t num, int level, bool expect_failure); | ||
| 83 | |||
| 84 | /* Used on the host side to get the hypercall info. */ | ||
| 85 | static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, | ||
| 86 | struct kvm_inject_args *args); | ||
| 87 | |||
| 88 | #define _KVM_INJECT_MULTI(cmd, intid, num, expect_failure) \ | ||
| 89 | kvm_inject_call(cmd, intid, num, -1 /* not used */, expect_failure) | ||
| 90 | |||
| 91 | #define KVM_INJECT_MULTI(cmd, intid, num) \ | ||
| 92 | _KVM_INJECT_MULTI(cmd, intid, num, false) | ||
| 93 | |||
| 94 | #define _KVM_INJECT(cmd, intid, expect_failure) \ | ||
| 95 | _KVM_INJECT_MULTI(cmd, intid, 1, expect_failure) | ||
| 96 | |||
| 97 | #define KVM_INJECT(cmd, intid) \ | ||
| 98 | _KVM_INJECT_MULTI(cmd, intid, 1, false) | ||
| 99 | |||
| 100 | #define KVM_ACTIVATE(cmd, intid) \ | ||
| 101 | kvm_inject_call(cmd, intid, 1, 1, false); | ||
| 102 | |||
| 103 | struct kvm_inject_desc { | ||
| 104 | kvm_inject_cmd cmd; | ||
| 105 | /* can inject PPIs, PPIs, and/or SPIs. */ | ||
| 106 | bool sgi, ppi, spi; | ||
| 107 | }; | ||
| 108 | |||
| 109 | static struct kvm_inject_desc inject_edge_fns[] = { | ||
| 110 | /* sgi ppi spi */ | ||
| 111 | { KVM_INJECT_EDGE_IRQ_LINE, false, false, true }, | ||
| 112 | { KVM_INJECT_IRQFD, false, false, true }, | ||
| 113 | { KVM_WRITE_ISPENDR, true, false, true }, | ||
| 114 | { 0, }, | ||
| 115 | }; | ||
| 116 | |||
| 117 | static struct kvm_inject_desc inject_level_fns[] = { | ||
| 118 | /* sgi ppi spi */ | ||
| 119 | { KVM_SET_IRQ_LINE_HIGH, false, true, true }, | ||
| 120 | { KVM_SET_LEVEL_INFO_HIGH, false, true, true }, | ||
| 121 | { KVM_INJECT_IRQFD, false, false, true }, | ||
| 122 | { KVM_WRITE_ISPENDR, false, true, true }, | ||
| 123 | { 0, }, | ||
| 124 | }; | ||
| 125 | |||
| 126 | static struct kvm_inject_desc set_active_fns[] = { | ||
| 127 | /* sgi ppi spi */ | ||
| 128 | { KVM_WRITE_ISACTIVER, true, true, true }, | ||
| 129 | { 0, }, | ||
| 130 | }; | ||
| 131 | |||
| 132 | #define for_each_inject_fn(t, f) \ | ||
| 133 | for ((f) = (t); (f)->cmd; (f)++) | ||
| 134 | |||
| 135 | #define for_each_supported_inject_fn(args, t, f) \ | ||
| 136 | for_each_inject_fn(t, f) \ | ||
| 137 | if ((args)->kvm_supports_irqfd || (f)->cmd != KVM_INJECT_IRQFD) | ||
| 138 | |||
| 139 | #define for_each_supported_activate_fn(args, t, f) \ | ||
| 140 | for_each_supported_inject_fn((args), (t), (f)) | ||
| 141 | |||
| 142 | /* Shared between the guest main thread and the IRQ handlers. */ | ||
| 143 | volatile uint64_t irq_handled; | ||
| 144 | volatile uint32_t irqnr_received[MAX_SPI + 1]; | ||
| 145 | |||
| 146 | static void reset_stats(void) | ||
| 147 | { | ||
| 148 | int i; | ||
| 149 | |||
| 150 | irq_handled = 0; | ||
| 151 | for (i = 0; i <= MAX_SPI; i++) | ||
| 152 | irqnr_received[i] = 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | static uint64_t gic_read_ap1r0(void) | ||
| 156 | { | ||
| 157 | uint64_t reg = read_sysreg_s(SYS_ICV_AP1R0_EL1); | ||
| 158 | |||
| 159 | dsb(sy); | ||
| 160 | return reg; | ||
| 161 | } | ||
| 162 | |||
| 163 | static void gic_write_ap1r0(uint64_t val) | ||
| 164 | { | ||
| 165 | write_sysreg_s(val, SYS_ICV_AP1R0_EL1); | ||
| 166 | isb(); | ||
| 167 | } | ||
| 168 | |||
| 169 | static void guest_set_irq_line(uint32_t intid, uint32_t level); | ||
| 170 | |||
| 171 | static void guest_irq_generic_handler(bool eoi_split, bool level_sensitive) | ||
| 172 | { | ||
| 173 | uint32_t intid = gic_get_and_ack_irq(); | ||
| 174 | |||
| 175 | if (intid == IAR_SPURIOUS) | ||
| 176 | return; | ||
| 177 | |||
| 178 | GUEST_ASSERT(gic_irq_get_active(intid)); | ||
| 179 | |||
| 180 | if (!level_sensitive) | ||
| 181 | GUEST_ASSERT(!gic_irq_get_pending(intid)); | ||
| 182 | |||
| 183 | if (level_sensitive) | ||
| 184 | guest_set_irq_line(intid, 0); | ||
| 185 | |||
| 186 | GUEST_ASSERT(intid < MAX_SPI); | ||
| 187 | irqnr_received[intid] += 1; | ||
| 188 | irq_handled += 1; | ||
| 189 | |||
| 190 | gic_set_eoi(intid); | ||
| 191 | GUEST_ASSERT_EQ(gic_read_ap1r0(), 0); | ||
| 192 | if (eoi_split) | ||
| 193 | gic_set_dir(intid); | ||
| 194 | |||
| 195 | GUEST_ASSERT(!gic_irq_get_active(intid)); | ||
| 196 | GUEST_ASSERT(!gic_irq_get_pending(intid)); | ||
| 197 | } | ||
| 198 | |||
| 199 | static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, | ||
| 200 | uint32_t num, int level, bool expect_failure) | ||
| 201 | { | ||
| 202 | struct kvm_inject_args args = { | ||
| 203 | .cmd = cmd, | ||
| 204 | .first_intid = first_intid, | ||
| 205 | .num = num, | ||
| 206 | .level = level, | ||
| 207 | .expect_failure = expect_failure, | ||
| 208 | }; | ||
| 209 | GUEST_SYNC(&args); | ||
| 210 | } | ||
| 211 | |||
| 212 | #define GUEST_ASSERT_IAR_EMPTY() \ | ||
| 213 | do { \ | ||
| 214 | uint32_t _intid; \ | ||
| 215 | _intid = gic_get_and_ack_irq(); \ | ||
| 216 | GUEST_ASSERT(_intid == 0 || _intid == IAR_SPURIOUS); \ | ||
| 217 | } while (0) | ||
| 218 | |||
| 219 | #define CAT_HELPER(a, b) a ## b | ||
| 220 | #define CAT(a, b) CAT_HELPER(a, b) | ||
| 221 | #define PREFIX guest_irq_handler_ | ||
| 222 | #define GUEST_IRQ_HANDLER_NAME(split, lev) CAT(PREFIX, CAT(split, lev)) | ||
| 223 | #define GENERATE_GUEST_IRQ_HANDLER(split, lev) \ | ||
| 224 | static void CAT(PREFIX, CAT(split, lev))(struct ex_regs *regs) \ | ||
| 225 | { \ | ||
| 226 | guest_irq_generic_handler(split, lev); \ | ||
| 227 | } | ||
| 228 | |||
| 229 | GENERATE_GUEST_IRQ_HANDLER(0, 0); | ||
| 230 | GENERATE_GUEST_IRQ_HANDLER(0, 1); | ||
| 231 | GENERATE_GUEST_IRQ_HANDLER(1, 0); | ||
| 232 | GENERATE_GUEST_IRQ_HANDLER(1, 1); | ||
| 233 | |||
| 234 | static void (*guest_irq_handlers[2][2])(struct ex_regs *) = { | ||
| 235 | {GUEST_IRQ_HANDLER_NAME(0, 0), GUEST_IRQ_HANDLER_NAME(0, 1),}, | ||
| 236 | {GUEST_IRQ_HANDLER_NAME(1, 0), GUEST_IRQ_HANDLER_NAME(1, 1),}, | ||
| 237 | }; | ||
| 238 | |||
| 239 | static void reset_priorities(struct test_args *args) | ||
| 240 | { | ||
| 241 | int i; | ||
| 242 | |||
| 243 | for (i = 0; i < args->nr_irqs; i++) | ||
| 244 | gic_set_priority(i, IRQ_DEFAULT_PRIO_REG); | ||
| 245 | } | ||
| 246 | |||
| 247 | static void guest_set_irq_line(uint32_t intid, uint32_t level) | ||
| 248 | { | ||
| 249 | kvm_inject_call(KVM_SET_IRQ_LINE, intid, 1, level, false); | ||
| 250 | } | ||
| 251 | |||
| 252 | static void test_inject_fail(struct test_args *args, | ||
| 253 | uint32_t intid, kvm_inject_cmd cmd) | ||
| 254 | { | ||
| 255 | reset_stats(); | ||
| 256 | |||
| 257 | _KVM_INJECT(cmd, intid, true); | ||
| 258 | /* no IRQ to handle on entry */ | ||
| 259 | |||
| 260 | GUEST_ASSERT_EQ(irq_handled, 0); | ||
| 261 | GUEST_ASSERT_IAR_EMPTY(); | ||
| 262 | } | ||
| 263 | |||
| 264 | static void guest_inject(struct test_args *args, | ||
| 265 | uint32_t first_intid, uint32_t num, | ||
| 266 | kvm_inject_cmd cmd) | ||
| 267 | { | ||
| 268 | uint32_t i; | ||
| 269 | |||
| 270 | reset_stats(); | ||
| 271 | |||
| 272 | /* Cycle over all priorities to make things more interesting. */ | ||
| 273 | for (i = first_intid; i < num + first_intid; i++) | ||
| 274 | gic_set_priority(i, (i % (KVM_NUM_PRIOS - 1)) << 3); | ||
| 275 | |||
| 276 | asm volatile("msr daifset, #2" : : : "memory"); | ||
| 277 | KVM_INJECT_MULTI(cmd, first_intid, num); | ||
| 278 | |||
| 279 | while (irq_handled < num) { | ||
| 280 | asm volatile("wfi\n" | ||
| 281 | "msr daifclr, #2\n" | ||
| 282 | /* handle IRQ */ | ||
| 283 | "msr daifset, #2\n" | ||
| 284 | : : : "memory"); | ||
| 285 | } | ||
| 286 | asm volatile("msr daifclr, #2" : : : "memory"); | ||
| 287 | |||
| 288 | GUEST_ASSERT_EQ(irq_handled, num); | ||
| 289 | for (i = first_intid; i < num + first_intid; i++) | ||
| 290 | GUEST_ASSERT_EQ(irqnr_received[i], 1); | ||
| 291 | GUEST_ASSERT_IAR_EMPTY(); | ||
| 292 | |||
| 293 | reset_priorities(args); | ||
| 294 | } | ||
| 295 | |||
| 296 | /* | ||
| 297 | * Restore the active state of multiple concurrent IRQs (given by | ||
| 298 | * concurrent_irqs). This does what a live-migration would do on the | ||
| 299 | * destination side assuming there are some active IRQs that were not | ||
| 300 | * deactivated yet. | ||
| 301 | */ | ||
| 302 | static void guest_restore_active(struct test_args *args, | ||
| 303 | uint32_t first_intid, uint32_t num, | ||
| 304 | kvm_inject_cmd cmd) | ||
| 305 | { | ||
| 306 | uint32_t prio, intid, ap1r; | ||
| 307 | int i; | ||
| 308 | |||
| 309 | /* Set the priorities of the first (KVM_NUM_PRIOS - 1) IRQs | ||
| 310 | * in descending order, so intid+1 can preempt intid. | ||
| 311 | */ | ||
| 312 | for (i = 0, prio = (num - 1) * 8; i < num; i++, prio -= 8) { | ||
| 313 | GUEST_ASSERT(prio >= 0); | ||
| 314 | intid = i + first_intid; | ||
| 315 | gic_set_priority(intid, prio); | ||
| 316 | } | ||
| 317 | |||
| 318 | /* In a real migration, KVM would restore all GIC state before running | ||
| 319 | * guest code. | ||
| 320 | */ | ||
| 321 | for (i = 0; i < num; i++) { | ||
| 322 | intid = i + first_intid; | ||
| 323 | KVM_ACTIVATE(cmd, intid); | ||
| 324 | ap1r = gic_read_ap1r0(); | ||
| 325 | ap1r |= 1U << i; | ||
| 326 | gic_write_ap1r0(ap1r); | ||
| 327 | } | ||
| 328 | |||
| 329 | /* This is where the "migration" would occur. */ | ||
| 330 | |||
| 331 | /* finish handling the IRQs starting with the highest priority one. */ | ||
| 332 | for (i = 0; i < num; i++) { | ||
| 333 | intid = num - i - 1 + first_intid; | ||
| 334 | gic_set_eoi(intid); | ||
| 335 | if (args->eoi_split) | ||
| 336 | gic_set_dir(intid); | ||
| 337 | } | ||
| 338 | |||
| 339 | for (i = 0; i < num; i++) | ||
| 340 | GUEST_ASSERT(!gic_irq_get_active(i + first_intid)); | ||
| 341 | GUEST_ASSERT_EQ(gic_read_ap1r0(), 0); | ||
| 342 | GUEST_ASSERT_IAR_EMPTY(); | ||
| 343 | } | ||
| 344 | |||
| 345 | /* | ||
| 346 | * Polls the IAR until it's not a spurious interrupt. | ||
| 347 | * | ||
| 348 | * This function should only be used in test_inject_preemption (with IRQs | ||
| 349 | * masked). | ||
| 350 | */ | ||
| 351 | static uint32_t wait_for_and_activate_irq(void) | ||
| 352 | { | ||
| 353 | uint32_t intid; | ||
| 354 | |||
| 355 | do { | ||
| 356 | asm volatile("wfi" : : : "memory"); | ||
| 357 | intid = gic_get_and_ack_irq(); | ||
| 358 | } while (intid == IAR_SPURIOUS); | ||
| 359 | |||
| 360 | return intid; | ||
| 361 | } | ||
| 362 | |||
| 363 | /* | ||
| 364 | * Inject multiple concurrent IRQs (num IRQs starting at first_intid) and | ||
| 365 | * handle them without handling the actual exceptions. This is done by masking | ||
| 366 | * interrupts for the whole test. | ||
| 367 | */ | ||
| 368 | static void test_inject_preemption(struct test_args *args, | ||
| 369 | uint32_t first_intid, int num, | ||
| 370 | kvm_inject_cmd cmd) | ||
| 371 | { | ||
| 372 | uint32_t intid, prio, step = KVM_PRIO_STEPS; | ||
| 373 | int i; | ||
| 374 | |||
| 375 | /* Set the priorities of the first (KVM_NUM_PRIOS - 1) IRQs | ||
| 376 | * in descending order, so intid+1 can preempt intid. | ||
| 377 | */ | ||
| 378 | for (i = 0, prio = (num - 1) * step; i < num; i++, prio -= step) { | ||
| 379 | GUEST_ASSERT(prio >= 0); | ||
| 380 | intid = i + first_intid; | ||
| 381 | gic_set_priority(intid, prio); | ||
| 382 | } | ||
| 383 | |||
| 384 | local_irq_disable(); | ||
| 385 | |||
| 386 | for (i = 0; i < num; i++) { | ||
| 387 | uint32_t tmp; | ||
| 388 | intid = i + first_intid; | ||
| 389 | KVM_INJECT(cmd, intid); | ||
| 390 | /* Each successive IRQ will preempt the previous one. */ | ||
| 391 | tmp = wait_for_and_activate_irq(); | ||
| 392 | GUEST_ASSERT_EQ(tmp, intid); | ||
| 393 | if (args->level_sensitive) | ||
| 394 | guest_set_irq_line(intid, 0); | ||
| 395 | } | ||
| 396 | |||
| 397 | /* finish handling the IRQs starting with the highest priority one. */ | ||
| 398 | for (i = 0; i < num; i++) { | ||
| 399 | intid = num - i - 1 + first_intid; | ||
| 400 | gic_set_eoi(intid); | ||
| 401 | if (args->eoi_split) | ||
| 402 | gic_set_dir(intid); | ||
| 403 | } | ||
| 404 | |||
| 405 | local_irq_enable(); | ||
| 406 | |||
| 407 | for (i = 0; i < num; i++) | ||
| 408 | GUEST_ASSERT(!gic_irq_get_active(i + first_intid)); | ||
| 409 | GUEST_ASSERT_EQ(gic_read_ap1r0(), 0); | ||
| 410 | GUEST_ASSERT_IAR_EMPTY(); | ||
| 411 | |||
| 412 | reset_priorities(args); | ||
| 413 | } | ||
| 414 | |||
| 415 | static void test_injection(struct test_args *args, struct kvm_inject_desc *f) | ||
| 416 | { | ||
| 417 | uint32_t nr_irqs = args->nr_irqs; | ||
| 418 | |||
| 419 | if (f->sgi) { | ||
| 420 | guest_inject(args, MIN_SGI, 1, f->cmd); | ||
| 421 | guest_inject(args, 0, 16, f->cmd); | ||
| 422 | } | ||
| 423 | |||
| 424 | if (f->ppi) | ||
| 425 | guest_inject(args, MIN_PPI, 1, f->cmd); | ||
| 426 | |||
| 427 | if (f->spi) { | ||
| 428 | guest_inject(args, MIN_SPI, 1, f->cmd); | ||
| 429 | guest_inject(args, nr_irqs - 1, 1, f->cmd); | ||
| 430 | guest_inject(args, MIN_SPI, nr_irqs - MIN_SPI, f->cmd); | ||
| 431 | } | ||
| 432 | } | ||
| 433 | |||
| 434 | static void test_injection_failure(struct test_args *args, | ||
| 435 | struct kvm_inject_desc *f) | ||
| 436 | { | ||
| 437 | uint32_t bad_intid[] = { args->nr_irqs, 1020, 1024, 1120, 5120, ~0U, }; | ||
| 438 | int i; | ||
| 439 | |||
| 440 | for (i = 0; i < ARRAY_SIZE(bad_intid); i++) | ||
| 441 | test_inject_fail(args, bad_intid[i], f->cmd); | ||
| 442 | } | ||
| 443 | |||
| 444 | static void test_preemption(struct test_args *args, struct kvm_inject_desc *f) | ||
| 445 | { | ||
| 446 | /* | ||
| 447 | * Test up to 4 levels of preemption. The reason is that KVM doesn't | ||
| 448 | * currently implement the ability to have more than the number-of-LRs | ||
| 449 | * number of concurrently active IRQs. The number of LRs implemented is | ||
| 450 | * IMPLEMENTATION DEFINED, however, it seems that most implement 4. | ||
| 451 | */ | ||
| 452 | if (f->sgi) | ||
| 453 | test_inject_preemption(args, MIN_SGI, 4, f->cmd); | ||
| 454 | |||
| 455 | if (f->ppi) | ||
| 456 | test_inject_preemption(args, MIN_PPI, 4, f->cmd); | ||
| 457 | |||
| 458 | if (f->spi) | ||
| 459 | test_inject_preemption(args, MIN_SPI, 4, f->cmd); | ||
| 460 | } | ||
| 461 | |||
| 462 | static void test_restore_active(struct test_args *args, struct kvm_inject_desc *f) | ||
| 463 | { | ||
| 464 | /* Test up to 4 active IRQs. Same reason as in test_preemption. */ | ||
| 465 | if (f->sgi) | ||
| 466 | guest_restore_active(args, MIN_SGI, 4, f->cmd); | ||
| 467 | |||
| 468 | if (f->ppi) | ||
| 469 | guest_restore_active(args, MIN_PPI, 4, f->cmd); | ||
| 470 | |||
| 471 | if (f->spi) | ||
| 472 | guest_restore_active(args, MIN_SPI, 4, f->cmd); | ||
| 473 | } | ||
| 474 | |||
| 475 | static void guest_code(struct test_args args) | ||
| 476 | { | ||
| 477 | uint32_t i, nr_irqs = args.nr_irqs; | ||
| 478 | bool level_sensitive = args.level_sensitive; | ||
| 479 | struct kvm_inject_desc *f, *inject_fns; | ||
| 480 | |||
| 481 | gic_init(GIC_V3, 1, dist, redist); | ||
| 482 | |||
| 483 | for (i = 0; i < nr_irqs; i++) | ||
| 484 | gic_irq_enable(i); | ||
| 485 | |||
| 486 | for (i = MIN_SPI; i < nr_irqs; i++) | ||
| 487 | gic_irq_set_config(i, !args.level_sensitive); | ||
| 488 | |||
| 489 | gic_set_eoi_split(args.eoi_split); | ||
| 490 | |||
| 491 | reset_priorities(&args); | ||
| 492 | gic_set_priority_mask(CPU_PRIO_MASK); | ||
| 493 | |||
| 494 | inject_fns = level_sensitive ? inject_level_fns | ||
| 495 | : inject_edge_fns; | ||
| 496 | |||
| 497 | local_irq_enable(); | ||
| 498 | |||
| 499 | /* Start the tests. */ | ||
| 500 | for_each_supported_inject_fn(&args, inject_fns, f) { | ||
| 501 | test_injection(&args, f); | ||
| 502 | test_preemption(&args, f); | ||
| 503 | test_injection_failure(&args, f); | ||
| 504 | } | ||
| 505 | |||
| 506 | /* Restore the active state of IRQs. This would happen when live | ||
| 507 | * migrating IRQs in the middle of being handled. | ||
| 508 | */ | ||
| 509 | for_each_supported_activate_fn(&args, set_active_fns, f) | ||
| 510 | test_restore_active(&args, f); | ||
| 511 | |||
| 512 | GUEST_DONE(); | ||
| 513 | } | ||
| 514 | |||
| 515 | static void kvm_irq_line_check(struct kvm_vm *vm, uint32_t intid, int level, | ||
| 516 | struct test_args *test_args, bool expect_failure) | ||
| 517 | { | ||
| 518 | int ret; | ||
| 519 | |||
| 520 | if (!expect_failure) { | ||
| 521 | kvm_arm_irq_line(vm, intid, level); | ||
| 522 | } else { | ||
| 523 | /* The interface doesn't allow larger intid's. */ | ||
| 524 | if (intid > KVM_ARM_IRQ_NUM_MASK) | ||
| 525 | return; | ||
| 526 | |||
| 527 | ret = _kvm_arm_irq_line(vm, intid, level); | ||
| 528 | TEST_ASSERT(ret != 0 && errno == EINVAL, | ||
| 529 | "Bad intid %i did not cause KVM_IRQ_LINE " | ||
| 530 | "error: rc: %i errno: %i", intid, ret, errno); | ||
| 531 | } | ||
| 532 | } | ||
| 533 | |||
| 534 | void kvm_irq_set_level_info_check(int gic_fd, uint32_t intid, int level, | ||
| 535 | bool expect_failure) | ||
| 536 | { | ||
| 537 | if (!expect_failure) { | ||
| 538 | kvm_irq_set_level_info(gic_fd, intid, level); | ||
| 539 | } else { | ||
| 540 | int ret = _kvm_irq_set_level_info(gic_fd, intid, level); | ||
| 541 | /* | ||
| 542 | * The kernel silently fails for invalid SPIs and SGIs (which | ||
| 543 | * are not level-sensitive). It only checks for intid to not | ||
| 544 | * spill over 1U << 10 (the max reserved SPI). Also, callers | ||
| 545 | * are supposed to mask the intid with 0x3ff (1023). | ||
| 546 | */ | ||
| 547 | if (intid > VGIC_MAX_RESERVED) | ||
| 548 | TEST_ASSERT(ret != 0 && errno == EINVAL, | ||
| 549 | "Bad intid %i did not cause VGIC_GRP_LEVEL_INFO " | ||
| 550 | "error: rc: %i errno: %i", intid, ret, errno); | ||
| 551 | else | ||
| 552 | TEST_ASSERT(!ret, "KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO " | ||
| 553 | "for intid %i failed, rc: %i errno: %i", | ||
| 554 | intid, ret, errno); | ||
| 555 | } | ||
| 556 | } | ||
| 557 | |||
| 558 | static void kvm_set_gsi_routing_irqchip_check(struct kvm_vm *vm, | ||
| 559 | uint32_t intid, uint32_t num, uint32_t kvm_max_routes, | ||
| 560 | bool expect_failure) | ||
| 561 | { | ||
| 562 | struct kvm_irq_routing *routing; | ||
| 563 | int ret; | ||
| 564 | uint64_t i; | ||
| 565 | |||
| 566 | assert(num <= kvm_max_routes && kvm_max_routes <= KVM_MAX_IRQ_ROUTES); | ||
| 567 | |||
| 568 | routing = kvm_gsi_routing_create(); | ||
| 569 | for (i = intid; i < (uint64_t)intid + num; i++) | ||
| 570 | kvm_gsi_routing_irqchip_add(routing, i - MIN_SPI, i - MIN_SPI); | ||
| 571 | |||
| 572 | if (!expect_failure) { | ||
| 573 | kvm_gsi_routing_write(vm, routing); | ||
| 574 | } else { | ||
| 575 | ret = _kvm_gsi_routing_write(vm, routing); | ||
| 576 | /* The kernel only checks for KVM_IRQCHIP_NUM_PINS. */ | ||
| 577 | if (intid >= KVM_IRQCHIP_NUM_PINS) | ||
| 578 | TEST_ASSERT(ret != 0 && errno == EINVAL, | ||
| 579 | "Bad intid %u did not cause KVM_SET_GSI_ROUTING " | ||
| 580 | "error: rc: %i errno: %i", intid, ret, errno); | ||
| 581 | else | ||
| 582 | TEST_ASSERT(ret == 0, "KVM_SET_GSI_ROUTING " | ||
| 583 | "for intid %i failed, rc: %i errno: %i", | ||
| 584 | intid, ret, errno); | ||
| 585 | } | ||
| 586 | } | ||
| 587 | |||
| 588 | static void kvm_irq_write_ispendr_check(int gic_fd, uint32_t intid, | ||
| 589 | uint32_t vcpu, bool expect_failure) | ||
| 590 | { | ||
| 591 | /* | ||
| 592 | * Ignore this when expecting failure as invalid intids will lead to | ||
| 593 | * either trying to inject SGIs when we configured the test to be | ||
| 594 | * level_sensitive (or the reverse), or inject large intids which | ||
| 595 | * will lead to writing above the ISPENDR register space (and we | ||
| 596 | * don't want to do that either). | ||
| 597 | */ | ||
| 598 | if (!expect_failure) | ||
| 599 | kvm_irq_write_ispendr(gic_fd, intid, vcpu); | ||
| 600 | } | ||
| 601 | |||
| 602 | static void kvm_routing_and_irqfd_check(struct kvm_vm *vm, | ||
| 603 | uint32_t intid, uint32_t num, uint32_t kvm_max_routes, | ||
| 604 | bool expect_failure) | ||
| 605 | { | ||
| 606 | int fd[MAX_SPI]; | ||
| 607 | uint64_t val; | ||
| 608 | int ret, f; | ||
| 609 | uint64_t i; | ||
| 610 | |||
| 611 | /* | ||
| 612 | * There is no way to try injecting an SGI or PPI as the interface | ||
| 613 | * starts counting from the first SPI (above the private ones), so just | ||
| 614 | * exit. | ||
| 615 | */ | ||
| 616 | if (INTID_IS_SGI(intid) || INTID_IS_PPI(intid)) | ||
| 617 | return; | ||
| 618 | |||
| 619 | kvm_set_gsi_routing_irqchip_check(vm, intid, num, | ||
| 620 | kvm_max_routes, expect_failure); | ||
| 621 | |||
| 622 | /* | ||
| 623 | * If expect_failure, then just to inject anyway. These | ||
| 624 | * will silently fail. And in any case, the guest will check | ||
| 625 | * that no actual interrupt was injected for those cases. | ||
| 626 | */ | ||
| 627 | |||
| 628 | for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { | ||
| 629 | fd[f] = eventfd(0, 0); | ||
| 630 | TEST_ASSERT(fd[f] != -1, | ||
| 631 | "eventfd failed, errno: %i\n", errno); | ||
| 632 | } | ||
| 633 | |||
| 634 | for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { | ||
| 635 | struct kvm_irqfd irqfd = { | ||
| 636 | .fd = fd[f], | ||
| 637 | .gsi = i - MIN_SPI, | ||
| 638 | }; | ||
| 639 | assert(i <= (uint64_t)UINT_MAX); | ||
| 640 | vm_ioctl(vm, KVM_IRQFD, &irqfd); | ||
| 641 | } | ||
| 642 | |||
| 643 | for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { | ||
| 644 | val = 1; | ||
| 645 | ret = write(fd[f], &val, sizeof(uint64_t)); | ||
| 646 | TEST_ASSERT(ret == sizeof(uint64_t), | ||
| 647 | "Write to KVM_IRQFD failed with ret: %d\n", ret); | ||
| 648 | } | ||
| 649 | |||
| 650 | for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) | ||
| 651 | close(fd[f]); | ||
| 652 | } | ||
| 653 | |||
| 654 | /* handles the valid case: intid=0xffffffff num=1 */ | ||
| 655 | #define for_each_intid(first, num, tmp, i) \ | ||
| 656 | for ((tmp) = (i) = (first); \ | ||
| 657 | (tmp) < (uint64_t)(first) + (uint64_t)(num); \ | ||
| 658 | (tmp)++, (i)++) | ||
| 659 | |||
| 660 | static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, | ||
| 661 | struct kvm_inject_args *inject_args, | ||
| 662 | struct test_args *test_args) | ||
| 663 | { | ||
| 664 | kvm_inject_cmd cmd = inject_args->cmd; | ||
| 665 | uint32_t intid = inject_args->first_intid; | ||
| 666 | uint32_t num = inject_args->num; | ||
| 667 | int level = inject_args->level; | ||
| 668 | bool expect_failure = inject_args->expect_failure; | ||
| 669 | uint64_t tmp; | ||
| 670 | uint32_t i; | ||
| 671 | |||
| 672 | /* handles the valid case: intid=0xffffffff num=1 */ | ||
| 673 | assert(intid < UINT_MAX - num || num == 1); | ||
| 674 | |||
| 675 | switch (cmd) { | ||
| 676 | case KVM_INJECT_EDGE_IRQ_LINE: | ||
| 677 | for_each_intid(intid, num, tmp, i) | ||
| 678 | kvm_irq_line_check(vm, i, 1, test_args, | ||
| 679 | expect_failure); | ||
| 680 | for_each_intid(intid, num, tmp, i) | ||
| 681 | kvm_irq_line_check(vm, i, 0, test_args, | ||
| 682 | expect_failure); | ||
| 683 | break; | ||
| 684 | case KVM_SET_IRQ_LINE: | ||
| 685 | for_each_intid(intid, num, tmp, i) | ||
| 686 | kvm_irq_line_check(vm, i, level, test_args, | ||
| 687 | expect_failure); | ||
| 688 | break; | ||
| 689 | case KVM_SET_IRQ_LINE_HIGH: | ||
| 690 | for_each_intid(intid, num, tmp, i) | ||
| 691 | kvm_irq_line_check(vm, i, 1, test_args, | ||
| 692 | expect_failure); | ||
| 693 | break; | ||
| 694 | case KVM_SET_LEVEL_INFO_HIGH: | ||
| 695 | for_each_intid(intid, num, tmp, i) | ||
| 696 | kvm_irq_set_level_info_check(gic_fd, i, 1, | ||
| 697 | expect_failure); | ||
| 698 | break; | ||
| 699 | case KVM_INJECT_IRQFD: | ||
| 700 | kvm_routing_and_irqfd_check(vm, intid, num, | ||
| 701 | test_args->kvm_max_routes, | ||
| 702 | expect_failure); | ||
| 703 | break; | ||
| 704 | case KVM_WRITE_ISPENDR: | ||
| 705 | for (i = intid; i < intid + num; i++) | ||
| 706 | kvm_irq_write_ispendr_check(gic_fd, i, | ||
| 707 | VCPU_ID, expect_failure); | ||
| 708 | break; | ||
| 709 | case KVM_WRITE_ISACTIVER: | ||
| 710 | for (i = intid; i < intid + num; i++) | ||
| 711 | kvm_irq_write_isactiver(gic_fd, i, VCPU_ID); | ||
| 712 | break; | ||
| 713 | default: | ||
| 714 | break; | ||
| 715 | } | ||
| 716 | } | ||
| 717 | |||
| 718 | static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, | ||
| 719 | struct kvm_inject_args *args) | ||
| 720 | { | ||
| 721 | struct kvm_inject_args *kvm_args_hva; | ||
| 722 | vm_vaddr_t kvm_args_gva; | ||
| 723 | |||
| 724 | kvm_args_gva = uc->args[1]; | ||
| 725 | kvm_args_hva = (struct kvm_inject_args *)addr_gva2hva(vm, kvm_args_gva); | ||
| 726 | memcpy(args, kvm_args_hva, sizeof(struct kvm_inject_args)); | ||
| 727 | } | ||
| 728 | |||
| 729 | static void print_args(struct test_args *args) | ||
| 730 | { | ||
| 731 | printf("nr-irqs=%d level-sensitive=%d eoi-split=%d\n", | ||
| 732 | args->nr_irqs, args->level_sensitive, | ||
| 733 | args->eoi_split); | ||
| 734 | } | ||
| 735 | |||
| 736 | static void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split) | ||
| 737 | { | ||
| 738 | struct ucall uc; | ||
| 739 | int gic_fd; | ||
| 740 | struct kvm_vm *vm; | ||
| 741 | struct kvm_inject_args inject_args; | ||
| 742 | |||
| 743 | struct test_args args = { | ||
| 744 | .nr_irqs = nr_irqs, | ||
| 745 | .level_sensitive = level_sensitive, | ||
| 746 | .eoi_split = eoi_split, | ||
| 747 | .kvm_max_routes = kvm_check_cap(KVM_CAP_IRQ_ROUTING), | ||
| 748 | .kvm_supports_irqfd = kvm_check_cap(KVM_CAP_IRQFD), | ||
| 749 | }; | ||
| 750 | |||
| 751 | print_args(&args); | ||
| 752 | |||
| 753 | vm = vm_create_default(VCPU_ID, 0, guest_code); | ||
| 754 | ucall_init(vm, NULL); | ||
| 755 | |||
| 756 | vm_init_descriptor_tables(vm); | ||
| 757 | vcpu_init_descriptor_tables(vm, VCPU_ID); | ||
| 758 | |||
| 759 | /* Setup the guest args page (so it gets the args). */ | ||
| 760 | vcpu_args_set(vm, 0, 1, args); | ||
| 761 | |||
| 762 | gic_fd = vgic_v3_setup(vm, 1, nr_irqs, | ||
| 763 | GICD_BASE_GPA, GICR_BASE_GPA); | ||
| 764 | |||
| 765 | vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, | ||
| 766 | guest_irq_handlers[args.eoi_split][args.level_sensitive]); | ||
| 767 | |||
| 768 | while (1) { | ||
| 769 | vcpu_run(vm, VCPU_ID); | ||
| 770 | |||
| 771 | switch (get_ucall(vm, VCPU_ID, &uc)) { | ||
| 772 | case UCALL_SYNC: | ||
| 773 | kvm_inject_get_call(vm, &uc, &inject_args); | ||
| 774 | run_guest_cmd(vm, gic_fd, &inject_args, &args); | ||
| 775 | break; | ||
| 776 | case UCALL_ABORT: | ||
| 777 | TEST_FAIL("%s at %s:%ld\n\tvalues: %#lx, %#lx", | ||
| 778 | (const char *)uc.args[0], | ||
| 779 | __FILE__, uc.args[1], uc.args[2], uc.args[3]); | ||
| 780 | break; | ||
| 781 | case UCALL_DONE: | ||
| 782 | goto done; | ||
| 783 | default: | ||
| 784 | TEST_FAIL("Unknown ucall %lu", uc.cmd); | ||
| 785 | } | ||
| 786 | } | ||
| 787 | |||
| 788 | done: | ||
| 789 | close(gic_fd); | ||
| 790 | kvm_vm_free(vm); | ||
| 791 | } | ||
| 792 | |||
| 793 | static void help(const char *name) | ||
| 794 | { | ||
| 795 | printf( | ||
| 796 | "\n" | ||
| 797 | "usage: %s [-n num_irqs] [-e eoi_split] [-l level_sensitive]\n", name); | ||
| 798 | printf(" -n: specify number of IRQs to setup the vgic with. " | ||
| 799 | "It has to be a multiple of 32 and between 64 and 1024.\n"); | ||
| 800 | printf(" -e: if 1 then EOI is split into a write to DIR on top " | ||
| 801 | "of writing EOI.\n"); | ||
| 802 | printf(" -l: specify whether the IRQs are level-sensitive (1) or not (0)."); | ||
| 803 | puts(""); | ||
| 804 | exit(1); | ||
| 805 | } | ||
| 806 | |||
| 807 | int main(int argc, char **argv) | ||
| 808 | { | ||
| 809 | uint32_t nr_irqs = 64; | ||
| 810 | bool default_args = true; | ||
| 811 | bool level_sensitive = false; | ||
| 812 | int opt; | ||
| 813 | bool eoi_split = false; | ||
| 814 | |||
| 815 | /* Tell stdout not to buffer its content */ | ||
| 816 | setbuf(stdout, NULL); | ||
| 817 | |||
| 818 | while ((opt = getopt(argc, argv, "hn:e:l:")) != -1) { | ||
| 819 | switch (opt) { | ||
| 820 | case 'n': | ||
| 821 | nr_irqs = atoi(optarg); | ||
| 822 | if (nr_irqs > 1024 || nr_irqs % 32) | ||
| 823 | help(argv[0]); | ||
| 824 | break; | ||
| 825 | case 'e': | ||
| 826 | eoi_split = (bool)atoi(optarg); | ||
| 827 | default_args = false; | ||
| 828 | break; | ||
| 829 | case 'l': | ||
| 830 | level_sensitive = (bool)atoi(optarg); | ||
| 831 | default_args = false; | ||
| 832 | break; | ||
| 833 | case 'h': | ||
| 834 | default: | ||
| 835 | help(argv[0]); | ||
| 836 | break; | ||
| 837 | } | ||
| 838 | } | ||
| 839 | |||
| 840 | /* If the user just specified nr_irqs and/or gic_version, then run all | ||
| 841 | * combinations. | ||
| 842 | */ | ||
| 843 | if (default_args) { | ||
| 844 | test_vgic(nr_irqs, false /* level */, false /* eoi_split */); | ||
| 845 | test_vgic(nr_irqs, false /* level */, true /* eoi_split */); | ||
| 846 | test_vgic(nr_irqs, true /* level */, false /* eoi_split */); | ||
| 847 | test_vgic(nr_irqs, true /* level */, true /* eoi_split */); | ||
| 848 | } else { | ||
| 849 | test_vgic(nr_irqs, level_sensitive, eoi_split); | ||
| 850 | } | ||
| 851 | |||
| 852 | return 0; | ||
| 853 | } | ||
| @@ -11,11 +11,37 @@ enum gic_type { · 10 unmodified lines | |||
| 11 | GIC_TYPE_MAX, | 11 | GIC_TYPE_MAX, |
| 12 | }; | 12 | }; |
| 13 | 13 | ||
| 14 | #define MIN_SGI 0 | ||
| 15 | #define MIN_PPI 16 | ||
| 16 | #define MIN_SPI 32 | ||
| 17 | #define MAX_SPI 1019 | ||
| 18 | #define IAR_SPURIOUS 1023 | ||
| 19 | |||
| 20 | #define INTID_IS_SGI(intid) (0 <= (intid) && (intid) < MIN_PPI) | ||
| 21 | #define INTID_IS_PPI(intid) (MIN_PPI <= (intid) && (intid) < MIN_SPI) | ||
| 22 | #define INTID_IS_SPI(intid) (MIN_SPI <= (intid) && (intid) <= MAX_SPI) | ||
| 23 | |||
| 14 | void gic_init(enum gic_type type, unsigned int nr_cpus, | 24 | void gic_init(enum gic_type type, unsigned int nr_cpus, |
| 15 | void *dist_base, void *redist_base); | 25 | void *dist_base, void *redist_base); |
| 16 | void gic_irq_enable(unsigned int intid); | 26 | void gic_irq_enable(unsigned int intid); |
| 17 | void gic_irq_disable(unsigned int intid); | 27 | void gic_irq_disable(unsigned int intid); |
| 18 | unsigned int gic_get_and_ack_irq(void); | 28 | unsigned int gic_get_and_ack_irq(void); |
| 19 | void gic_set_eoi(unsigned int intid); | 29 | void gic_set_eoi(unsigned int intid); |
| 30 | void gic_set_dir(unsigned int intid); | ||
| 31 | |||
| 32 | /* | ||
| 33 | * Sets the EOI mode. When split is false, EOI just drops the priority. When | ||
| 34 | * split is true, EOI drops the priority and deactivates the interrupt. | ||
| 35 | */ | ||
| 36 | void gic_set_eoi_split(bool split); | ||
| 37 | void gic_set_priority_mask(uint64_t mask); | ||
| 38 | void gic_set_priority(uint32_t intid, uint32_t prio); | ||
| 39 | void gic_irq_set_active(unsigned int intid); | ||
| 40 | void gic_irq_clear_active(unsigned int intid); | ||
| 41 | bool gic_irq_get_active(unsigned int intid); | ||
| 42 | void gic_irq_set_pending(unsigned int intid); | ||
| 43 | void gic_irq_clear_pending(unsigned int intid); | ||
| 44 | bool gic_irq_get_pending(unsigned int intid); | ||
| 45 | void gic_irq_set_config(unsigned int intid, bool is_edge); | ||
| 20 | 46 | ||
| 21 | #endif /* SELFTEST_KVM_GIC_H */ | 47 | #endif /* SELFTEST_KVM_GIC_H */ |
| @@ -16,8 +16,12 @@ · 15 unmodified lines | |||
| 16 | #define GICD_IGROUPR 0x0080 | 16 | #define GICD_IGROUPR 0x0080 |
| 17 | #define GICD_ISENABLER 0x0100 | 17 | #define GICD_ISENABLER 0x0100 |
| 18 | #define GICD_ICENABLER 0x0180 | 18 | #define GICD_ICENABLER 0x0180 |
| 19 | #define GICD_ISPENDR 0x0200 | ||
| 20 | #define GICD_ICPENDR 0x0280 | ||
| 19 | #define GICD_ICACTIVER 0x0380 | 21 | #define GICD_ICACTIVER 0x0380 |
| 22 | #define GICD_ISACTIVER 0x0300 | ||
| 20 | #define GICD_IPRIORITYR 0x0400 | 23 | #define GICD_IPRIORITYR 0x0400 |
| 24 | #define GICD_ICFGR 0x0C00 | ||
| 21 | 25 | ||
| 22 | /* | 26 | /* |
| 23 | * The assumption is that the guest runs in a non-secure mode. | 27 | * The assumption is that the guest runs in a non-secure mode. |
| @@ -49,16 +53,24 @@ · 25 unmodified lines | |||
| 49 | #define GICR_IGROUPR0 GICD_IGROUPR | 53 | #define GICR_IGROUPR0 GICD_IGROUPR |
| 50 | #define GICR_ISENABLER0 GICD_ISENABLER | 54 | #define GICR_ISENABLER0 GICD_ISENABLER |
| 51 | #define GICR_ICENABLER0 GICD_ICENABLER | 55 | #define GICR_ICENABLER0 GICD_ICENABLER |
| 56 | #define GICR_ISPENDR0 GICD_ISPENDR | ||
| 57 | #define GICR_ISACTIVER0 GICD_ISACTIVER | ||
| 52 | #define GICR_ICACTIVER0 GICD_ICACTIVER | 58 | #define GICR_ICACTIVER0 GICD_ICACTIVER |
| 59 | #define GICR_ICENABLER GICD_ICENABLER | ||
| 60 | #define GICR_ICACTIVER GICD_ICACTIVER | ||
| 53 | #define GICR_IPRIORITYR0 GICD_IPRIORITYR | 61 | #define GICR_IPRIORITYR0 GICD_IPRIORITYR |
| 54 | 62 | ||
| 55 | /* CPU interface registers */ | 63 | /* CPU interface registers */ |
| 56 | #define SYS_ICC_PMR_EL1 sys_reg(3, 0, 4, 6, 0) | 64 | #define SYS_ICC_PMR_EL1 sys_reg(3, 0, 4, 6, 0) |
| 57 | #define SYS_ICC_IAR1_EL1 sys_reg(3, 0, 12, 12, 0) | 65 | #define SYS_ICC_IAR1_EL1 sys_reg(3, 0, 12, 12, 0) |
| 58 | #define SYS_ICC_EOIR1_EL1 sys_reg(3, 0, 12, 12, 1) | 66 | #define SYS_ICC_EOIR1_EL1 sys_reg(3, 0, 12, 12, 1) |
| 67 | #define SYS_ICC_DIR_EL1 sys_reg(3, 0, 12, 11, 1) | ||
| 68 | #define SYS_ICC_CTLR_EL1 sys_reg(3, 0, 12, 12, 4) | ||
| 59 | #define SYS_ICC_SRE_EL1 sys_reg(3, 0, 12, 12, 5) | 69 | #define SYS_ICC_SRE_EL1 sys_reg(3, 0, 12, 12, 5) |
| 60 | #define SYS_ICC_GRPEN1_EL1 sys_reg(3, 0, 12, 12, 7) | 70 | #define SYS_ICC_GRPEN1_EL1 sys_reg(3, 0, 12, 12, 7) |
| 61 | 71 | ||
| 72 | #define SYS_ICV_AP1R0_EL1 sys_reg(3, 0, 12, 9, 0) | ||
| 73 | |||
| 62 | #define ICC_PMR_DEF_PRIO 0xf0 | 74 | #define ICC_PMR_DEF_PRIO 0xf0 |
| 63 | 75 | ||
| 64 | #define ICC_SRE_EL1_SRE (1U << 0) | 76 | #define ICC_SRE_EL1_SRE (1U << 0) |
| @@ -113,6 +113,9 @@ enum { · 112 unmodified lines | |||
| 113 | #define ESR_EC_WP_CURRENT 0x35 | 113 | #define ESR_EC_WP_CURRENT 0x35 |
| 114 | #define ESR_EC_BRK_INS 0x3c | 114 | #define ESR_EC_BRK_INS 0x3c |
| 115 | 115 | ||
| 116 | void aarch64_get_supported_page_sizes(uint32_t ipa, | ||
| 117 | bool *ps4k, bool *ps16k, bool *ps64k); | ||
| 118 | |||
| 116 | void vm_init_descriptor_tables(struct kvm_vm *vm); | 119 | void vm_init_descriptor_tables(struct kvm_vm *vm); |
| 117 | void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid); | 120 | void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid); |
| 118 | 121 | ||
| @@ -14,7 +14,21 @@ · 13 unmodified lines | |||
| 14 | ((uint64_t)(flags) << 12) | \ | 14 | ((uint64_t)(flags) << 12) | \ |
| 15 | index) | 15 | index) |
| 16 | 16 | ||
| 17 | int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, | 17 | int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs, |
| 18 | uint64_t gicd_base_gpa, uint64_t gicr_base_gpa); | 18 | uint64_t gicd_base_gpa, uint64_t gicr_base_gpa); |
| 19 | 19 | ||
| 20 | #endif /* SELFTEST_KVM_VGIC_H */ | 20 | #define VGIC_MAX_RESERVED 1023 |
| 21 | |||
| 22 | void kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level); | ||
| 23 | int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level); | ||
| 24 | |||
| 25 | void kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level); | ||
| 26 | int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level); | ||
| 27 | |||
| 28 | /* The vcpu arg only applies to private interrupts. */ | ||
| 29 | void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, uint32_t vcpu); | ||
| 30 | void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, uint32_t vcpu); | ||
| 31 | |||
| 32 | #define KVM_IRQCHIP_NUM_PINS (1020 - 32) | ||
| 33 | |||
| 34 | #endif // SELFTEST_KVM_VGIC_H | ||
| @@ -42,18 +42,26 @@ enum vm_guest_mode { · 41 unmodified lines | |||
| 42 | VM_MODE_P52V48_4K, | 42 | VM_MODE_P52V48_4K, |
| 43 | VM_MODE_P52V48_64K, | 43 | VM_MODE_P52V48_64K, |
| 44 | VM_MODE_P48V48_4K, | 44 | VM_MODE_P48V48_4K, |
| 45 | VM_MODE_P48V48_16K, | ||
| 45 | VM_MODE_P48V48_64K, | 46 | VM_MODE_P48V48_64K, |
| 46 | VM_MODE_P40V48_4K, | 47 | VM_MODE_P40V48_4K, |
| 48 | VM_MODE_P40V48_16K, | ||
| 47 | VM_MODE_P40V48_64K, | 49 | VM_MODE_P40V48_64K, |
| 48 | VM_MODE_PXXV48_4K, /* For 48bits VA but ANY bits PA */ | 50 | VM_MODE_PXXV48_4K, /* For 48bits VA but ANY bits PA */ |
| 49 | VM_MODE_P47V64_4K, | 51 | VM_MODE_P47V64_4K, |
| 50 | VM_MODE_P44V64_4K, | 52 | VM_MODE_P44V64_4K, |
| 53 | VM_MODE_P36V48_4K, | ||
| 54 | VM_MODE_P36V48_16K, | ||
| 55 | VM_MODE_P36V48_64K, | ||
| 56 | VM_MODE_P36V47_16K, | ||
| 51 | NUM_VM_MODES, | 57 | NUM_VM_MODES, |
| 52 | }; | 58 | }; |
| 53 | 59 | ||
| 54 | #if defined(__aarch64__) | 60 | #if defined(__aarch64__) |
| 55 | 61 | ||
| 56 | #define VM_MODE_DEFAULT VM_MODE_P40V48_4K | 62 | extern enum vm_guest_mode vm_mode_default; |
| 63 | |||
| 64 | #define VM_MODE_DEFAULT vm_mode_default | ||
| 57 | #define MIN_PAGE_SHIFT 12U | 65 | #define MIN_PAGE_SHIFT 12U |
| 58 | #define ptes_per_page(page_size) ((page_size) / 8) | 66 | #define ptes_per_page(page_size) ((page_size) / 8) |
| 59 | 67 | ||
| @@ -240,6 +248,8 @@ int _kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, · 180 unmodified lines | |||
| 240 | void *val, bool write); | 248 | void *val, bool write); |
| 241 | int kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, | 249 | int kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, |
| 242 | void *val, bool write); | 250 | void *val, bool write); |
| 251 | void kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level); | ||
| 252 | int _kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level); | ||
| 243 | 253 | ||
| 244 | int _vcpu_has_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, | 254 | int _vcpu_has_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, |
| 245 | uint64_t attr); | 255 | uint64_t attr); |
| @@ -250,6 +260,14 @@ int _vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, · 4 unmodified lines | |||
| 250 | int vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, | 260 | int vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, |
| 251 | uint64_t attr, void *val, bool write); | 261 | uint64_t attr, void *val, bool write); |
| 252 | 262 | ||
| 263 | #define KVM_MAX_IRQ_ROUTES 4096 | ||
| 264 | |||
| 265 | struct kvm_irq_routing *kvm_gsi_routing_create(void); | ||
| 266 | void kvm_gsi_routing_irqchip_add(struct kvm_irq_routing *routing, | ||
| 267 | uint32_t gsi, uint32_t pin); | ||
| 268 | int _kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); | ||
| 269 | void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); | ||
| 270 | |||
| 253 | const char *exit_reason_str(unsigned int exit_reason); | 271 | const char *exit_reason_str(unsigned int exit_reason); |
| 254 | 272 | ||
| 255 | void virt_pgd_alloc(struct kvm_vm *vm); | 273 | void virt_pgd_alloc(struct kvm_vm *vm); |
| @@ -93,3 +93,69 @@ void gic_set_eoi(unsigned int intid) · 92 unmodified lines | |||
| 93 | GUEST_ASSERT(gic_common_ops); | 93 | GUEST_ASSERT(gic_common_ops); |
| 94 | gic_common_ops->gic_write_eoir(intid); | 94 | gic_common_ops->gic_write_eoir(intid); |
| 95 | } | 95 | } |
| 96 | |||
| 97 | void gic_set_dir(unsigned int intid) | ||
| 98 | { | ||
| 99 | GUEST_ASSERT(gic_common_ops); | ||
| 100 | gic_common_ops->gic_write_dir(intid); | ||
| 101 | } | ||
| 102 | |||
| 103 | void gic_set_eoi_split(bool split) | ||
| 104 | { | ||
| 105 | GUEST_ASSERT(gic_common_ops); | ||
| 106 | gic_common_ops->gic_set_eoi_split(split); | ||
| 107 | } | ||
| 108 | |||
| 109 | void gic_set_priority_mask(uint64_t pmr) | ||
| 110 | { | ||
| 111 | GUEST_ASSERT(gic_common_ops); | ||
| 112 | gic_common_ops->gic_set_priority_mask(pmr); | ||
| 113 | } | ||
| 114 | |||
| 115 | void gic_set_priority(unsigned int intid, unsigned int prio) | ||
| 116 | { | ||
| 117 | GUEST_ASSERT(gic_common_ops); | ||
| 118 | gic_common_ops->gic_set_priority(intid, prio); | ||
| 119 | } | ||
| 120 | |||
| 121 | void gic_irq_set_active(unsigned int intid) | ||
| 122 | { | ||
| 123 | GUEST_ASSERT(gic_common_ops); | ||
| 124 | gic_common_ops->gic_irq_set_active(intid); | ||
| 125 | } | ||
| 126 | |||
| 127 | void gic_irq_clear_active(unsigned int intid) | ||
| 128 | { | ||
| 129 | GUEST_ASSERT(gic_common_ops); | ||
| 130 | gic_common_ops->gic_irq_clear_active(intid); | ||
| 131 | } | ||
| 132 | |||
| 133 | bool gic_irq_get_active(unsigned int intid) | ||
| 134 | { | ||
| 135 | GUEST_ASSERT(gic_common_ops); | ||
| 136 | return gic_common_ops->gic_irq_get_active(intid); | ||
| 137 | } | ||
| 138 | |||
| 139 | void gic_irq_set_pending(unsigned int intid) | ||
| 140 | { | ||
| 141 | GUEST_ASSERT(gic_common_ops); | ||
| 142 | gic_common_ops->gic_irq_set_pending(intid); | ||
| 143 | } | ||
| 144 | |||
| 145 | void gic_irq_clear_pending(unsigned int intid) | ||
| 146 | { | ||
| 147 | GUEST_ASSERT(gic_common_ops); | ||
| 148 | gic_common_ops->gic_irq_clear_pending(intid); | ||
| 149 | } | ||
| 150 | |||
| 151 | bool gic_irq_get_pending(unsigned int intid) | ||
| 152 | { | ||
| 153 | GUEST_ASSERT(gic_common_ops); | ||
| 154 | return gic_common_ops->gic_irq_get_pending(intid); | ||
| 155 | } | ||
| 156 | |||
| 157 | void gic_irq_set_config(unsigned int intid, bool is_edge) | ||
| 158 | { | ||
| 159 | GUEST_ASSERT(gic_common_ops); | ||
| 160 | gic_common_ops->gic_irq_set_config(intid, is_edge); | ||
| 161 | } | ||
| @@ -14,6 +14,17 @@ struct gic_common_ops { · 13 unmodified lines | |||
| 14 | void (*gic_irq_disable)(unsigned int intid); | 14 | void (*gic_irq_disable)(unsigned int intid); |
| 15 | uint64_t (*gic_read_iar)(void); | 15 | uint64_t (*gic_read_iar)(void); |
| 16 | void (*gic_write_eoir)(uint32_t irq); | 16 | void (*gic_write_eoir)(uint32_t irq); |
| 17 | void (*gic_write_dir)(uint32_t irq); | ||
| 18 | void (*gic_set_eoi_split)(bool split); | ||
| 19 | void (*gic_set_priority_mask)(uint64_t mask); | ||
| 20 | void (*gic_set_priority)(uint32_t intid, uint32_t prio); | ||
| 21 | void (*gic_irq_set_active)(uint32_t intid); | ||
| 22 | void (*gic_irq_clear_active)(uint32_t intid); | ||
| 23 | bool (*gic_irq_get_active)(uint32_t intid); | ||
| 24 | void (*gic_irq_set_pending)(uint32_t intid); | ||
| 25 | void (*gic_irq_clear_pending)(uint32_t intid); | ||
| 26 | bool (*gic_irq_get_pending)(uint32_t intid); | ||
| 27 | void (*gic_irq_set_config)(uint32_t intid, bool is_edge); | ||
| 17 | }; | 28 | }; |
| 18 | 29 | ||
| 19 | extern const struct gic_common_ops gicv3_ops; | 30 | extern const struct gic_common_ops gicv3_ops; |
| @@ -19,7 +19,8 @@ struct gicv3_data { · 18 unmodified lines | |||
| 19 | unsigned int nr_spis; | 19 | unsigned int nr_spis; |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | #define sgi_base_from_redist(redist_base) (redist_base + SZ_64K) | 22 | #define sgi_base_from_redist(redist_base) (redist_base + SZ_64K) |
| 23 | #define DIST_BIT (1U << 31) | ||
| 23 | 24 | ||
| 24 | enum gicv3_intid_range { | 25 | enum gicv3_intid_range { |
| 25 | SGI_RANGE, | 26 | SGI_RANGE, |
| @@ -50,6 +51,14 @@ static void gicv3_gicr_wait_for_rwp(void *redist_base) · 24 unmodified lines | |||
| 50 | } | 51 | } |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 54 | static void gicv3_wait_for_rwp(uint32_t cpu_or_dist) | ||
| 55 | { | ||
| 56 | if (cpu_or_dist & DIST_BIT) | ||
| 57 | gicv3_gicd_wait_for_rwp(); | ||
| 58 | else | ||
| 59 | gicv3_gicr_wait_for_rwp(gicv3_data.redist_base[cpu_or_dist]); | ||
| 60 | } | ||
| 61 | |||
| 53 | static enum gicv3_intid_range get_intid_range(unsigned int intid) | 62 | static enum gicv3_intid_range get_intid_range(unsigned int intid) |
| 54 | { | 63 | { |
| 55 | switch (intid) { | 64 | switch (intid) { |
| @@ -81,39 +90,175 @@ static void gicv3_write_eoir(uint32_t irq) · 25 unmodified lines | |||
| 81 | isb(); | 90 | isb(); |
| 82 | } | 91 | } |
| 83 | 92 | ||
| 84 | static void | 93 | static void gicv3_write_dir(uint32_t irq) |
| 85 | gicv3_config_irq(unsigned int intid, unsigned int offset) | 94 | { |
| 95 | write_sysreg_s(irq, SYS_ICC_DIR_EL1); | ||
| 96 | isb(); | ||
| 97 | } | ||
| 98 | |||
| 99 | static void gicv3_set_priority_mask(uint64_t mask) | ||
| 100 | { | ||
| 101 | write_sysreg_s(mask, SYS_ICC_PMR_EL1); | ||
| 102 | } | ||
| 103 | |||
| 104 | static void gicv3_set_eoi_split(bool split) | ||
| 105 | { | ||
| 106 | uint32_t val; | ||
| 107 | |||
| 108 | /* All other fields are read-only, so no need to read CTLR first. In | ||
| 109 | * fact, the kernel does the same. | ||
| 110 | */ | ||
| 111 | val = split ? (1U << 1) : 0; | ||
| 112 | write_sysreg_s(val, SYS_ICC_CTLR_EL1); | ||
| 113 | isb(); | ||
| 114 | } | ||
| 115 | |||
| 116 | uint32_t gicv3_reg_readl(uint32_t cpu_or_dist, uint64_t offset) | ||
| 117 | { | ||
| 118 | void *base = cpu_or_dist & DIST_BIT ? gicv3_data.dist_base | ||
| 119 | : sgi_base_from_redist(gicv3_data.redist_base[cpu_or_dist]); | ||
| 120 | return readl(base + offset); | ||
| 121 | } | ||
| 122 | |||
| 123 | void gicv3_reg_writel(uint32_t cpu_or_dist, uint64_t offset, uint32_t reg_val) | ||
| 124 | { | ||
| 125 | void *base = cpu_or_dist & DIST_BIT ? gicv3_data.dist_base | ||
| 126 | : sgi_base_from_redist(gicv3_data.redist_base[cpu_or_dist]); | ||
| 127 | writel(reg_val, base + offset); | ||
| 128 | } | ||
| 129 | |||
| 130 | uint32_t gicv3_getl_fields(uint32_t cpu_or_dist, uint64_t offset, uint32_t mask) | ||
| 131 | { | ||
| 132 | return gicv3_reg_readl(cpu_or_dist, offset) & mask; | ||
| 133 | } | ||
| 134 | |||
| 135 | void gicv3_setl_fields(uint32_t cpu_or_dist, uint64_t offset, | ||
| 136 | uint32_t mask, uint32_t reg_val) | ||
| 137 | { | ||
| 138 | uint32_t tmp = gicv3_reg_readl(cpu_or_dist, offset) & ~mask; | ||
| 139 | |||
| 140 | tmp |= (reg_val & mask); | ||
| 141 | gicv3_reg_writel(cpu_or_dist, offset, tmp); | ||
| 142 | } | ||
| 143 | |||
| 144 | /* | ||
| 145 | * We use a single offset for the distributor and redistributor maps as they | ||
| 146 | * have the same value in both. The only exceptions are registers that only | ||
| 147 | * exist in one and not the other, like GICR_WAKER that doesn't exist in the | ||
| 148 | * distributor map. Such registers are conveniently marked as reserved in the | ||
| 149 | * map that doesn't implement it; like GICR_WAKER's offset of 0x0014 being | ||
| 150 | * marked as "Reserved" in the Distributor map. | ||
| 151 | */ | ||
| 152 | static void gicv3_access_reg(uint32_t intid, uint64_t offset, | ||
| 153 | uint32_t reg_bits, uint32_t bits_per_field, | ||
| 154 | bool write, uint32_t *val) | ||
| 86 | { | 155 | { |
| 87 | uint32_t cpu = guest_get_vcpuid(); | 156 | uint32_t cpu = guest_get_vcpuid(); |
| 88 | uint32_t mask = 1 << (intid % 32); | ||
| 89 | enum gicv3_intid_range intid_range = get_intid_range(intid); | 157 | enum gicv3_intid_range intid_range = get_intid_range(intid); |
| 90 | void *reg; | 158 | uint32_t fields_per_reg, index, mask, shift; |
| 91 | 159 | uint32_t cpu_or_dist; | |
| 92 | /* We care about 'cpu' only for SGIs or PPIs */ | 160 | |
| 93 | if (intid_range == SGI_RANGE || intid_range == PPI_RANGE) { | 161 | GUEST_ASSERT(bits_per_field <= reg_bits); |
| 94 | GUEST_ASSERT(cpu < gicv3_data.nr_cpus); | 162 | GUEST_ASSERT(*val < (1U << bits_per_field)); |
| 95 | 163 | /* Some registers like IROUTER are 64 bit long. Those are currently not | |
| 96 | reg = sgi_base_from_redist(gicv3_data.redist_base[cpu]) + | 164 | * supported by readl nor writel, so just asserting here until then. |
| 97 | offset; | 165 | */ |
| 98 | writel(mask, reg); | 166 | GUEST_ASSERT(reg_bits == 32); |
| 99 | gicv3_gicr_wait_for_rwp(gicv3_data.redist_base[cpu]); | 167 | |
| 100 | } else if (intid_range == SPI_RANGE) { | 168 | fields_per_reg = reg_bits / bits_per_field; |
| 101 | reg = gicv3_data.dist_base + offset + (intid / 32) * 4; | 169 | index = intid % fields_per_reg; |
| 102 | writel(mask, reg); | 170 | shift = index * bits_per_field; |
| 103 | gicv3_gicd_wait_for_rwp(); | 171 | mask = ((1U << bits_per_field) - 1) << shift; |
| 104 | } else { | 172 | |
| 105 | GUEST_ASSERT(0); | 173 | /* Set offset to the actual register holding intid's config. */ |
| 106 | } | 174 | offset += (intid / fields_per_reg) * (reg_bits / 8); |
| 175 | |||
| 176 | cpu_or_dist = (intid_range == SPI_RANGE) ? DIST_BIT : cpu; | ||
| 177 | |||
| 178 | if (write) | ||
| 179 | gicv3_setl_fields(cpu_or_dist, offset, mask, *val << shift); | ||
| 180 | *val = gicv3_getl_fields(cpu_or_dist, offset, mask) >> shift; | ||
| 181 | } | ||
| 182 | |||
| 183 | static void gicv3_write_reg(uint32_t intid, uint64_t offset, | ||
| 184 | uint32_t reg_bits, uint32_t bits_per_field, uint32_t val) | ||
| 185 | { | ||
| 186 | gicv3_access_reg(intid, offset, reg_bits, | ||
| 187 | bits_per_field, true, &val); | ||
| 188 | } | ||
| 189 | |||
| 190 | static uint32_t gicv3_read_reg(uint32_t intid, uint64_t offset, | ||
| 191 | uint32_t reg_bits, uint32_t bits_per_field) | ||
| 192 | { | ||
| 193 | uint32_t val; | ||
| 194 | |||
| 195 | gicv3_access_reg(intid, offset, reg_bits, | ||
| 196 | bits_per_field, false, &val); | ||
| 197 | return val; | ||
| 198 | } | ||
| 199 | |||
| 200 | static void gicv3_set_priority(uint32_t intid, uint32_t prio) | ||
| 201 | { | ||
| 202 | gicv3_write_reg(intid, GICD_IPRIORITYR, 32, 8, prio); | ||
| 203 | } | ||
| 204 | |||
| 205 | /* Sets the intid to be level-sensitive or edge-triggered. */ | ||
| 206 | static void gicv3_irq_set_config(uint32_t intid, bool is_edge) | ||
| 207 | { | ||
| 208 | uint32_t val; | ||
| 209 | |||
| 210 | /* N/A for private interrupts. */ | ||
| 211 | GUEST_ASSERT(get_intid_range(intid) == SPI_RANGE); | ||
| 212 | val = is_edge ? 2 : 0; | ||
| 213 | gicv3_write_reg(intid, GICD_ICFGR, 32, 2, val); | ||
| 214 | } | ||
| 215 | |||
| 216 | static void gicv3_irq_enable(uint32_t intid) | ||
| 217 | { | ||
| 218 | bool is_spi = get_intid_range(intid) == SPI_RANGE; | ||
| 219 | uint32_t cpu = guest_get_vcpuid(); | ||
| 220 | |||
| 221 | gicv3_write_reg(intid, GICD_ISENABLER, 32, 1, 1); | ||
| 222 | gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu); | ||
| 223 | } | ||
| 224 | |||
| 225 | static void gicv3_irq_disable(uint32_t intid) | ||
| 226 | { | ||
| 227 | bool is_spi = get_intid_range(intid) == SPI_RANGE; | ||
| 228 | uint32_t cpu = guest_get_vcpuid(); | ||
| 229 | |||
| 230 | gicv3_write_reg(intid, GICD_ICENABLER, 32, 1, 1); | ||
| 231 | gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu); | ||
| 232 | } | ||
| 233 | |||
| 234 | static void gicv3_irq_set_active(uint32_t intid) | ||
| 235 | { | ||
| 236 | gicv3_write_reg(intid, GICD_ISACTIVER, 32, 1, 1); | ||
| 237 | } | ||
| 238 | |||
| 239 | static void gicv3_irq_clear_active(uint32_t intid) | ||
| 240 | { | ||
| 241 | gicv3_write_reg(intid, GICD_ICACTIVER, 32, 1, 1); | ||
| 242 | } | ||
| 243 | |||
| 244 | static bool gicv3_irq_get_active(uint32_t intid) | ||
| 245 | { | ||
| 246 | return gicv3_read_reg(intid, GICD_ISACTIVER, 32, 1); | ||
| 247 | } | ||
| 248 | |||
| 249 | static void gicv3_irq_set_pending(uint32_t intid) | ||
| 250 | { | ||
| 251 | gicv3_write_reg(intid, GICD_ISPENDR, 32, 1, 1); | ||
| 107 | } | 252 | } |
| 108 | 253 | ||
| 109 | static void gicv3_irq_enable(unsigned int intid) | 254 | static void gicv3_irq_clear_pending(uint32_t intid) |
| 110 | { | 255 | { |
| 111 | gicv3_config_irq(intid, GICD_ISENABLER); | 256 | gicv3_write_reg(intid, GICD_ICPENDR, 32, 1, 1); |
| 112 | } | 257 | } |
| 113 | 258 | ||
| 114 | static void gicv3_irq_disable(unsigned int intid) | 259 | static bool gicv3_irq_get_pending(uint32_t intid) |
| 115 | { | 260 | { |
| 116 | gicv3_config_irq(intid, GICD_ICENABLER); | 261 | return gicv3_read_reg(intid, GICD_ISPENDR, 32, 1); |
| 117 | } | 262 | } |
| 118 | 263 | ||
| 119 | static void gicv3_enable_redist(void *redist_base) | 264 | static void gicv3_enable_redist(void *redist_base) |
| @@ -237,4 +382,15 @@ const struct gic_common_ops gicv3_ops = { · 117 unmodified lines | |||
| 237 | .gic_irq_disable = gicv3_irq_disable, | 382 | .gic_irq_disable = gicv3_irq_disable, |
| 238 | .gic_read_iar = gicv3_read_iar, | 383 | .gic_read_iar = gicv3_read_iar, |
| 239 | .gic_write_eoir = gicv3_write_eoir, | 384 | .gic_write_eoir = gicv3_write_eoir, |
| 385 | .gic_write_dir = gicv3_write_dir, | ||
| 386 | .gic_set_priority_mask = gicv3_set_priority_mask, | ||
| 387 | .gic_set_eoi_split = gicv3_set_eoi_split, | ||
| 388 | .gic_set_priority = gicv3_set_priority, | ||
| 389 | .gic_irq_set_active = gicv3_irq_set_active, | ||
| 390 | .gic_irq_clear_active = gicv3_irq_clear_active, | ||
| 391 | .gic_irq_get_active = gicv3_irq_get_active, | ||
| 392 | .gic_irq_set_pending = gicv3_irq_set_pending, | ||
| 393 | .gic_irq_clear_pending = gicv3_irq_clear_pending, | ||
| 394 | .gic_irq_get_pending = gicv3_irq_get_pending, | ||
| 395 | .gic_irq_set_config = gicv3_irq_set_config, | ||
| 240 | }; | 396 | }; |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | |||
| 8 | #include <linux/compiler.h> | 8 | #include <linux/compiler.h> |
| 9 | #include <assert.h> | 9 | #include <assert.h> |
| 10 | 10 | ||
| 11 | #include "guest_modes.h" | ||
| 11 | #include "kvm_util.h" | 12 | #include "kvm_util.h" |
| 12 | #include "../kvm_util_internal.h" | 13 | #include "../kvm_util_internal.h" |
| 13 | #include "processor.h" | 14 | #include "processor.h" |
| @@ -237,6 +238,7 @@ void aarch64_vcpu_setup(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_vcpu_init · 223 unmodified lines | |||
| 237 | get_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), &sctlr_el1); | 238 | get_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), &sctlr_el1); |
| 238 | get_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_TCR_EL1), &tcr_el1); | 239 | get_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_TCR_EL1), &tcr_el1); |
| 239 | 240 | ||
| 241 | /* Configure base granule size */ | ||
| 240 | switch (vm->mode) { | 242 | switch (vm->mode) { |
| 241 | case VM_MODE_P52V48_4K: | 243 | case VM_MODE_P52V48_4K: |
| 242 | TEST_FAIL("AArch64 does not support 4K sized pages " | 244 | TEST_FAIL("AArch64 does not support 4K sized pages " |
| @@ -245,25 +247,47 @@ void aarch64_vcpu_setup(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_vcpu_init · 2 unmodified lines | |||
| 245 | TEST_FAIL("AArch64 does not support 4K sized pages " | 247 | TEST_FAIL("AArch64 does not support 4K sized pages " |
| 246 | "with ANY-bit physical address ranges"); | 248 | "with ANY-bit physical address ranges"); |
| 247 | case VM_MODE_P52V48_64K: | 249 | case VM_MODE_P52V48_64K: |
| 250 | case VM_MODE_P48V48_64K: | ||
| 251 | case VM_MODE_P40V48_64K: | ||
| 252 | case VM_MODE_P36V48_64K: | ||
| 248 | tcr_el1 |= 1ul << 14; /* TG0 = 64KB */ | 253 | tcr_el1 |= 1ul << 14; /* TG0 = 64KB */ |
| 249 | tcr_el1 |= 6ul << 32; /* IPS = 52 bits */ | 254 | break; |
| 255 | case VM_MODE_P48V48_16K: | ||
| 256 | case VM_MODE_P40V48_16K: | ||
| 257 | case VM_MODE_P36V48_16K: | ||
| 258 | case VM_MODE_P36V47_16K: | ||
| 259 | tcr_el1 |= 2ul << 14; /* TG0 = 16KB */ | ||
| 250 | break; | 260 | break; |
| 251 | case VM_MODE_P48V48_4K: | 261 | case VM_MODE_P48V48_4K: |
| 262 | case VM_MODE_P40V48_4K: | ||
| 263 | case VM_MODE_P36V48_4K: | ||
| 252 | tcr_el1 |= 0ul << 14; /* TG0 = 4KB */ | 264 | tcr_el1 |= 0ul << 14; /* TG0 = 4KB */ |
| 253 | tcr_el1 |= 5ul << 32; /* IPS = 48 bits */ | ||
| 254 | break; | 265 | break; |
| 266 | default: | ||
| 267 | TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); | ||
| 268 | } | ||
| 269 | |||
| 270 | /* Configure output size */ | ||
| 271 | switch (vm->mode) { | ||
| 272 | case VM_MODE_P52V48_64K: | ||
| 273 | tcr_el1 |= 6ul << 32; /* IPS = 52 bits */ | ||
| 274 | break; | ||
| 275 | case VM_MODE_P48V48_4K: | ||
| 276 | case VM_MODE_P48V48_16K: | ||
| 255 | case VM_MODE_P48V48_64K: | 277 | case VM_MODE_P48V48_64K: |
| 256 | tcr_el1 |= 1ul << 14; /* TG0 = 64KB */ | ||
| 257 | tcr_el1 |= 5ul << 32; /* IPS = 48 bits */ | 278 | tcr_el1 |= 5ul << 32; /* IPS = 48 bits */ |
| 258 | break; | 279 | break; |
| 259 | case VM_MODE_P40V48_4K: | 280 | case VM_MODE_P40V48_4K: |
| 260 | tcr_el1 |= 0ul << 14; /* TG0 = 4KB */ | 281 | case VM_MODE_P40V48_16K: |
| 261 | tcr_el1 |= 2ul << 32; /* IPS = 40 bits */ | ||
| 262 | break; | ||
| 263 | case VM_MODE_P40V48_64K: | 282 | case VM_MODE_P40V48_64K: |
| 264 | tcr_el1 |= 1ul << 14; /* TG0 = 64KB */ | ||
| 265 | tcr_el1 |= 2ul << 32; /* IPS = 40 bits */ | 283 | tcr_el1 |= 2ul << 32; /* IPS = 40 bits */ |
| 266 | break; | 284 | break; |
| 285 | case VM_MODE_P36V48_4K: | ||
| 286 | case VM_MODE_P36V48_16K: | ||
| 287 | case VM_MODE_P36V48_64K: | ||
| 288 | case VM_MODE_P36V47_16K: | ||
| 289 | tcr_el1 |= 1ul << 32; /* IPS = 36 bits */ | ||
| 290 | break; | ||
| 267 | default: | 291 | default: |
| 268 | TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); | 292 | TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); |
| 269 | } | 293 | } |
| @@ -432,3 +456,47 @@ uint32_t guest_get_vcpuid(void) · 162 unmodified lines | |||
| 432 | { | 456 | { |
| 433 | return read_sysreg(tpidr_el1); | 457 | return read_sysreg(tpidr_el1); |
| 434 | } | 458 | } |
| 459 | |||
| 460 | void aarch64_get_supported_page_sizes(uint32_t ipa, | ||
| 461 | bool *ps4k, bool *ps16k, bool *ps64k) | ||
| 462 | { | ||
| 463 | struct kvm_vcpu_init preferred_init; | ||
| 464 | int kvm_fd, vm_fd, vcpu_fd, err; | ||
| 465 | uint64_t val; | ||
| 466 | struct kvm_one_reg reg = { | ||
| 467 | .id = KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR0_EL1), | ||
| 468 | .addr = (uint64_t)&val, | ||
| 469 | }; | ||
| 470 | |||
| 471 | kvm_fd = open_kvm_dev_path_or_exit(); | ||
| 472 | vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, ipa); | ||
| 473 | TEST_ASSERT(vm_fd >= 0, "Can't create VM"); | ||
| 474 | |||
| 475 | vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0); | ||
| 476 | TEST_ASSERT(vcpu_fd >= 0, "Can't create vcpu"); | ||
| 477 | |||
| 478 | err = ioctl(vm_fd, KVM_ARM_PREFERRED_TARGET, &preferred_init); | ||
| 479 | TEST_ASSERT(err == 0, "Can't get target"); | ||
| 480 | err = ioctl(vcpu_fd, KVM_ARM_VCPU_INIT, &preferred_init); | ||
| 481 | TEST_ASSERT(err == 0, "Can't get init vcpu"); | ||
| 482 | |||
| 483 | err = ioctl(vcpu_fd, KVM_GET_ONE_REG, ®); | ||
| 484 | TEST_ASSERT(err == 0, "Can't get MMFR0"); | ||
| 485 | |||
| 486 | *ps4k = ((val >> 28) & 0xf) != 0xf; | ||
| 487 | *ps64k = ((val >> 24) & 0xf) == 0; | ||
| 488 | *ps16k = ((val >> 20) & 0xf) != 0; | ||
| 489 | |||
| 490 | close(vcpu_fd); | ||
| 491 | close(vm_fd); | ||
| 492 | close(kvm_fd); | ||
| 493 | } | ||
| 494 | |||
| 495 | /* | ||
| 496 | * arm64 doesn't have a true default mode, so start by computing the | ||
| 497 | * available IPA space and page sizes early. | ||
| 498 | */ | ||
| 499 | void __attribute__((constructor)) init_guest_modes(void) | ||
| 500 | { | ||
| 501 | guest_modes_append_default(); | ||
| 502 | } | ||
| @@ -5,11 +5,14 @@ · 4 unmodified lines | |||
| 5 | 5 | ||
| 6 | #include <linux/kvm.h> | 6 | #include <linux/kvm.h> |
| 7 | #include <linux/sizes.h> | 7 | #include <linux/sizes.h> |
| 8 | #include <asm/kvm_para.h> | ||
| 8 | #include <asm/kvm.h> | 9 | #include <asm/kvm.h> |
| 9 | 10 | ||
| 10 | #include "kvm_util.h" | 11 | #include "kvm_util.h" |
| 11 | #include "../kvm_util_internal.h" | 12 | #include "../kvm_util_internal.h" |
| 12 | #include "vgic.h" | 13 | #include "vgic.h" |
| 14 | #include "gic.h" | ||
| 15 | #include "gic_v3.h" | ||
| 13 | 16 | ||
| 14 | /* | 17 | /* |
| 15 | * vGIC-v3 default host setup | 18 | * vGIC-v3 default host setup |
| @@ -28,7 +31,7 @@ · 12 unmodified lines | |||
| 28 | * redistributor regions of the guest. Since it depends on the number of | 31 | * redistributor regions of the guest. Since it depends on the number of |
| 29 | * vCPUs for the VM, it must be called after all the vCPUs have been created. | 32 | * vCPUs for the VM, it must be called after all the vCPUs have been created. |
| 30 | */ | 33 | */ |
| 31 | int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, | 34 | int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs, |
| 32 | uint64_t gicd_base_gpa, uint64_t gicr_base_gpa) | 35 | uint64_t gicd_base_gpa, uint64_t gicr_base_gpa) |
| 33 | { | 36 | { |
| 34 | int gic_fd; | 37 | int gic_fd; |
| @@ -50,6 +53,13 @@ int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, · 15 unmodified lines | |||
| 50 | 53 | ||
| 51 | /* Distributor setup */ | 54 | /* Distributor setup */ |
| 52 | gic_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3, false); | 55 | gic_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3, false); |
| 56 | |||
| 57 | kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, | ||
| 58 | 0, &nr_irqs, true); | ||
| 59 | |||
| 60 | kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, | ||
| 61 | KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); | ||
| 62 | |||
| 53 | kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, | 63 | kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, |
| 54 | KVM_VGIC_V3_ADDR_TYPE_DIST, &gicd_base_gpa, true); | 64 | KVM_VGIC_V3_ADDR_TYPE_DIST, &gicd_base_gpa, true); |
| 55 | nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE); | 65 | nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE); |
| @@ -68,3 +78,94 @@ int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, · 12 unmodified lines | |||
| 68 | 78 | ||
| 69 | return gic_fd; | 79 | return gic_fd; |
| 70 | } | 80 | } |
| 81 | |||
| 82 | /* should only work for level sensitive interrupts */ | ||
| 83 | int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level) | ||
| 84 | { | ||
| 85 | uint64_t attr = 32 * (intid / 32); | ||
| 86 | uint64_t index = intid % 32; | ||
| 87 | uint64_t val; | ||
| 88 | int ret; | ||
| 89 | |||
| 90 | ret = _kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, | ||
| 91 | attr, &val, false); | ||
| 92 | if (ret != 0) | ||
| 93 | return ret; | ||
| 94 | |||
| 95 | val |= 1U << index; | ||
| 96 | ret = _kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, | ||
| 97 | attr, &val, true); | ||
| 98 | return ret; | ||
| 99 | } | ||
| 100 | |||
| 101 | void kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level) | ||
| 102 | { | ||
| 103 | int ret = _kvm_irq_set_level_info(gic_fd, intid, level); | ||
| 104 | |||
| 105 | TEST_ASSERT(ret == 0, "KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO failed, " | ||
| 106 | "rc: %i errno: %i", ret, errno); | ||
| 107 | } | ||
| 108 | |||
| 109 | int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level) | ||
| 110 | { | ||
| 111 | uint32_t irq = intid & KVM_ARM_IRQ_NUM_MASK; | ||
| 112 | |||
| 113 | TEST_ASSERT(!INTID_IS_SGI(intid), "KVM_IRQ_LINE's interface itself " | ||
| 114 | "doesn't allow injecting SGIs. There's no mask for it."); | ||
| 115 | |||
| 116 | if (INTID_IS_PPI(intid)) | ||
| 117 | irq |= KVM_ARM_IRQ_TYPE_PPI << KVM_ARM_IRQ_TYPE_SHIFT; | ||
| 118 | else | ||
| 119 | irq |= KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT; | ||
| 120 | |||
| 121 | return _kvm_irq_line(vm, irq, level); | ||
| 122 | } | ||
| 123 | |||
| 124 | void kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level) | ||
| 125 | { | ||
| 126 | int ret = _kvm_arm_irq_line(vm, intid, level); | ||
| 127 | |||
| 128 | TEST_ASSERT(ret == 0, "KVM_IRQ_LINE failed, rc: %i errno: %i", | ||
| 129 | ret, errno); | ||
| 130 | } | ||
| 131 | |||
| 132 | static void vgic_poke_irq(int gic_fd, uint32_t intid, | ||
| 133 | uint32_t vcpu, uint64_t reg_off) | ||
| 134 | { | ||
| 135 | uint64_t reg = intid / 32; | ||
| 136 | uint64_t index = intid % 32; | ||
| 137 | uint64_t attr = reg_off + reg * 4; | ||
| 138 | uint64_t val; | ||
| 139 | bool intid_is_private = INTID_IS_SGI(intid) || INTID_IS_PPI(intid); | ||
| 140 | |||
| 141 | /* Check that the addr part of the attr is within 32 bits. */ | ||
| 142 | assert(attr <= KVM_DEV_ARM_VGIC_OFFSET_MASK); | ||
| 143 | |||
| 144 | uint32_t group = intid_is_private ? KVM_DEV_ARM_VGIC_GRP_REDIST_REGS | ||
| 145 | : KVM_DEV_ARM_VGIC_GRP_DIST_REGS; | ||
| 146 | |||
| 147 | if (intid_is_private) { | ||
| 148 | /* TODO: only vcpu 0 implemented for now. */ | ||
| 149 | assert(vcpu == 0); | ||
| 150 | attr += SZ_64K; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* All calls will succeed, even with invalid intid's, as long as the | ||
| 154 | * addr part of the attr is within 32 bits (checked above). An invalid | ||
| 155 | * intid will just make the read/writes point to above the intended | ||
| 156 | * register space (i.e., ICPENDR after ISPENDR). | ||
| 157 | */ | ||
| 158 | kvm_device_access(gic_fd, group, attr, &val, false); | ||
| 159 | val |= 1ULL << index; | ||
| 160 | kvm_device_access(gic_fd, group, attr, &val, true); | ||
| 161 | } | ||
| 162 | |||
| 163 | void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, uint32_t vcpu) | ||
| 164 | { | ||
| 165 | vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISPENDR); | ||
| 166 | } | ||
| 167 | |||
| 168 | void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, uint32_t vcpu) | ||
| 169 | { | ||
| 170 | vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISACTIVER); | ||
| 171 | } | ||
| @@ -4,22 +4,59 @@ · 3 unmodified lines | |||
| 4 | */ | 4 | */ |
| 5 | #include "guest_modes.h" | 5 | #include "guest_modes.h" |
| 6 | 6 | ||
| 7 | #ifdef __aarch64__ | ||
| 8 | #include "processor.h" | ||
| 9 | enum vm_guest_mode vm_mode_default; | ||
| 10 | #endif | ||
| 11 | |||
| 7 | struct guest_mode guest_modes[NUM_VM_MODES]; | 12 | struct guest_mode guest_modes[NUM_VM_MODES]; |
| 8 | 13 | ||
| 9 | void guest_modes_append_default(void) | 14 | void guest_modes_append_default(void) |
| 10 | { | 15 | { |
| 16 | #ifndef __aarch64__ | ||
| 11 | guest_mode_append(VM_MODE_DEFAULT, true, true); | 17 | guest_mode_append(VM_MODE_DEFAULT, true, true); |
| 12 | 18 | #else | |
| 13 | #ifdef __aarch64__ | ||
| 14 | guest_mode_append(VM_MODE_P40V48_64K, true, true); | ||
| 15 | { | 19 | { |
| 16 | unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE); | 20 | unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE); |
| 21 | bool ps4k, ps16k, ps64k; | ||
| 22 | int i; | ||
| 23 | |||
| 24 | aarch64_get_supported_page_sizes(limit, &ps4k, &ps16k, &ps64k); | ||
| 25 | |||
| 26 | vm_mode_default = NUM_VM_MODES; | ||
| 27 | |||
| 17 | if (limit >= 52) | 28 | if (limit >= 52) |
| 18 | guest_mode_append(VM_MODE_P52V48_64K, true, true); | 29 | guest_mode_append(VM_MODE_P52V48_64K, ps64k, ps64k); |
| 19 | if (limit >= 48) { | 30 | if (limit >= 48) { |
| 20 | guest_mode_append(VM_MODE_P48V48_4K, true, true); | 31 | guest_mode_append(VM_MODE_P48V48_4K, ps4k, ps4k); |
| 21 | guest_mode_append(VM_MODE_P48V48_64K, true, true); | 32 | guest_mode_append(VM_MODE_P48V48_16K, ps16k, ps16k); |
| 33 | guest_mode_append(VM_MODE_P48V48_64K, ps64k, ps64k); | ||
| 34 | } | ||
| 35 | if (limit >= 40) { | ||
| 36 | guest_mode_append(VM_MODE_P40V48_4K, ps4k, ps4k); | ||
| 37 | guest_mode_append(VM_MODE_P40V48_16K, ps16k, ps16k); | ||
| 38 | guest_mode_append(VM_MODE_P40V48_64K, ps64k, ps64k); | ||
| 39 | if (ps4k) | ||
| 40 | vm_mode_default = VM_MODE_P40V48_4K; | ||
| 41 | } | ||
| 42 | if (limit >= 36) { | ||
| 43 | guest_mode_append(VM_MODE_P36V48_4K, ps4k, ps4k); | ||
| 44 | guest_mode_append(VM_MODE_P36V48_16K, ps16k, ps16k); | ||
| 45 | guest_mode_append(VM_MODE_P36V48_64K, ps64k, ps64k); | ||
| 46 | guest_mode_append(VM_MODE_P36V47_16K, ps16k, ps16k); | ||
| 22 | } | 47 | } |
| 48 | |||
| 49 | /* | ||
| 50 | * Pick the first supported IPA size if the default | ||
| 51 | * isn't available. | ||
| 52 | */ | ||
| 53 | for (i = 0; vm_mode_default == NUM_VM_MODES && i < NUM_VM_MODES; i++) { | ||
| 54 | if (guest_modes[i].supported && guest_modes[i].enabled) | ||
| 55 | vm_mode_default = i; | ||
| 56 | } | ||
| 57 | |||
| 58 | TEST_ASSERT(vm_mode_default != NUM_VM_MODES, | ||
| 59 | "No supported mode!"); | ||
| 23 | } | 60 | } |
| 24 | #endif | 61 | #endif |
| 25 | #ifdef __s390x__ | 62 | #ifdef __s390x__ |
| @@ -166,12 +166,18 @@ const char *vm_guest_mode_string(uint32_t i) · 165 unmodified lines | |||
| 166 | [VM_MODE_P52V48_4K] = "PA-bits:52, VA-bits:48, 4K pages", | 166 | [VM_MODE_P52V48_4K] = "PA-bits:52, VA-bits:48, 4K pages", |
| 167 | [VM_MODE_P52V48_64K] = "PA-bits:52, VA-bits:48, 64K pages", | 167 | [VM_MODE_P52V48_64K] = "PA-bits:52, VA-bits:48, 64K pages", |
| 168 | [VM_MODE_P48V48_4K] = "PA-bits:48, VA-bits:48, 4K pages", | 168 | [VM_MODE_P48V48_4K] = "PA-bits:48, VA-bits:48, 4K pages", |
| 169 | [VM_MODE_P48V48_16K] = "PA-bits:48, VA-bits:48, 16K pages", | ||
| 169 | [VM_MODE_P48V48_64K] = "PA-bits:48, VA-bits:48, 64K pages", | 170 | [VM_MODE_P48V48_64K] = "PA-bits:48, VA-bits:48, 64K pages", |
| 170 | [VM_MODE_P40V48_4K] = "PA-bits:40, VA-bits:48, 4K pages", | 171 | [VM_MODE_P40V48_4K] = "PA-bits:40, VA-bits:48, 4K pages", |
| 172 | [VM_MODE_P40V48_16K] = "PA-bits:40, VA-bits:48, 16K pages", | ||
| 171 | [VM_MODE_P40V48_64K] = "PA-bits:40, VA-bits:48, 64K pages", | 173 | [VM_MODE_P40V48_64K] = "PA-bits:40, VA-bits:48, 64K pages", |
| 172 | [VM_MODE_PXXV48_4K] = "PA-bits:ANY, VA-bits:48, 4K pages", | 174 | [VM_MODE_PXXV48_4K] = "PA-bits:ANY, VA-bits:48, 4K pages", |
| 173 | [VM_MODE_P47V64_4K] = "PA-bits:47, VA-bits:64, 4K pages", | 175 | [VM_MODE_P47V64_4K] = "PA-bits:47, VA-bits:64, 4K pages", |
| 174 | [VM_MODE_P44V64_4K] = "PA-bits:44, VA-bits:64, 4K pages", | 176 | [VM_MODE_P44V64_4K] = "PA-bits:44, VA-bits:64, 4K pages", |
| 177 | [VM_MODE_P36V48_4K] = "PA-bits:36, VA-bits:48, 4K pages", | ||
| 178 | [VM_MODE_P36V48_16K] = "PA-bits:36, VA-bits:48, 16K pages", | ||
| 179 | [VM_MODE_P36V48_64K] = "PA-bits:36, VA-bits:48, 64K pages", | ||
| 180 | [VM_MODE_P36V47_16K] = "PA-bits:36, VA-bits:47, 16K pages", | ||
| 175 | }; | 181 | }; |
| 176 | _Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES, | 182 | _Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES, |
| 177 | "Missing new mode strings?"); | 183 | "Missing new mode strings?"); |
| @@ -185,12 +191,18 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = { · 7 unmodified lines | |||
| 185 | [VM_MODE_P52V48_4K] = { 52, 48, 0x1000, 12 }, | 191 | [VM_MODE_P52V48_4K] = { 52, 48, 0x1000, 12 }, |
| 186 | [VM_MODE_P52V48_64K] = { 52, 48, 0x10000, 16 }, | 192 | [VM_MODE_P52V48_64K] = { 52, 48, 0x10000, 16 }, |
| 187 | [VM_MODE_P48V48_4K] = { 48, 48, 0x1000, 12 }, | 193 | [VM_MODE_P48V48_4K] = { 48, 48, 0x1000, 12 }, |
| 194 | [VM_MODE_P48V48_16K] = { 48, 48, 0x4000, 14 }, | ||
| 188 | [VM_MODE_P48V48_64K] = { 48, 48, 0x10000, 16 }, | 195 | [VM_MODE_P48V48_64K] = { 48, 48, 0x10000, 16 }, |
| 189 | [VM_MODE_P40V48_4K] = { 40, 48, 0x1000, 12 }, | 196 | [VM_MODE_P40V48_4K] = { 40, 48, 0x1000, 12 }, |
| 197 | [VM_MODE_P40V48_16K] = { 40, 48, 0x4000, 14 }, | ||
| 190 | [VM_MODE_P40V48_64K] = { 40, 48, 0x10000, 16 }, | 198 | [VM_MODE_P40V48_64K] = { 40, 48, 0x10000, 16 }, |
| 191 | [VM_MODE_PXXV48_4K] = { 0, 0, 0x1000, 12 }, | 199 | [VM_MODE_PXXV48_4K] = { 0, 0, 0x1000, 12 }, |
| 192 | [VM_MODE_P47V64_4K] = { 47, 64, 0x1000, 12 }, | 200 | [VM_MODE_P47V64_4K] = { 47, 64, 0x1000, 12 }, |
| 193 | [VM_MODE_P44V64_4K] = { 44, 64, 0x1000, 12 }, | 201 | [VM_MODE_P44V64_4K] = { 44, 64, 0x1000, 12 }, |
| 202 | [VM_MODE_P36V48_4K] = { 36, 48, 0x1000, 12 }, | ||
| 203 | [VM_MODE_P36V48_16K] = { 36, 48, 0x4000, 14 }, | ||
| 204 | [VM_MODE_P36V48_64K] = { 36, 48, 0x10000, 16 }, | ||
| 205 | [VM_MODE_P36V47_16K] = { 36, 47, 0x4000, 14 }, | ||
| 194 | }; | 206 | }; |
| 195 | _Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES, | 207 | _Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES, |
| 196 | "Missing new mode params?"); | 208 | "Missing new mode params?"); |
| @@ -252,9 +264,19 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm) · 55 unmodified lines | |||
| 252 | vm->pgtable_levels = 3; | 264 | vm->pgtable_levels = 3; |
| 253 | break; | 265 | break; |
| 254 | case VM_MODE_P40V48_4K: | 266 | case VM_MODE_P40V48_4K: |
| 267 | case VM_MODE_P36V48_4K: | ||
| 255 | vm->pgtable_levels = 4; | 268 | vm->pgtable_levels = 4; |
| 256 | break; | 269 | break; |
| 257 | case VM_MODE_P40V48_64K: | 270 | case VM_MODE_P40V48_64K: |
| 271 | case VM_MODE_P36V48_64K: | ||
| 272 | vm->pgtable_levels = 3; | ||
| 273 | break; | ||
| 274 | case VM_MODE_P48V48_16K: | ||
| 275 | case VM_MODE_P40V48_16K: | ||
| 276 | case VM_MODE_P36V48_16K: | ||
| 277 | vm->pgtable_levels = 4; | ||
| 278 | break; | ||
| 279 | case VM_MODE_P36V47_16K: | ||
| 258 | vm->pgtable_levels = 3; | 280 | vm->pgtable_levels = 3; |
| 259 | break; | 281 | break; |
| 260 | case VM_MODE_PXXV48_4K: | 282 | case VM_MODE_PXXV48_4K: |
| @@ -2086,6 +2108,78 @@ int vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, · 1825 unmodified lines | |||
| 2086 | return ret; | 2108 | return ret; |
| 2087 | } | 2109 | } |
| 2088 | 2110 | ||
| 2111 | /* | ||
| 2112 | * IRQ related functions. | ||
| 2113 | */ | ||
| 2114 | |||
| 2115 | int _kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) | ||
| 2116 | { | ||
| 2117 | struct kvm_irq_level irq_level = { | ||
| 2118 | .irq = irq, | ||
| 2119 | .level = level, | ||
| 2120 | }; | ||
| 2121 | |||
| 2122 | return _vm_ioctl(vm, KVM_IRQ_LINE, &irq_level); | ||
| 2123 | } | ||
| 2124 | |||
| 2125 | void kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) | ||
| 2126 | { | ||
| 2127 | int ret = _kvm_irq_line(vm, irq, level); | ||
| 2128 | |||
| 2129 | TEST_ASSERT(ret >= 0, "KVM_IRQ_LINE failed, rc: %i errno: %i", ret, errno); | ||
| 2130 | } | ||
| 2131 | |||
| 2132 | struct kvm_irq_routing *kvm_gsi_routing_create(void) | ||
| 2133 | { | ||
| 2134 | struct kvm_irq_routing *routing; | ||
| 2135 | size_t size; | ||
| 2136 | |||
| 2137 | size = sizeof(struct kvm_irq_routing); | ||
| 2138 | /* Allocate space for the max number of entries: this wastes 196 KBs. */ | ||
| 2139 | size += KVM_MAX_IRQ_ROUTES * sizeof(struct kvm_irq_routing_entry); | ||
| 2140 | routing = calloc(1, size); | ||
| 2141 | assert(routing); | ||
| 2142 | |||
| 2143 | return routing; | ||
| 2144 | } | ||
| 2145 | |||
| 2146 | void kvm_gsi_routing_irqchip_add(struct kvm_irq_routing *routing, | ||
| 2147 | uint32_t gsi, uint32_t pin) | ||
| 2148 | { | ||
| 2149 | int i; | ||
| 2150 | |||
| 2151 | assert(routing); | ||
| 2152 | assert(routing->nr < KVM_MAX_IRQ_ROUTES); | ||
| 2153 | |||
| 2154 | i = routing->nr; | ||
| 2155 | routing->entries[i].gsi = gsi; | ||
| 2156 | routing->entries[i].type = KVM_IRQ_ROUTING_IRQCHIP; | ||
| 2157 | routing->entries[i].flags = 0; | ||
| 2158 | routing->entries[i].u.irqchip.irqchip = 0; | ||
| 2159 | routing->entries[i].u.irqchip.pin = pin; | ||
| 2160 | routing->nr++; | ||
| 2161 | } | ||
| 2162 | |||
| 2163 | int _kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing) | ||
| 2164 | { | ||
| 2165 | int ret; | ||
| 2166 | |||
| 2167 | assert(routing); | ||
| 2168 | ret = ioctl(vm_get_fd(vm), KVM_SET_GSI_ROUTING, routing); | ||
| 2169 | free(routing); | ||
| 2170 | |||
| 2171 | return ret; | ||
| 2172 | } | ||
| 2173 | |||
| 2174 | void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing) | ||
| 2175 | { | ||
| 2176 | int ret; | ||
| 2177 | |||
| 2178 | ret = _kvm_gsi_routing_write(vm, routing); | ||
| 2179 | TEST_ASSERT(ret == 0, "KVM_SET_GSI_ROUTING failed, rc: %i errno: %i", | ||
| 2180 | ret, errno); | ||
| 2181 | } | ||
| 2182 | |||
| 2089 | /* | 2183 | /* |
| 2090 | * VM Dump | 2184 | * VM Dump |
| 2091 | * | 2185 | * |