vmm: Squelch noisy prints
[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 <vmm/vmm.h>
32 #include <ros/arch/trapframe.h>
33
34 struct emmsr {
35         uint32_t reg;
36         char *name;
37         int (*f)(struct guest_thread *vm_thread, struct emmsr *, uint32_t);
38         bool written;
39         uint32_t edx, eax;
40 };
41 // Might need to mfence rdmsr.  supposedly wrmsr serializes, but not for x2APIC
42 static inline uint64_t read_msr(uint32_t reg)
43 {
44         uint32_t edx, eax;
45         asm volatile("rdmsr; mfence" : "=d"(edx), "=a"(eax) : "c"(reg));
46         return (uint64_t)edx << 32 | eax;
47 }
48
49 static inline void write_msr(uint32_t reg, uint64_t val)
50 {
51         asm volatile("wrmsr" : : "d"((uint32_t)(val >> 32)),
52                                  "a"((uint32_t)(val & 0xFFFFFFFF)),
53                                  "c"(reg));
54 }
55
56 static int emsr_miscenable(struct guest_thread *vm_thread, struct emmsr *,
57                            uint32_t);
58 static int emsr_mustmatch(struct guest_thread *vm_thread, struct emmsr *,
59                           uint32_t);
60 static int emsr_readonly(struct guest_thread *vm_thread, struct emmsr *,
61                          uint32_t);
62 static int emsr_readzero(struct guest_thread *vm_thread, struct emmsr *,
63                          uint32_t);
64 static int emsr_fakewrite(struct guest_thread *vm_thread, struct emmsr *,
65                           uint32_t);
66 static int emsr_ok(struct guest_thread *vm_thread, struct emmsr *, uint32_t);
67
68 struct emmsr emmsrs[] = {
69         {MSR_RAPL_POWER_UNIT, "MSR_RAPL_POWER_UNIT", emsr_readzero},
70 };
71
72 static uint64_t set_low32(uint64_t hi, uint32_t lo)
73 {
74         return (hi & 0xffffffff00000000ULL) | lo;
75 }
76
77 static uint64_t set_low16(uint64_t hi, uint16_t lo)
78 {
79         return (hi & 0xffffffffffff0000ULL) | lo;
80 }
81
82 static uint64_t set_low8(uint64_t hi, uint8_t lo)
83 {
84         return (hi & 0xffffffffffffff00ULL) | lo;
85 }
86
87 /* this may be the only register that needs special handling.
88  * If there others then we might want to extend teh emmsr struct.
89  */
90 static int emsr_miscenable(struct guest_thread *vm_thread, struct emmsr *msr,
91                            uint32_t opcode) {
92         uint32_t eax, edx;
93         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
94
95         rdmsr(msr->reg, eax, edx);
96         /* we just let them read the misc msr for now. */
97         if (opcode == EXIT_REASON_MSR_READ) {
98                 vm_tf->tf_rax = set_low32(vm_tf->tf_rax, eax);
99                 vm_tf->tf_rax |= MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL;
100                 vm_tf->tf_rdx = set_low32(vm_tf->tf_rdx, edx);
101                 return 0;
102         } else {
103                 /* if they are writing what is already written, that's ok. */
104                 if (((uint32_t) vm_tf->tf_rax == eax)
105                     && ((uint32_t) vm_tf->tf_rdx == edx))
106                         return 0;
107         }
108         fprintf(stderr,
109                 "%s: Wanted to write 0x%x:0x%x, but could not; value was 0x%x:0x%x\n",
110                  msr->name, (uint32_t) vm_tf->tf_rdx,
111                  (uint32_t) vm_tf->tf_rax, edx, eax);
112         return SHUTDOWN_UNHANDLED_EXIT_REASON;
113 }
114
115 static int emsr_mustmatch(struct guest_thread *vm_thread, struct emmsr *msr,
116                           uint32_t opcode) {
117         uint32_t eax, edx;
118         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
119
120         rdmsr(msr->reg, eax, edx);
121         /* we just let them read the misc msr for now. */
122         if (opcode == EXIT_REASON_MSR_READ) {
123                 vm_tf->tf_rax = set_low32(vm_tf->tf_rax, eax);
124                 vm_tf->tf_rdx = set_low32(vm_tf->tf_rdx, edx);
125                 return 0;
126         } else {
127                 /* if they are writing what is already written, that's ok. */
128                 if (((uint32_t) vm_tf->tf_rax == eax)
129                     && ((uint32_t) vm_tf->tf_rdx == edx))
130                         return 0;
131         }
132         fprintf(stderr,
133                 "%s: Wanted to write 0x%x:0x%x, but could not; value was 0x%x:0x%x\n",
134                  msr->name, (uint32_t) vm_tf->tf_rdx,
135                  (uint32_t) vm_tf->tf_rax, edx, eax);
136         return SHUTDOWN_UNHANDLED_EXIT_REASON;
137 }
138
139 static int emsr_ok(struct guest_thread *vm_thread, struct emmsr *msr,
140                    uint32_t opcode)
141 {
142         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
143
144         if (opcode == EXIT_REASON_MSR_READ) {
145                 rdmsr(msr->reg, vm_tf->tf_rdx, vm_tf->tf_rax);
146         } else {
147                 uint64_t val =
148                         (uint64_t) vm_tf->tf_rdx << 32 | vm_tf->tf_rax;
149                 write_msr(msr->reg, val);
150         }
151         return 0;
152 }
153
154 static int emsr_readonly(struct guest_thread *vm_thread, struct emmsr *msr,
155                          uint32_t opcode)
156 {
157         uint32_t eax, edx;
158         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
159
160         rdmsr((uint32_t) vm_tf->tf_rcx, eax, edx);
161         /* we just let them read the misc msr for now. */
162         if (opcode == EXIT_REASON_MSR_READ) {
163                 vm_tf->tf_rax = set_low32(vm_tf->tf_rax, eax);
164                 vm_tf->tf_rdx = set_low32(vm_tf->tf_rdx, edx);
165                 return 0;
166         }
167
168         fprintf(stderr,"%s: Tried to write a readonly register\n", msr->name);
169         return SHUTDOWN_UNHANDLED_EXIT_REASON;
170 }
171
172 static int emsr_readzero(struct guest_thread *vm_thread, struct emmsr *msr,
173                          uint32_t opcode)
174 {
175         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
176
177         if (opcode == EXIT_REASON_MSR_READ) {
178                 vm_tf->tf_rax = 0;
179                 vm_tf->tf_rdx = 0;
180                 return 0;
181         }
182
183         fprintf(stderr,"%s: Tried to write a readonly register\n", msr->name);
184         return SHUTDOWN_UNHANDLED_EXIT_REASON;
185 }
186
187 /* pretend to write it, but don't write it. */
188 static int emsr_fakewrite(struct guest_thread *vm_thread, struct emmsr *msr,
189                           uint32_t opcode)
190 {
191         uint32_t eax, edx;
192         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
193
194         if (!msr->written) {
195                 rdmsr(msr->reg, eax, edx);
196         } else {
197                 edx = msr->edx;
198                 eax = msr->eax;
199         }
200         /* we just let them read the misc msr for now. */
201         if (opcode == EXIT_REASON_MSR_READ) {
202                 vm_tf->tf_rax = set_low32(vm_tf->tf_rax, eax);
203                 vm_tf->tf_rdx = set_low32(vm_tf->tf_rdx, edx);
204                 return 0;
205         } else {
206                 /* if they are writing what is already written, that's ok. */
207                 if (((uint32_t) vm_tf->tf_rax == eax)
208                     && ((uint32_t) vm_tf->tf_rdx == edx))
209                         return 0;
210                 msr->edx = vm_tf->tf_rdx;
211                 msr->eax = vm_tf->tf_rax;
212                 msr->written = true;
213         }
214         return 0;
215 }
216
217 static int emsr_apic(struct guest_thread *vm_thread,
218                      struct vmm_gpcore_init *gpci, uint32_t opcode)
219 {
220         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
221         int apic_offset = vm_tf->tf_rcx & 0xff;
222         uint64_t value;
223
224         if (opcode == EXIT_REASON_MSR_READ) {
225                 if (vm_tf->tf_rcx != MSR_LAPIC_ICR) {
226                         vm_tf->tf_rax = ((uint32_t *)(gpci->vapic_addr))[apic_offset];
227                         vm_tf->tf_rdx = 0;
228                 } else {
229                         vm_tf->tf_rax = ((uint32_t *)(gpci->vapic_addr))[apic_offset];
230                         vm_tf->tf_rdx = ((uint32_t *)(gpci->vapic_addr))[apic_offset + 1];
231                 }
232         } else {
233                 if (vm_tf->tf_rcx != MSR_LAPIC_ICR)
234                         ((uint32_t *)(gpci->vapic_addr))[apic_offset] =
235                                                                (uint32_t)(vm_tf->tf_rax);
236                 else {
237                         /* We currently only handle physical destinations.
238                          * TODO(ganshun): Support logical destinations if needed. */
239                         struct virtual_machine *vm = gth_to_vm(vm_thread);
240                         uint32_t destination = vm_tf->tf_rdx & 0xffffffff;
241                         uint8_t vector = vm_tf->tf_rax & 0xff;
242                         uint8_t type = (vm_tf->tf_rax >> 8) & 0x7;
243
244                         if (destination >= vm->nr_gpcs && destination != 0xffffffff) {
245                                 fprintf(stderr, "UNSUPPORTED DESTINATION 0x%02x!\n",
246                                                 destination);
247                                 return SHUTDOWN_UNHANDLED_EXIT_REASON;
248                         }
249                         switch (type) {
250                                 case 0:
251                                         /* Send IPI */
252                                         if (destination == 0xffffffff) {
253                                                 /* Broadcast */
254                                                 for (int i = 0; i < vm->nr_gpcs; i++)
255                                                         vmm_interrupt_guest(vm, i, vector);
256                                         } else {
257                                                 /* Send individual IPI */
258                                                 vmm_interrupt_guest(vm, destination, vector);
259                                         }
260                                         break;
261                                 default:
262                                         /* This is not a terrible error, we don't currently support
263                                          * SIPIs and INIT IPIs. The guest is allowed to try to make
264                                          * them for now even though we don't do anything. */
265                                         fprintf(stderr, "Unsupported IPI type %d!\n", type);
266                                         break;
267                         }
268
269                         ((uint32_t *)(gpci->vapic_addr))[apic_offset] =
270                                                                (uint32_t)(vm_tf->tf_rax);
271                         ((uint32_t *)(gpci->vapic_addr))[apic_offset + 1] =
272                                                                (uint32_t)(vm_tf->tf_rdx);
273                 }
274         }
275         return 0;
276 }
277
278 int msrio(struct guest_thread *vm_thread, struct vmm_gpcore_init *gpci,
279           uint32_t opcode)
280 {
281         int i;
282         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
283
284         if (vm_tf->tf_rcx >= MSR_LAPIC_ID && vm_tf->tf_rcx < MSR_LAPIC_END)
285                 return emsr_apic(vm_thread, gpci, opcode);
286
287         for (i = 0; i < sizeof(emmsrs)/sizeof(emmsrs[0]); i++) {
288                 if (emmsrs[i].reg != vm_tf->tf_rcx)
289                         continue;
290                 return emmsrs[i].f(vm_thread, &emmsrs[i], opcode);
291         }
292         printd("msrio for 0x%lx failed\n", vm_tf->tf_rcx);
293         return SHUTDOWN_UNHANDLED_EXIT_REASON;
294 }
295