Delete unsupported entries for userspace MSR handling.
[akaros.git] / user / vmm / vmxmsr.c
1 /*
2  * MSR emulation
3  *
4  * Copyright 2015 Google Inc.
5  *
6  * See LICENSE for details.
7  */
8
9 #include <stdio.h>
10 #include <sys/types.h>
11 #include <pthread.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <parlib/arch/arch.h>
15 #include <parlib/ros_debug.h>
16 #include <unistd.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/uio.h>
21 #include <stdint.h>
22 #include <err.h>
23 #include <sys/mman.h>
24 #include <ros/vmm.h>
25 #include <ros/arch/msr-index.h>
26 #include <vmm/virtio.h>
27 #include <vmm/virtio_mmio.h>
28 #include <vmm/virtio_ids.h>
29 #include <vmm/virtio_config.h>
30 #include <vmm/sched.h>
31 #include <ros/arch/trapframe.h>
32
33 struct emmsr {
34         uint32_t reg;
35         char *name;
36         int (*f)(struct guest_thread *vm_thread, struct emmsr *, uint32_t);
37         bool written;
38         uint32_t edx, eax;
39 };
40 // Might need to mfence rdmsr.  supposedly wrmsr serializes, but not for x2APIC
41 static inline uint64_t read_msr(uint32_t reg)
42 {
43         uint32_t edx, eax;
44         asm volatile("rdmsr; mfence" : "=d"(edx), "=a"(eax) : "c"(reg));
45         return (uint64_t)edx << 32 | eax;
46 }
47
48 static inline void write_msr(uint32_t reg, uint64_t val)
49 {
50         asm volatile("wrmsr" : : "d"((uint32_t)(val >> 32)),
51                                  "a"((uint32_t)(val & 0xFFFFFFFF)), 
52                                  "c"(reg));
53 }
54
55 static int emsr_miscenable(struct guest_thread *vm_thread, struct emmsr *,
56                            uint32_t);
57 static int emsr_mustmatch(struct guest_thread *vm_thread, struct emmsr *,
58                           uint32_t);
59 static int emsr_readonly(struct guest_thread *vm_thread, struct emmsr *,
60                          uint32_t);
61 static int emsr_readzero(struct guest_thread *vm_thread, struct emmsr *,
62                          uint32_t);
63 static int emsr_fakewrite(struct guest_thread *vm_thread, struct emmsr *,
64                           uint32_t);
65 static int emsr_ok(struct guest_thread *vm_thread, struct emmsr *, uint32_t);
66
67 struct emmsr emmsrs[] = {
68         {MSR_RAPL_POWER_UNIT, "MSR_RAPL_POWER_UNIT", emsr_readzero},
69 };
70
71 static uint64_t set_low32(uint64_t hi, uint32_t lo)
72 {
73         return (hi & 0xffffffff00000000ULL) | lo;
74 }
75
76 static uint64_t set_low16(uint64_t hi, uint16_t lo)
77 {
78         return (hi & 0xffffffffffff0000ULL) | lo;
79 }
80
81 static uint64_t set_low8(uint64_t hi, uint8_t lo)
82 {
83         return (hi & 0xffffffffffffff00ULL) | lo;
84 }
85
86 /* this may be the only register that needs special handling.
87  * If there others then we might want to extend teh emmsr struct.
88  */
89 static int emsr_miscenable(struct guest_thread *vm_thread, struct emmsr *msr,
90                            uint32_t opcode) {
91         uint32_t eax, edx;
92         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
93
94         rdmsr(msr->reg, eax, edx);
95         /* we just let them read the misc msr for now. */
96         if (opcode == EXIT_REASON_MSR_READ) {
97                 vm_tf->tf_rax = set_low32(vm_tf->tf_rax, eax);
98                 vm_tf->tf_rax |= MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL;
99                 vm_tf->tf_rdx = set_low32(vm_tf->tf_rdx, edx);
100                 return 0;
101         } else {
102                 /* if they are writing what is already written, that's ok. */
103                 if (((uint32_t) vm_tf->tf_rax == eax)
104                     && ((uint32_t) vm_tf->tf_rdx == edx))
105                         return 0;
106         }
107         fprintf(stderr,
108                 "%s: Wanted to write 0x%x:0x%x, but could not; value was 0x%x:0x%x\n",
109                  msr->name, (uint32_t) vm_tf->tf_rdx,
110                  (uint32_t) vm_tf->tf_rax, edx, eax);
111         return SHUTDOWN_UNHANDLED_EXIT_REASON;
112 }
113
114 static int emsr_mustmatch(struct guest_thread *vm_thread, struct emmsr *msr,
115                           uint32_t opcode) {
116         uint32_t eax, edx;
117         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
118
119         rdmsr(msr->reg, eax, edx);
120         /* we just let them read the misc msr for now. */
121         if (opcode == EXIT_REASON_MSR_READ) {
122                 vm_tf->tf_rax = set_low32(vm_tf->tf_rax, eax);
123                 vm_tf->tf_rdx = set_low32(vm_tf->tf_rdx, edx);
124                 return 0;
125         } else {
126                 /* if they are writing what is already written, that's ok. */
127                 if (((uint32_t) vm_tf->tf_rax == eax)
128                     && ((uint32_t) vm_tf->tf_rdx == edx))
129                         return 0;
130         }
131         fprintf(stderr,
132                 "%s: Wanted to write 0x%x:0x%x, but could not; value was 0x%x:0x%x\n",
133                  msr->name, (uint32_t) vm_tf->tf_rdx,
134                  (uint32_t) vm_tf->tf_rax, edx, eax);
135         return SHUTDOWN_UNHANDLED_EXIT_REASON;
136 }
137
138 static int emsr_ok(struct guest_thread *vm_thread, struct emmsr *msr,
139                    uint32_t opcode)
140 {
141         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
142
143         if (opcode == EXIT_REASON_MSR_READ) {
144                 rdmsr(msr->reg, vm_tf->tf_rdx, vm_tf->tf_rax);
145         } else {
146                 uint64_t val =
147                         (uint64_t) vm_tf->tf_rdx << 32 | vm_tf->tf_rax;
148                 write_msr(msr->reg, val);
149         }
150         return 0;
151 }
152
153 static int emsr_readonly(struct guest_thread *vm_thread, struct emmsr *msr,
154                          uint32_t opcode)
155 {
156         uint32_t eax, edx;
157         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
158
159         rdmsr((uint32_t) vm_tf->tf_rcx, eax, edx);
160         /* we just let them read the misc msr for now. */
161         if (opcode == EXIT_REASON_MSR_READ) {
162                 vm_tf->tf_rax = set_low32(vm_tf->tf_rax, eax);
163                 vm_tf->tf_rdx = set_low32(vm_tf->tf_rdx, edx);
164                 return 0;
165         }
166
167         fprintf(stderr,"%s: Tried to write a readonly register\n", msr->name);
168         return SHUTDOWN_UNHANDLED_EXIT_REASON;
169 }
170
171 static int emsr_readzero(struct guest_thread *vm_thread, struct emmsr *msr,
172                          uint32_t opcode)
173 {
174         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
175
176         if (opcode == EXIT_REASON_MSR_READ) {
177                 vm_tf->tf_rax = 0;
178                 vm_tf->tf_rdx = 0;
179                 return 0;
180         }
181
182         fprintf(stderr,"%s: Tried to write a readonly register\n", msr->name);
183         return SHUTDOWN_UNHANDLED_EXIT_REASON;
184 }
185
186 /* pretend to write it, but don't write it. */
187 static int emsr_fakewrite(struct guest_thread *vm_thread, struct emmsr *msr,
188                           uint32_t opcode)
189 {
190         uint32_t eax, edx;
191         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
192
193         if (!msr->written) {
194                 rdmsr(msr->reg, eax, edx);
195         } else {
196                 edx = msr->edx;
197                 eax = msr->eax;
198         }
199         /* we just let them read the misc msr for now. */
200         if (opcode == EXIT_REASON_MSR_READ) {
201                 vm_tf->tf_rax = set_low32(vm_tf->tf_rax, eax);
202                 vm_tf->tf_rdx = set_low32(vm_tf->tf_rdx, edx);
203                 return 0;
204         } else {
205                 /* if they are writing what is already written, that's ok. */
206                 if (((uint32_t) vm_tf->tf_rax == eax)
207                     && ((uint32_t) vm_tf->tf_rdx == edx))
208                         return 0;
209                 msr->edx = vm_tf->tf_rdx;
210                 msr->eax = vm_tf->tf_rax;
211                 msr->written = true;
212         }
213         return 0;
214 }
215
216 static int emsr_apic(struct guest_thread *vm_thread,
217                      struct vmm_gpcore_init *gpci, uint32_t opcode)
218 {
219         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
220         int apic_offset = vm_tf->tf_rcx & 0xff;
221         uint64_t value;
222
223         if (opcode == EXIT_REASON_MSR_READ) {
224                 if (vm_tf->tf_rcx != MSR_LAPIC_ICR) {
225                         vm_tf->tf_rax = ((uint32_t *)(gpci->vapic_addr))[apic_offset];
226                         vm_tf->tf_rdx = 0;
227                 } else {
228                         vm_tf->tf_rax = ((uint32_t *)(gpci->vapic_addr))[apic_offset];
229                         vm_tf->tf_rdx = ((uint32_t *)(gpci->vapic_addr))[apic_offset + 1];
230                 }
231         } else {
232                 if (vm_tf->tf_rcx != MSR_LAPIC_ICR)
233                         ((uint32_t *)(gpci->vapic_addr))[apic_offset] =
234                                                                (uint32_t)(vm_tf->tf_rax);
235                 else {
236                         ((uint32_t *)(gpci->vapic_addr))[apic_offset] =
237                                                                (uint32_t)(vm_tf->tf_rax);
238                         ((uint32_t *)(gpci->vapic_addr))[apic_offset + 1] =
239                                                                (uint32_t)(vm_tf->tf_rdx);
240                 }
241         }
242         return 0;
243 }
244
245 int msrio(struct guest_thread *vm_thread, struct vmm_gpcore_init *gpci,
246           uint32_t opcode)
247 {
248         int i;
249         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
250
251         if (vm_tf->tf_rcx >= MSR_LAPIC_ID && vm_tf->tf_rcx < MSR_LAPIC_END)
252                 return emsr_apic(vm_thread, gpci, opcode);
253
254         for (i = 0; i < sizeof(emmsrs)/sizeof(emmsrs[0]); i++) {
255                 if (emmsrs[i].reg != vm_tf->tf_rcx)
256                         continue;
257                 return emmsrs[i].f(vm_thread, &emmsrs[i], opcode);
258         }
259         fprintf(stderr, "msrio for 0x%lx failed\n", vm_tf->tf_rcx);
260         return SHUTDOWN_UNHANDLED_EXIT_REASON;
261 }
262