1 /* 2 * Copyright (C) 2012 - ARM Ltd 3 * Author: Marc Zyngier <marc.zyngier@arm.com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include <linux/kvm_host.h> 19 #include <linux/wait.h> 20 21 #include <asm/kvm_emulate.h> 22 #include <asm/kvm_psci.h> 23 24 /* 25 * This is an implementation of the Power State Coordination Interface 26 * as described in ARM document number ARM DEN 0022A. 27 */ 28 kvm_psci_vcpu_off(struct kvm_vcpu * vcpu)29static void kvm_psci_vcpu_off(struct kvm_vcpu *vcpu) 30 { 31 vcpu->arch.pause = true; 32 } 33 kvm_psci_vcpu_on(struct kvm_vcpu * source_vcpu)34static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu) 35 { 36 struct kvm *kvm = source_vcpu->kvm; 37 struct kvm_vcpu *vcpu; 38 wait_queue_head_t *wq; 39 unsigned long cpu_id; 40 phys_addr_t target_pc; 41 42 cpu_id = *vcpu_reg(source_vcpu, 1); 43 if (vcpu_mode_is_32bit(source_vcpu)) 44 cpu_id &= ~((u32) 0); 45 46 if (cpu_id >= atomic_read(&kvm->online_vcpus)) 47 return KVM_PSCI_RET_INVAL; 48 49 target_pc = *vcpu_reg(source_vcpu, 2); 50 51 vcpu = kvm_get_vcpu(kvm, cpu_id); 52 53 wq = kvm_arch_vcpu_wq(vcpu); 54 if (!waitqueue_active(wq)) 55 return KVM_PSCI_RET_INVAL; 56 57 kvm_reset_vcpu(vcpu); 58 59 /* Gracefully handle Thumb2 entry point */ 60 if (vcpu_mode_is_32bit(vcpu) && (target_pc & 1)) { 61 target_pc &= ~((phys_addr_t) 1); 62 vcpu_set_thumb(vcpu); 63 } 64 65 *vcpu_pc(vcpu) = target_pc; 66 vcpu->arch.pause = false; 67 smp_mb(); /* Make sure the above is visible */ 68 69 wake_up_interruptible(wq); 70 71 return KVM_PSCI_RET_SUCCESS; 72 } 73 74 /** 75 * kvm_psci_call - handle PSCI call if r0 value is in range 76 * @vcpu: Pointer to the VCPU struct 77 * 78 * Handle PSCI calls from guests through traps from HVC or SMC instructions. 79 * The calling convention is similar to SMC calls to the secure world where 80 * the function number is placed in r0 and this function returns true if the 81 * function number specified in r0 is withing the PSCI range, and false 82 * otherwise. 83 */ kvm_psci_call(struct kvm_vcpu * vcpu)84bool kvm_psci_call(struct kvm_vcpu *vcpu) 85 { 86 unsigned long psci_fn = *vcpu_reg(vcpu, 0) & ~((u32) 0); 87 unsigned long val; 88 89 switch (psci_fn) { 90 case KVM_PSCI_FN_CPU_OFF: 91 kvm_psci_vcpu_off(vcpu); 92 val = KVM_PSCI_RET_SUCCESS; 93 break; 94 case KVM_PSCI_FN_CPU_ON: 95 val = kvm_psci_vcpu_on(vcpu); 96 break; 97 case KVM_PSCI_FN_CPU_SUSPEND: 98 case KVM_PSCI_FN_MIGRATE: 99 val = KVM_PSCI_RET_NI; 100 break; 101 102 default: 103 return false; 104 } 105 106 *vcpu_reg(vcpu, 0) = val; 107 return true; 108 } 109