Updates for KVM/ARM including cpu=host and Cortex-A7 support
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQEcBAABAgAGBQJSXeGaAAoJEEtpOizt6ddyeyYH/AnWdKGUELjxC0lIBDkTitnD znyzSxqXG6z1Z6d+EYI3XCL1eB3dtyOBSJsZj45adG4HXGkCmGqosgDzivGO6GcI yhjYgXGhP8ZvIwky1ijbVQODaEE70SEYqKwyCpU4rLJw2uRkbfRaxTrpgnusL8Bg RG37uaOS/sasLoNxCe5GEUjm8BFGbvZGVAjcL7yJTPBw5qd7GYBxndFSTILa2iRQ ikoBD0bUVhoaBUqSNQenoNllUBwDpFJF1HiEXKMJkUIxX/FggrSvRp8A/MAWDBw0 6Ef1P8Pt/hMfMQpOOeu8QFWM2s+smh2rTkO/O9mqi/tSvEf5YcZHMAl48B8OR88= =tJ2u -----END PGP SIGNATURE----- Merge tag 'kvm-arm-for-3.13-1' of git://git.linaro.org/people/cdall/linux-kvm-arm into next Updates for KVM/ARM including cpu=host and Cortex-A7 support
This commit is contained in:
		
				commit
				
					
						d570142674
					
				
			
		
					 16 changed files with 270 additions and 134 deletions
				
			
		|  | @ -2304,7 +2304,31 @@ Possible features: | ||||||
| 	  Depends on KVM_CAP_ARM_EL1_32BIT (arm64 only). | 	  Depends on KVM_CAP_ARM_EL1_32BIT (arm64 only). | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 4.83 KVM_GET_REG_LIST | 4.83 KVM_ARM_PREFERRED_TARGET | ||||||
|  | 
 | ||||||
|  | Capability: basic | ||||||
|  | Architectures: arm, arm64 | ||||||
|  | Type: vm ioctl | ||||||
|  | Parameters: struct struct kvm_vcpu_init (out) | ||||||
|  | Returns: 0 on success; -1 on error | ||||||
|  | Errors: | ||||||
|  |   ENODEV:    no preferred target available for the host | ||||||
|  | 
 | ||||||
|  | This queries KVM for preferred CPU target type which can be emulated | ||||||
|  | by KVM on underlying host. | ||||||
|  | 
 | ||||||
|  | The ioctl returns struct kvm_vcpu_init instance containing information | ||||||
|  | about preferred CPU target type and recommended features for it.  The | ||||||
|  | kvm_vcpu_init->features bitmap returned will have feature bits set if | ||||||
|  | the preferred target recommends setting these features, but this is | ||||||
|  | not mandatory. | ||||||
|  | 
 | ||||||
|  | The information returned by this ioctl can be used to prepare an instance | ||||||
|  | of struct kvm_vcpu_init for KVM_ARM_VCPU_INIT ioctl which will result in | ||||||
|  | in VCPU matching underlying host. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 4.84 KVM_GET_REG_LIST | ||||||
| 
 | 
 | ||||||
| Capability: basic | Capability: basic | ||||||
| Architectures: arm, arm64 | Architectures: arm, arm64 | ||||||
|  | @ -2323,8 +2347,7 @@ struct kvm_reg_list { | ||||||
| This ioctl returns the guest registers that are supported for the | This ioctl returns the guest registers that are supported for the | ||||||
| KVM_GET_ONE_REG/KVM_SET_ONE_REG calls. | KVM_GET_ONE_REG/KVM_SET_ONE_REG calls. | ||||||
| 
 | 
 | ||||||
| 
 | 4.85 KVM_ARM_SET_DEVICE_ADDR | ||||||
| 4.84 KVM_ARM_SET_DEVICE_ADDR |  | ||||||
| 
 | 
 | ||||||
| Capability: KVM_CAP_ARM_SET_DEVICE_ADDR | Capability: KVM_CAP_ARM_SET_DEVICE_ADDR | ||||||
| Architectures: arm, arm64 | Architectures: arm, arm64 | ||||||
|  | @ -2362,7 +2385,7 @@ must be called after calling KVM_CREATE_IRQCHIP, but before calling | ||||||
| KVM_RUN on any of the VCPUs.  Calling this ioctl twice for any of the | KVM_RUN on any of the VCPUs.  Calling this ioctl twice for any of the | ||||||
| base addresses will return -EEXIST. | base addresses will return -EEXIST. | ||||||
| 
 | 
 | ||||||
| 4.85 KVM_PPC_RTAS_DEFINE_TOKEN | 4.86 KVM_PPC_RTAS_DEFINE_TOKEN | ||||||
| 
 | 
 | ||||||
| Capability: KVM_CAP_PPC_RTAS | Capability: KVM_CAP_PPC_RTAS | ||||||
| Architectures: ppc | Architectures: ppc | ||||||
|  |  | ||||||
|  | @ -95,12 +95,12 @@ | ||||||
| #define TTBCR_IRGN1	(3 << 24) | #define TTBCR_IRGN1	(3 << 24) | ||||||
| #define TTBCR_EPD1	(1 << 23) | #define TTBCR_EPD1	(1 << 23) | ||||||
| #define TTBCR_A1	(1 << 22) | #define TTBCR_A1	(1 << 22) | ||||||
| #define TTBCR_T1SZ	(3 << 16) | #define TTBCR_T1SZ	(7 << 16) | ||||||
| #define TTBCR_SH0	(3 << 12) | #define TTBCR_SH0	(3 << 12) | ||||||
| #define TTBCR_ORGN0	(3 << 10) | #define TTBCR_ORGN0	(3 << 10) | ||||||
| #define TTBCR_IRGN0	(3 << 8) | #define TTBCR_IRGN0	(3 << 8) | ||||||
| #define TTBCR_EPD0	(1 << 7) | #define TTBCR_EPD0	(1 << 7) | ||||||
| #define TTBCR_T0SZ	3 | #define TTBCR_T0SZ	(7 << 0) | ||||||
| #define HTCR_MASK	(TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0) | #define HTCR_MASK	(TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0) | ||||||
| 
 | 
 | ||||||
| /* Hyp System Trap Register */ | /* Hyp System Trap Register */ | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ | ||||||
| #define c6_IFAR		17	/* Instruction Fault Address Register */ | #define c6_IFAR		17	/* Instruction Fault Address Register */ | ||||||
| #define c7_PAR		18	/* Physical Address Register */ | #define c7_PAR		18	/* Physical Address Register */ | ||||||
| #define c7_PAR_high	19	/* PAR top 32 bits */ | #define c7_PAR_high	19	/* PAR top 32 bits */ | ||||||
| #define c9_L2CTLR	20	/* Cortex A15 L2 Control Register */ | #define c9_L2CTLR	20	/* Cortex A15/A7 L2 Control Register */ | ||||||
| #define c10_PRRR	21	/* Primary Region Remap Register */ | #define c10_PRRR	21	/* Primary Region Remap Register */ | ||||||
| #define c10_NMRR	22	/* Normal Memory Remap Register */ | #define c10_NMRR	22	/* Normal Memory Remap Register */ | ||||||
| #define c12_VBAR	23	/* Vector Base Address Register */ | #define c12_VBAR	23	/* Vector Base Address Register */ | ||||||
|  |  | ||||||
|  | @ -149,6 +149,7 @@ struct kvm_vcpu_stat { | ||||||
| struct kvm_vcpu_init; | struct kvm_vcpu_init; | ||||||
| int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, | int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, | ||||||
| 			const struct kvm_vcpu_init *init); | 			const struct kvm_vcpu_init *init); | ||||||
|  | int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init); | ||||||
| unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu); | unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu); | ||||||
| int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); | int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); | ||||||
| struct kvm_one_reg; | struct kvm_one_reg; | ||||||
|  |  | ||||||
|  | @ -63,7 +63,8 @@ struct kvm_regs { | ||||||
| 
 | 
 | ||||||
| /* Supported Processor Types */ | /* Supported Processor Types */ | ||||||
| #define KVM_ARM_TARGET_CORTEX_A15	0 | #define KVM_ARM_TARGET_CORTEX_A15	0 | ||||||
| #define KVM_ARM_NUM_TARGETS		1 | #define KVM_ARM_TARGET_CORTEX_A7	1 | ||||||
|  | #define KVM_ARM_NUM_TARGETS		2 | ||||||
| 
 | 
 | ||||||
| /* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ | /* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ | ||||||
| #define KVM_ARM_DEVICE_TYPE_SHIFT	0 | #define KVM_ARM_DEVICE_TYPE_SHIFT	0 | ||||||
|  |  | ||||||
|  | @ -19,6 +19,6 @@ kvm-arm-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o | ||||||
| 
 | 
 | ||||||
| obj-y += kvm-arm.o init.o interrupts.o | obj-y += kvm-arm.o init.o interrupts.o | ||||||
| obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o | obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o | ||||||
| obj-y += coproc.o coproc_a15.o mmio.o psci.o perf.o | obj-y += coproc.o coproc_a15.o coproc_a7.o mmio.o psci.o perf.o | ||||||
| obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o | obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o | ||||||
| obj-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o | obj-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o | ||||||
|  |  | ||||||
|  | @ -797,6 +797,19 @@ long kvm_arch_vm_ioctl(struct file *filp, | ||||||
| 			return -EFAULT; | 			return -EFAULT; | ||||||
| 		return kvm_vm_ioctl_set_device_addr(kvm, &dev_addr); | 		return kvm_vm_ioctl_set_device_addr(kvm, &dev_addr); | ||||||
| 	} | 	} | ||||||
|  | 	case KVM_ARM_PREFERRED_TARGET: { | ||||||
|  | 		int err; | ||||||
|  | 		struct kvm_vcpu_init init; | ||||||
|  | 
 | ||||||
|  | 		err = kvm_vcpu_preferred_target(&init); | ||||||
|  | 		if (err) | ||||||
|  | 			return err; | ||||||
|  | 
 | ||||||
|  | 		if (copy_to_user(argp, &init, sizeof(init))) | ||||||
|  | 			return -EFAULT; | ||||||
|  | 
 | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
| 	default: | 	default: | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -71,6 +71,92 @@ int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||||||
| 	return 1; | 	return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) | ||||||
|  | { | ||||||
|  | 	/*
 | ||||||
|  | 	 * Compute guest MPIDR. No need to mess around with different clusters | ||||||
|  | 	 * but we read the 'U' bit from the underlying hardware directly. | ||||||
|  | 	 */ | ||||||
|  | 	vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & MPIDR_SMP_BITMASK) | ||||||
|  | 					| vcpu->vcpu_id; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* TRM entries A7:4.3.31 A15:4.3.28 - RO WI */ | ||||||
|  | static bool access_actlr(struct kvm_vcpu *vcpu, | ||||||
|  | 			 const struct coproc_params *p, | ||||||
|  | 			 const struct coproc_reg *r) | ||||||
|  | { | ||||||
|  | 	if (p->is_write) | ||||||
|  | 		return ignore_write(vcpu, p); | ||||||
|  | 
 | ||||||
|  | 	*vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c1_ACTLR]; | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* TRM entries A7:4.3.56, A15:4.3.60 - R/O. */ | ||||||
|  | static bool access_cbar(struct kvm_vcpu *vcpu, | ||||||
|  | 			const struct coproc_params *p, | ||||||
|  | 			const struct coproc_reg *r) | ||||||
|  | { | ||||||
|  | 	if (p->is_write) | ||||||
|  | 		return write_to_read_only(vcpu, p); | ||||||
|  | 	return read_zero(vcpu, p); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* TRM entries A7:4.3.49, A15:4.3.48 - R/O WI */ | ||||||
|  | static bool access_l2ctlr(struct kvm_vcpu *vcpu, | ||||||
|  | 			  const struct coproc_params *p, | ||||||
|  | 			  const struct coproc_reg *r) | ||||||
|  | { | ||||||
|  | 	if (p->is_write) | ||||||
|  | 		return ignore_write(vcpu, p); | ||||||
|  | 
 | ||||||
|  | 	*vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c9_L2CTLR]; | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void reset_l2ctlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) | ||||||
|  | { | ||||||
|  | 	u32 l2ctlr, ncores; | ||||||
|  | 
 | ||||||
|  | 	asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr)); | ||||||
|  | 	l2ctlr &= ~(3 << 24); | ||||||
|  | 	ncores = atomic_read(&vcpu->kvm->online_vcpus) - 1; | ||||||
|  | 	l2ctlr |= (ncores & 3) << 24; | ||||||
|  | 
 | ||||||
|  | 	vcpu->arch.cp15[c9_L2CTLR] = l2ctlr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void reset_actlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) | ||||||
|  | { | ||||||
|  | 	u32 actlr; | ||||||
|  | 
 | ||||||
|  | 	/* ACTLR contains SMP bit: make sure you create all cpus first! */ | ||||||
|  | 	asm volatile("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr)); | ||||||
|  | 	/* Make the SMP bit consistent with the guest configuration */ | ||||||
|  | 	if (atomic_read(&vcpu->kvm->online_vcpus) > 1) | ||||||
|  | 		actlr |= 1U << 6; | ||||||
|  | 	else | ||||||
|  | 		actlr &= ~(1U << 6); | ||||||
|  | 
 | ||||||
|  | 	vcpu->arch.cp15[c1_ACTLR] = actlr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * TRM entries: A7:4.3.50, A15:4.3.49 | ||||||
|  |  * R/O WI (even if NSACR.NS_L2ERR, a write of 1 is ignored). | ||||||
|  |  */ | ||||||
|  | static bool access_l2ectlr(struct kvm_vcpu *vcpu, | ||||||
|  | 			   const struct coproc_params *p, | ||||||
|  | 			   const struct coproc_reg *r) | ||||||
|  | { | ||||||
|  | 	if (p->is_write) | ||||||
|  | 		return ignore_write(vcpu, p); | ||||||
|  | 
 | ||||||
|  | 	*vcpu_reg(vcpu, p->Rt1) = 0; | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* See note at ARM ARM B1.14.4 */ | /* See note at ARM ARM B1.14.4 */ | ||||||
| static bool access_dcsw(struct kvm_vcpu *vcpu, | static bool access_dcsw(struct kvm_vcpu *vcpu, | ||||||
| 			const struct coproc_params *p, | 			const struct coproc_params *p, | ||||||
|  | @ -153,10 +239,22 @@ static bool pm_fake(struct kvm_vcpu *vcpu, | ||||||
|  *            registers preceding 32-bit ones. |  *            registers preceding 32-bit ones. | ||||||
|  */ |  */ | ||||||
| static const struct coproc_reg cp15_regs[] = { | static const struct coproc_reg cp15_regs[] = { | ||||||
|  | 	/* MPIDR: we use VMPIDR for guest access. */ | ||||||
|  | 	{ CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32, | ||||||
|  | 			NULL, reset_mpidr, c0_MPIDR }, | ||||||
|  | 
 | ||||||
| 	/* CSSELR: swapped by interrupt.S. */ | 	/* CSSELR: swapped by interrupt.S. */ | ||||||
| 	{ CRn( 0), CRm( 0), Op1( 2), Op2( 0), is32, | 	{ CRn( 0), CRm( 0), Op1( 2), Op2( 0), is32, | ||||||
| 			NULL, reset_unknown, c0_CSSELR }, | 			NULL, reset_unknown, c0_CSSELR }, | ||||||
| 
 | 
 | ||||||
|  | 	/* ACTLR: trapped by HCR.TAC bit. */ | ||||||
|  | 	{ CRn( 1), CRm( 0), Op1( 0), Op2( 1), is32, | ||||||
|  | 			access_actlr, reset_actlr, c1_ACTLR }, | ||||||
|  | 
 | ||||||
|  | 	/* CPACR: swapped by interrupt.S. */ | ||||||
|  | 	{ CRn( 1), CRm( 0), Op1( 0), Op2( 2), is32, | ||||||
|  | 			NULL, reset_val, c1_CPACR, 0x00000000 }, | ||||||
|  | 
 | ||||||
| 	/* TTBR0/TTBR1: swapped by interrupt.S. */ | 	/* TTBR0/TTBR1: swapped by interrupt.S. */ | ||||||
| 	{ CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 }, | 	{ CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 }, | ||||||
| 	{ CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 }, | 	{ CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 }, | ||||||
|  | @ -194,6 +292,13 @@ static const struct coproc_reg cp15_regs[] = { | ||||||
| 	{ CRn( 7), CRm( 6), Op1( 0), Op2( 2), is32, access_dcsw}, | 	{ CRn( 7), CRm( 6), Op1( 0), Op2( 2), is32, access_dcsw}, | ||||||
| 	{ CRn( 7), CRm(10), Op1( 0), Op2( 2), is32, access_dcsw}, | 	{ CRn( 7), CRm(10), Op1( 0), Op2( 2), is32, access_dcsw}, | ||||||
| 	{ CRn( 7), CRm(14), Op1( 0), Op2( 2), is32, access_dcsw}, | 	{ CRn( 7), CRm(14), Op1( 0), Op2( 2), is32, access_dcsw}, | ||||||
|  | 	/*
 | ||||||
|  | 	 * L2CTLR access (guest wants to know #CPUs). | ||||||
|  | 	 */ | ||||||
|  | 	{ CRn( 9), CRm( 0), Op1( 1), Op2( 2), is32, | ||||||
|  | 			access_l2ctlr, reset_l2ctlr, c9_L2CTLR }, | ||||||
|  | 	{ CRn( 9), CRm( 0), Op1( 1), Op2( 3), is32, access_l2ectlr}, | ||||||
|  | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Dummy performance monitor implementation. | 	 * Dummy performance monitor implementation. | ||||||
| 	 */ | 	 */ | ||||||
|  | @ -234,6 +339,9 @@ static const struct coproc_reg cp15_regs[] = { | ||||||
| 	/* CNTKCTL: swapped by interrupt.S. */ | 	/* CNTKCTL: swapped by interrupt.S. */ | ||||||
| 	{ CRn(14), CRm( 1), Op1( 0), Op2( 0), is32, | 	{ CRn(14), CRm( 1), Op1( 0), Op2( 0), is32, | ||||||
| 			NULL, reset_val, c14_CNTKCTL, 0x00000000 }, | 			NULL, reset_val, c14_CNTKCTL, 0x00000000 }, | ||||||
|  | 
 | ||||||
|  | 	/* The Configuration Base Address Register. */ | ||||||
|  | 	{ CRn(15), CRm( 0), Op1( 4), Op2( 0), is32, access_cbar}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* Target specific emulation tables */ | /* Target specific emulation tables */ | ||||||
|  | @ -241,6 +349,12 @@ static struct kvm_coproc_target_table *target_tables[KVM_ARM_NUM_TARGETS]; | ||||||
| 
 | 
 | ||||||
| void kvm_register_target_coproc_table(struct kvm_coproc_target_table *table) | void kvm_register_target_coproc_table(struct kvm_coproc_target_table *table) | ||||||
| { | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 1; i < table->num; i++) | ||||||
|  | 		BUG_ON(cmp_reg(&table->table[i-1], | ||||||
|  | 			       &table->table[i]) >= 0); | ||||||
|  | 
 | ||||||
| 	target_tables[table->target] = table; | 	target_tables[table->target] = table; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,101 +17,12 @@ | ||||||
|  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. |  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||||
|  */ |  */ | ||||||
| #include <linux/kvm_host.h> | #include <linux/kvm_host.h> | ||||||
| #include <asm/cputype.h> |  | ||||||
| #include <asm/kvm_arm.h> |  | ||||||
| #include <asm/kvm_host.h> |  | ||||||
| #include <asm/kvm_emulate.h> |  | ||||||
| #include <asm/kvm_coproc.h> | #include <asm/kvm_coproc.h> | ||||||
|  | #include <asm/kvm_emulate.h> | ||||||
| #include <linux/init.h> | #include <linux/init.h> | ||||||
| 
 | 
 | ||||||
| static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) |  | ||||||
| { |  | ||||||
| 	/*
 |  | ||||||
| 	 * Compute guest MPIDR: |  | ||||||
| 	 * (Even if we present only one VCPU to the guest on an SMP |  | ||||||
| 	 * host we don't set the U bit in the MPIDR, or vice versa, as |  | ||||||
| 	 * revealing the underlying hardware properties is likely to |  | ||||||
| 	 * be the best choice). |  | ||||||
| 	 */ |  | ||||||
| 	vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~MPIDR_LEVEL_MASK) |  | ||||||
| 		| (vcpu->vcpu_id & MPIDR_LEVEL_MASK); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #include "coproc.h" | #include "coproc.h" | ||||||
| 
 | 
 | ||||||
| /* A15 TRM 4.3.28: RO WI */ |  | ||||||
| static bool access_actlr(struct kvm_vcpu *vcpu, |  | ||||||
| 			 const struct coproc_params *p, |  | ||||||
| 			 const struct coproc_reg *r) |  | ||||||
| { |  | ||||||
| 	if (p->is_write) |  | ||||||
| 		return ignore_write(vcpu, p); |  | ||||||
| 
 |  | ||||||
| 	*vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c1_ACTLR]; |  | ||||||
| 	return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* A15 TRM 4.3.60: R/O. */ |  | ||||||
| static bool access_cbar(struct kvm_vcpu *vcpu, |  | ||||||
| 			const struct coproc_params *p, |  | ||||||
| 			const struct coproc_reg *r) |  | ||||||
| { |  | ||||||
| 	if (p->is_write) |  | ||||||
| 		return write_to_read_only(vcpu, p); |  | ||||||
| 	return read_zero(vcpu, p); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* A15 TRM 4.3.48: R/O WI. */ |  | ||||||
| static bool access_l2ctlr(struct kvm_vcpu *vcpu, |  | ||||||
| 			  const struct coproc_params *p, |  | ||||||
| 			  const struct coproc_reg *r) |  | ||||||
| { |  | ||||||
| 	if (p->is_write) |  | ||||||
| 		return ignore_write(vcpu, p); |  | ||||||
| 
 |  | ||||||
| 	*vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c9_L2CTLR]; |  | ||||||
| 	return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void reset_l2ctlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) |  | ||||||
| { |  | ||||||
| 	u32 l2ctlr, ncores; |  | ||||||
| 
 |  | ||||||
| 	asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr)); |  | ||||||
| 	l2ctlr &= ~(3 << 24); |  | ||||||
| 	ncores = atomic_read(&vcpu->kvm->online_vcpus) - 1; |  | ||||||
| 	l2ctlr |= (ncores & 3) << 24; |  | ||||||
| 
 |  | ||||||
| 	vcpu->arch.cp15[c9_L2CTLR] = l2ctlr; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void reset_actlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) |  | ||||||
| { |  | ||||||
| 	u32 actlr; |  | ||||||
| 
 |  | ||||||
| 	/* ACTLR contains SMP bit: make sure you create all cpus first! */ |  | ||||||
| 	asm volatile("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr)); |  | ||||||
| 	/* Make the SMP bit consistent with the guest configuration */ |  | ||||||
| 	if (atomic_read(&vcpu->kvm->online_vcpus) > 1) |  | ||||||
| 		actlr |= 1U << 6; |  | ||||||
| 	else |  | ||||||
| 		actlr &= ~(1U << 6); |  | ||||||
| 
 |  | ||||||
| 	vcpu->arch.cp15[c1_ACTLR] = actlr; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* A15 TRM 4.3.49: R/O WI (even if NSACR.NS_L2ERR, a write of 1 is ignored). */ |  | ||||||
| static bool access_l2ectlr(struct kvm_vcpu *vcpu, |  | ||||||
| 			   const struct coproc_params *p, |  | ||||||
| 			   const struct coproc_reg *r) |  | ||||||
| { |  | ||||||
| 	if (p->is_write) |  | ||||||
| 		return ignore_write(vcpu, p); |  | ||||||
| 
 |  | ||||||
| 	*vcpu_reg(vcpu, p->Rt1) = 0; |  | ||||||
| 	return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*
 | /*
 | ||||||
|  * A15-specific CP15 registers. |  * A15-specific CP15 registers. | ||||||
|  * CRn denotes the primary register number, but is copied to the CRm in the |  * CRn denotes the primary register number, but is copied to the CRm in the | ||||||
|  | @ -121,29 +32,9 @@ static bool access_l2ectlr(struct kvm_vcpu *vcpu, | ||||||
|  *            registers preceding 32-bit ones. |  *            registers preceding 32-bit ones. | ||||||
|  */ |  */ | ||||||
| static const struct coproc_reg a15_regs[] = { | static const struct coproc_reg a15_regs[] = { | ||||||
| 	/* MPIDR: we use VMPIDR for guest access. */ |  | ||||||
| 	{ CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32, |  | ||||||
| 			NULL, reset_mpidr, c0_MPIDR }, |  | ||||||
| 
 |  | ||||||
| 	/* SCTLR: swapped by interrupt.S. */ | 	/* SCTLR: swapped by interrupt.S. */ | ||||||
| 	{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32, | 	{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32, | ||||||
| 			NULL, reset_val, c1_SCTLR, 0x00C50078 }, | 			NULL, reset_val, c1_SCTLR, 0x00C50078 }, | ||||||
| 	/* ACTLR: trapped by HCR.TAC bit. */ |  | ||||||
| 	{ CRn( 1), CRm( 0), Op1( 0), Op2( 1), is32, |  | ||||||
| 			access_actlr, reset_actlr, c1_ACTLR }, |  | ||||||
| 	/* CPACR: swapped by interrupt.S. */ |  | ||||||
| 	{ CRn( 1), CRm( 0), Op1( 0), Op2( 2), is32, |  | ||||||
| 			NULL, reset_val, c1_CPACR, 0x00000000 }, |  | ||||||
| 
 |  | ||||||
| 	/*
 |  | ||||||
| 	 * L2CTLR access (guest wants to know #CPUs). |  | ||||||
| 	 */ |  | ||||||
| 	{ CRn( 9), CRm( 0), Op1( 1), Op2( 2), is32, |  | ||||||
| 			access_l2ctlr, reset_l2ctlr, c9_L2CTLR }, |  | ||||||
| 	{ CRn( 9), CRm( 0), Op1( 1), Op2( 3), is32, access_l2ectlr}, |  | ||||||
| 
 |  | ||||||
| 	/* The Configuration Base Address Register. */ |  | ||||||
| 	{ CRn(15), CRm( 0), Op1( 4), Op2( 0), is32, access_cbar}, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static struct kvm_coproc_target_table a15_target_table = { | static struct kvm_coproc_target_table a15_target_table = { | ||||||
|  | @ -154,12 +45,6 @@ static struct kvm_coproc_target_table a15_target_table = { | ||||||
| 
 | 
 | ||||||
| static int __init coproc_a15_init(void) | static int __init coproc_a15_init(void) | ||||||
| { | { | ||||||
| 	unsigned int i; |  | ||||||
| 
 |  | ||||||
| 	for (i = 1; i < ARRAY_SIZE(a15_regs); i++) |  | ||||||
| 		BUG_ON(cmp_reg(&a15_regs[i-1], |  | ||||||
| 			       &a15_regs[i]) >= 0); |  | ||||||
| 
 |  | ||||||
| 	kvm_register_target_coproc_table(&a15_target_table); | 	kvm_register_target_coproc_table(&a15_target_table); | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										54
									
								
								arch/arm/kvm/coproc_a7.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								arch/arm/kvm/coproc_a7.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,54 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2012 - Virtual Open Systems and Columbia University | ||||||
|  |  * Copyright (C) 2013 - ARM Ltd | ||||||
|  |  * | ||||||
|  |  * Authors: Rusty Russell <rusty@rustcorp.au> | ||||||
|  |  *          Christoffer Dall <c.dall@virtualopensystems.com> | ||||||
|  |  *          Jonathan Austin <jonathan.austin@arm.com> | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License, version 2, as | ||||||
|  |  * published by the Free Software Foundation. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program; if not, write to the Free Software | ||||||
|  |  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||||
|  |  */ | ||||||
|  | #include <linux/kvm_host.h> | ||||||
|  | #include <asm/kvm_coproc.h> | ||||||
|  | #include <asm/kvm_emulate.h> | ||||||
|  | #include <linux/init.h> | ||||||
|  | 
 | ||||||
|  | #include "coproc.h" | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Cortex-A7 specific CP15 registers. | ||||||
|  |  * CRn denotes the primary register number, but is copied to the CRm in the | ||||||
|  |  * user space API for 64-bit register access in line with the terminology used | ||||||
|  |  * in the ARM ARM. | ||||||
|  |  * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit | ||||||
|  |  *            registers preceding 32-bit ones. | ||||||
|  |  */ | ||||||
|  | static const struct coproc_reg a7_regs[] = { | ||||||
|  | 	/* SCTLR: swapped by interrupt.S. */ | ||||||
|  | 	{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32, | ||||||
|  | 			NULL, reset_val, c1_SCTLR, 0x00C50878 }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct kvm_coproc_target_table a7_target_table = { | ||||||
|  | 	.target = KVM_ARM_TARGET_CORTEX_A7, | ||||||
|  | 	.table = a7_regs, | ||||||
|  | 	.num = ARRAY_SIZE(a7_regs), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int __init coproc_a7_init(void) | ||||||
|  | { | ||||||
|  | 	kvm_register_target_coproc_table(&a7_target_table); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | late_initcall(coproc_a7_init); | ||||||
|  | @ -354,7 +354,7 @@ static void inject_abt(struct kvm_vcpu *vcpu, bool is_pabt, unsigned long addr) | ||||||
| 	*vcpu_pc(vcpu) = exc_vector_base(vcpu) + vect_offset; | 	*vcpu_pc(vcpu) = exc_vector_base(vcpu) + vect_offset; | ||||||
| 
 | 
 | ||||||
| 	if (is_pabt) { | 	if (is_pabt) { | ||||||
| 		/* Set DFAR and DFSR */ | 		/* Set IFAR and IFSR */ | ||||||
| 		vcpu->arch.cp15[c6_IFAR] = addr; | 		vcpu->arch.cp15[c6_IFAR] = addr; | ||||||
| 		is_lpae = (vcpu->arch.cp15[c2_TTBCR] >> 31); | 		is_lpae = (vcpu->arch.cp15[c2_TTBCR] >> 31); | ||||||
| 		/* Always give debug fault for now - should give guest a clue */ | 		/* Always give debug fault for now - should give guest a clue */ | ||||||
|  |  | ||||||
|  | @ -190,6 +190,8 @@ int __attribute_const__ kvm_target_cpu(void) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	switch (part_number) { | 	switch (part_number) { | ||||||
|  | 	case ARM_CPU_PART_CORTEX_A7: | ||||||
|  | 		return KVM_ARM_TARGET_CORTEX_A7; | ||||||
| 	case ARM_CPU_PART_CORTEX_A15: | 	case ARM_CPU_PART_CORTEX_A15: | ||||||
| 		return KVM_ARM_TARGET_CORTEX_A15; | 		return KVM_ARM_TARGET_CORTEX_A15; | ||||||
| 	default: | 	default: | ||||||
|  | @ -202,7 +204,7 @@ int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, | ||||||
| { | { | ||||||
| 	unsigned int i; | 	unsigned int i; | ||||||
| 
 | 
 | ||||||
| 	/* We can only do a cortex A15 for now. */ | 	/* We can only cope with guest==host and only on A15/A7 (for now). */ | ||||||
| 	if (init->target != kvm_target_cpu()) | 	if (init->target != kvm_target_cpu()) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
|  | @ -222,6 +224,26 @@ int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, | ||||||
| 	return kvm_reset_vcpu(vcpu); | 	return kvm_reset_vcpu(vcpu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init) | ||||||
|  | { | ||||||
|  | 	int target = kvm_target_cpu(); | ||||||
|  | 
 | ||||||
|  | 	if (target < 0) | ||||||
|  | 		return -ENODEV; | ||||||
|  | 
 | ||||||
|  | 	memset(init, 0, sizeof(*init)); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * For now, we don't return any features. | ||||||
|  | 	 * In future, we might use features to return target | ||||||
|  | 	 * specific features available for the preferred | ||||||
|  | 	 * target type. | ||||||
|  | 	 */ | ||||||
|  | 	init->target = (__u32)target; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) | int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) | ||||||
| { | { | ||||||
| 	return -EINVAL; | 	return -EINVAL; | ||||||
|  |  | ||||||
|  | @ -30,16 +30,16 @@ | ||||||
| #include <kvm/arm_arch_timer.h> | #include <kvm/arm_arch_timer.h> | ||||||
| 
 | 
 | ||||||
| /******************************************************************************
 | /******************************************************************************
 | ||||||
|  * Cortex-A15 Reset Values |  * Cortex-A15 and Cortex-A7 Reset Values | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| static const int a15_max_cpu_idx = 3; | static const int cortexa_max_cpu_idx = 3; | ||||||
| 
 | 
 | ||||||
| static struct kvm_regs a15_regs_reset = { | static struct kvm_regs cortexa_regs_reset = { | ||||||
| 	.usr_regs.ARM_cpsr = SVC_MODE | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT, | 	.usr_regs.ARM_cpsr = SVC_MODE | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const struct kvm_irq_level a15_vtimer_irq = { | static const struct kvm_irq_level cortexa_vtimer_irq = { | ||||||
| 	{ .irq = 27 }, | 	{ .irq = 27 }, | ||||||
| 	.level = 1, | 	.level = 1, | ||||||
| }; | }; | ||||||
|  | @ -62,12 +62,13 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu) | ||||||
| 	const struct kvm_irq_level *cpu_vtimer_irq; | 	const struct kvm_irq_level *cpu_vtimer_irq; | ||||||
| 
 | 
 | ||||||
| 	switch (vcpu->arch.target) { | 	switch (vcpu->arch.target) { | ||||||
|  | 	case KVM_ARM_TARGET_CORTEX_A7: | ||||||
| 	case KVM_ARM_TARGET_CORTEX_A15: | 	case KVM_ARM_TARGET_CORTEX_A15: | ||||||
| 		if (vcpu->vcpu_id > a15_max_cpu_idx) | 		if (vcpu->vcpu_id > cortexa_max_cpu_idx) | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 		cpu_reset = &a15_regs_reset; | 		cpu_reset = &cortexa_regs_reset; | ||||||
| 		vcpu->arch.midr = read_cpuid_id(); | 		vcpu->arch.midr = read_cpuid_id(); | ||||||
| 		cpu_vtimer_irq = &a15_vtimer_irq; | 		cpu_vtimer_irq = &cortexa_vtimer_irq; | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		return -ENODEV; | 		return -ENODEV; | ||||||
|  |  | ||||||
|  | @ -146,6 +146,7 @@ struct kvm_vcpu_stat { | ||||||
| struct kvm_vcpu_init; | struct kvm_vcpu_init; | ||||||
| int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, | int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, | ||||||
| 			const struct kvm_vcpu_init *init); | 			const struct kvm_vcpu_init *init); | ||||||
|  | int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init); | ||||||
| unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu); | unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu); | ||||||
| int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); | int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); | ||||||
| struct kvm_one_reg; | struct kvm_one_reg; | ||||||
|  |  | ||||||
|  | @ -248,6 +248,26 @@ int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, | ||||||
| 	return kvm_reset_vcpu(vcpu); | 	return kvm_reset_vcpu(vcpu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init) | ||||||
|  | { | ||||||
|  | 	int target = kvm_target_cpu(); | ||||||
|  | 
 | ||||||
|  | 	if (target < 0) | ||||||
|  | 		return -ENODEV; | ||||||
|  | 
 | ||||||
|  | 	memset(init, 0, sizeof(*init)); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * For now, we don't return any features. | ||||||
|  | 	 * In future, we might use features to return target | ||||||
|  | 	 * specific features available for the preferred | ||||||
|  | 	 * target type. | ||||||
|  | 	 */ | ||||||
|  | 	init->target = (__u32)target; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) | int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) | ||||||
| { | { | ||||||
| 	return -EINVAL; | 	return -EINVAL; | ||||||
|  |  | ||||||
|  | @ -1012,6 +1012,7 @@ struct kvm_s390_ucas_mapping { | ||||||
| /* VM is being stopped by host */ | /* VM is being stopped by host */ | ||||||
| #define KVM_KVMCLOCK_CTRL	  _IO(KVMIO,   0xad) | #define KVM_KVMCLOCK_CTRL	  _IO(KVMIO,   0xad) | ||||||
| #define KVM_ARM_VCPU_INIT	  _IOW(KVMIO,  0xae, struct kvm_vcpu_init) | #define KVM_ARM_VCPU_INIT	  _IOW(KVMIO,  0xae, struct kvm_vcpu_init) | ||||||
|  | #define KVM_ARM_PREFERRED_TARGET  _IOR(KVMIO,  0xaf, struct kvm_vcpu_init) | ||||||
| #define KVM_GET_REG_LIST	  _IOWR(KVMIO, 0xb0, struct kvm_reg_list) | #define KVM_GET_REG_LIST	  _IOWR(KVMIO, 0xb0, struct kvm_reg_list) | ||||||
| 
 | 
 | ||||||
| #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0) | #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gleb Natapov
				Gleb Natapov