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/cputype.h> 22#include <asm/kvm_emulate.h> 23#include <asm/kvm_psci.h> 24 25/* 26 * This is an implementation of the Power State Coordination Interface 27 * as described in ARM document number ARM DEN 0022A. 28 */ 29 30static void kvm_psci_vcpu_off(struct kvm_vcpu *vcpu) 31{ 32 vcpu->arch.pause = true; 33} 34 35static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu) 36{ 37 struct kvm *kvm = source_vcpu->kvm; 38 struct kvm_vcpu *vcpu = NULL, *tmp; 39 wait_queue_head_t *wq; 40 unsigned long cpu_id; 41 unsigned long mpidr; 42 phys_addr_t target_pc; 43 int i; 44 45 cpu_id = *vcpu_reg(source_vcpu, 1); 46 if (vcpu_mode_is_32bit(source_vcpu)) 47 cpu_id &= ~((u32) 0); 48 49 kvm_for_each_vcpu(i, tmp, kvm) { 50 mpidr = kvm_vcpu_get_mpidr(tmp); 51 if ((mpidr & MPIDR_HWID_BITMASK) == (cpu_id & MPIDR_HWID_BITMASK)) { 52 vcpu = tmp; 53 break; 54 } 55 } 56 57 /* 58 * Make sure the caller requested a valid CPU and that the CPU is 59 * turned off. 60 */ 61 if (!vcpu || !vcpu->arch.pause) 62 return KVM_PSCI_RET_INVAL; 63 64 target_pc = *vcpu_reg(source_vcpu, 2); 65 66 kvm_reset_vcpu(vcpu); 67 68 /* Gracefully handle Thumb2 entry point */ 69 if (vcpu_mode_is_32bit(vcpu) && (target_pc & 1)) { 70 target_pc &= ~((phys_addr_t) 1); 71 vcpu_set_thumb(vcpu); 72 } 73 74 /* Propagate caller endianness */ 75 if (kvm_vcpu_is_be(source_vcpu)) 76 kvm_vcpu_set_be(vcpu); 77 78 *vcpu_pc(vcpu) = target_pc; 79 vcpu->arch.pause = false; 80 smp_mb(); /* Make sure the above is visible */ 81 82 wq = kvm_arch_vcpu_wq(vcpu); 83 wake_up_interruptible(wq); 84 85 return KVM_PSCI_RET_SUCCESS; 86} 87 88/** 89 * kvm_psci_call - handle PSCI call if r0 value is in range 90 * @vcpu: Pointer to the VCPU struct 91 * 92 * Handle PSCI calls from guests through traps from HVC instructions. 93 * The calling convention is similar to SMC calls to the secure world where 94 * the function number is placed in r0 and this function returns true if the 95 * function number specified in r0 is withing the PSCI range, and false 96 * otherwise. 97 */ 98bool kvm_psci_call(struct kvm_vcpu *vcpu) 99{ 100 unsigned long psci_fn = *vcpu_reg(vcpu, 0) & ~((u32) 0); 101 unsigned long val; 102 103 switch (psci_fn) { 104 case KVM_PSCI_FN_CPU_OFF: 105 kvm_psci_vcpu_off(vcpu); 106 val = KVM_PSCI_RET_SUCCESS; 107 break; 108 case KVM_PSCI_FN_CPU_ON: 109 val = kvm_psci_vcpu_on(vcpu); 110 break; 111 case KVM_PSCI_FN_CPU_SUSPEND: 112 case KVM_PSCI_FN_MIGRATE: 113 val = KVM_PSCI_RET_NI; 114 break; 115 116 default: 117 return false; 118 } 119 120 *vcpu_reg(vcpu, 0) = val; 121 return true; 122} 123