]> Pileus Git - ~andy/linux/blob - arch/mips/kvm/kvm_mips_dyntrans.c
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
[~andy/linux] / arch / mips / kvm / kvm_mips_dyntrans.c
1 /*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License.  See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * KVM/MIPS: Binary Patching for privileged instructions, reduces traps.
7 *
8 * Copyright (C) 2012  MIPS Technologies, Inc.  All rights reserved.
9 * Authors: Sanjay Lal <sanjayl@kymasys.com>
10 */
11
12 #include <linux/errno.h>
13 #include <linux/err.h>
14 #include <linux/kvm_host.h>
15 #include <linux/module.h>
16 #include <linux/vmalloc.h>
17 #include <linux/fs.h>
18 #include <linux/bootmem.h>
19
20 #include "kvm_mips_comm.h"
21
22 #define SYNCI_TEMPLATE  0x041f0000
23 #define SYNCI_BASE(x)   (((x) >> 21) & 0x1f)
24 #define SYNCI_OFFSET    ((x) & 0xffff)
25
26 #define LW_TEMPLATE     0x8c000000
27 #define CLEAR_TEMPLATE  0x00000020
28 #define SW_TEMPLATE     0xac000000
29
30 int
31 kvm_mips_trans_cache_index(uint32_t inst, uint32_t *opc,
32                            struct kvm_vcpu *vcpu)
33 {
34         int result = 0;
35         unsigned long kseg0_opc;
36         uint32_t synci_inst = 0x0;
37
38         /* Replace the CACHE instruction, with a NOP */
39         kseg0_opc =
40             CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa
41                        (vcpu, (unsigned long) opc));
42         memcpy((void *)kseg0_opc, (void *)&synci_inst, sizeof(uint32_t));
43         mips32_SyncICache(kseg0_opc, 32);
44
45         return result;
46 }
47
48 /*
49  *  Address based CACHE instructions are transformed into synci(s). A little heavy
50  * for just D-cache invalidates, but avoids an expensive trap
51  */
52 int
53 kvm_mips_trans_cache_va(uint32_t inst, uint32_t *opc,
54                         struct kvm_vcpu *vcpu)
55 {
56         int result = 0;
57         unsigned long kseg0_opc;
58         uint32_t synci_inst = SYNCI_TEMPLATE, base, offset;
59
60         base = (inst >> 21) & 0x1f;
61         offset = inst & 0xffff;
62         synci_inst |= (base << 21);
63         synci_inst |= offset;
64
65         kseg0_opc =
66             CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa
67                        (vcpu, (unsigned long) opc));
68         memcpy((void *)kseg0_opc, (void *)&synci_inst, sizeof(uint32_t));
69         mips32_SyncICache(kseg0_opc, 32);
70
71         return result;
72 }
73
74 int
75 kvm_mips_trans_mfc0(uint32_t inst, uint32_t *opc, struct kvm_vcpu *vcpu)
76 {
77         int32_t rt, rd, sel;
78         uint32_t mfc0_inst;
79         unsigned long kseg0_opc, flags;
80
81         rt = (inst >> 16) & 0x1f;
82         rd = (inst >> 11) & 0x1f;
83         sel = inst & 0x7;
84
85         if ((rd == MIPS_CP0_ERRCTL) && (sel == 0)) {
86                 mfc0_inst = CLEAR_TEMPLATE;
87                 mfc0_inst |= ((rt & 0x1f) << 16);
88         } else {
89                 mfc0_inst = LW_TEMPLATE;
90                 mfc0_inst |= ((rt & 0x1f) << 16);
91                 mfc0_inst |=
92                     offsetof(struct mips_coproc,
93                              reg[rd][sel]) + offsetof(struct kvm_mips_commpage,
94                                                       cop0);
95         }
96
97         if (KVM_GUEST_KSEGX(opc) == KVM_GUEST_KSEG0) {
98                 kseg0_opc =
99                     CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa
100                                (vcpu, (unsigned long) opc));
101                 memcpy((void *)kseg0_opc, (void *)&mfc0_inst, sizeof(uint32_t));
102                 mips32_SyncICache(kseg0_opc, 32);
103         } else if (KVM_GUEST_KSEGX((unsigned long) opc) == KVM_GUEST_KSEG23) {
104                 local_irq_save(flags);
105                 memcpy((void *)opc, (void *)&mfc0_inst, sizeof(uint32_t));
106                 mips32_SyncICache((unsigned long) opc, 32);
107                 local_irq_restore(flags);
108         } else {
109                 kvm_err("%s: Invalid address: %p\n", __func__, opc);
110                 return -EFAULT;
111         }
112
113         return 0;
114 }
115
116 int
117 kvm_mips_trans_mtc0(uint32_t inst, uint32_t *opc, struct kvm_vcpu *vcpu)
118 {
119         int32_t rt, rd, sel;
120         uint32_t mtc0_inst = SW_TEMPLATE;
121         unsigned long kseg0_opc, flags;
122
123         rt = (inst >> 16) & 0x1f;
124         rd = (inst >> 11) & 0x1f;
125         sel = inst & 0x7;
126
127         mtc0_inst |= ((rt & 0x1f) << 16);
128         mtc0_inst |=
129             offsetof(struct mips_coproc,
130                      reg[rd][sel]) + offsetof(struct kvm_mips_commpage, cop0);
131
132         if (KVM_GUEST_KSEGX(opc) == KVM_GUEST_KSEG0) {
133                 kseg0_opc =
134                     CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa
135                                (vcpu, (unsigned long) opc));
136                 memcpy((void *)kseg0_opc, (void *)&mtc0_inst, sizeof(uint32_t));
137                 mips32_SyncICache(kseg0_opc, 32);
138         } else if (KVM_GUEST_KSEGX((unsigned long) opc) == KVM_GUEST_KSEG23) {
139                 local_irq_save(flags);
140                 memcpy((void *)opc, (void *)&mtc0_inst, sizeof(uint32_t));
141                 mips32_SyncICache((unsigned long) opc, 32);
142                 local_irq_restore(flags);
143         } else {
144                 kvm_err("%s: Invalid address: %p\n", __func__, opc);
145                 return -EFAULT;
146         }
147
148         return 0;
149 }