tests/linux: use Akaros's CFLAGS
[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 <parlib/stdio.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <parlib/arch/arch.h>
14 #include <parlib/ros_debug.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/uio.h>
20 #include <stdint.h>
21 #include <err.h>
22 #include <sys/mman.h>
23 #include <ros/vmm.h>
24 #include <ros/arch/msr-index.h>
25 #include <vmm/virtio.h>
26 #include <vmm/virtio_mmio.h>
27 #include <vmm/virtio_ids.h>
28 #include <vmm/virtio_config.h>
29 #include <vmm/sched.h>
30 #include <vmm/vmm.h>
31 #include <ros/arch/trapframe.h>
32 #include <parlib/alarm.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
42 static inline uint64_t read_msr(uint32_t reg)
43 {
44         panic("Not implemented for userspace");
45 }
46
47 static inline void write_msr(uint32_t reg, uint64_t val)
48 {
49         panic("Not implemented for userspace");
50 }
51
52 static int emsr_readonly(struct guest_thread *vm_thread, struct emmsr *,
53                          uint32_t);
54 static int emsr_readzero(struct guest_thread *vm_thread, struct emmsr *,
55                          uint32_t);
56 static int emsr_fakewrite(struct guest_thread *vm_thread, struct emmsr *,
57                           uint32_t);
58 static int emsr_ok(struct guest_thread *vm_thread, struct emmsr *, uint32_t);
59
60 struct emmsr emmsrs[] = {
61         {MSR_RAPL_POWER_UNIT, "MSR_RAPL_POWER_UNIT", emsr_readzero},
62 };
63
64 static inline uint32_t low32(uint64_t val)
65 {
66         return val & 0xffffffff;
67 }
68
69 static inline uint32_t high32(uint64_t val)
70 {
71         return val >> 32;
72 }
73
74 static int emsr_ok(struct guest_thread *vm_thread, struct emmsr *msr,
75                    uint32_t opcode)
76 {
77         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
78         uint64_t msr_val;
79
80         if (opcode == EXIT_REASON_MSR_READ) {
81                 msr_val = read_msr(msr->reg);
82                 vm_tf->tf_rax = low32(msr_val);
83                 vm_tf->tf_rdx = high32(msr_val);
84         } else {
85                 msr_val = (vm_tf->tf_rdx << 32) | vm_tf->tf_rax;
86                 write_msr(msr->reg, msr_val);
87         }
88         return 0;
89 }
90
91 static int emsr_readonly(struct guest_thread *vm_thread, struct emmsr *msr,
92                          uint32_t opcode)
93 {
94         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
95         uint64_t msr_val;
96
97         msr_val = read_msr(msr->reg);
98         if (opcode == EXIT_REASON_MSR_READ) {
99                 vm_tf->tf_rax = low32(msr_val);
100                 vm_tf->tf_rdx = high32(msr_val);
101                 return 0;
102         }
103
104         fprintf(stderr,"%s: Tried to write a readonly register\n", msr->name);
105         return SHUTDOWN_UNHANDLED_EXIT_REASON;
106 }
107
108 static int emsr_readzero(struct guest_thread *vm_thread, struct emmsr *msr,
109                          uint32_t opcode)
110 {
111         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
112
113         if (opcode == EXIT_REASON_MSR_READ) {
114                 vm_tf->tf_rax = 0;
115                 vm_tf->tf_rdx = 0;
116                 return 0;
117         }
118
119         fprintf(stderr,"%s: Tried to write a readonly register\n", msr->name);
120         return SHUTDOWN_UNHANDLED_EXIT_REASON;
121 }
122
123 /* pretend to write it, but don't write it. */
124 static int emsr_fakewrite(struct guest_thread *vm_thread, struct emmsr *msr,
125                           uint32_t opcode)
126 {
127         uint32_t eax, edx;
128         uint64_t msr_val;
129         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
130
131         if (opcode == EXIT_REASON_MSR_READ) {
132                 if (!msr->written) {
133                         msr_val = read_msr(msr->reg);
134                         eax = low32(msr_val);
135                         edx = high32(msr_val);
136                 } else {
137                         eax = msr->eax;
138                         edx = msr->edx;
139                 }
140                 vm_tf->tf_rax = eax;
141                 vm_tf->tf_rdx = edx;
142         } else {
143                 msr->edx = vm_tf->tf_rdx;
144                 msr->eax = vm_tf->tf_rax;
145                 msr->written = true;
146         }
147         return 0;
148 }
149
150 static int apic_icr_write(struct guest_thread *vm_thread,
151                           struct vmm_gpcore_init *gpci)
152 {
153         /* We currently only handle physical destinations.
154          * TODO(ganshun): Support logical destinations if needed. */
155         struct virtual_machine *vm = gth_to_vm(vm_thread);
156         struct vm_trapframe *vm_tf = gth_to_vmtf(vm_thread);
157         uint32_t destination = vm_tf->tf_rdx & 0xffffffff;
158         uint8_t vector = vm_tf->tf_rax & 0xff;
159         uint8_t type = (vm_tf->tf_rax >> 8) & 0x7;
160         int apic_offset = vm_tf->tf_rcx & 0xff;
161
162         if (destination >= vm->nr_gpcs && destination != 0xffffffff) {
163                 fprintf(stderr, "UNSUPPORTED DESTINATION 0x%02x!\n",
164                                 destination);
165                 return SHUTDOWN_UNHANDLED_EXIT_REASON;
166         }
167         switch (type) {
168         case 0:
169                 /* Send IPI */
170                 if (destination == 0xffffffff) {
171                         /* Broadcast */
172                         for (int i = 0; i < vm->nr_gpcs; i++)
173                                 vmm_interrupt_guest(vm, i, vector);
174                 } else {
175                         /* Send individual IPI */
176                         vmm_interrupt_guest(vm, destination, vector);
177                 }
178                 break;
179         case 0x5:       /* INIT */
180         case 0x6:       /* SIPI */
181                 /* We don't use INIT/SIPI for SMP boot.  The guest is still
182                  * allowed to try to make them for now. */
183                 break;
184         default:
185                 fprintf(stderr, "Unsupported IPI type %d!\n", type);
186                 break;
187         }
188
189         ((uint32_t *)(gpci->vapic_addr))[apic_offset] =
190                                                (uint32_t)(vm_tf->tf_rax);
191         ((uint32_t *)(gpci->vapic_addr))[apic_offset + 1] =
192                                                (uint32_t)(vm_tf->tf_rdx);
193         return 0;
194 }
195
196 static int apic_timer_write(struct guest_thread *gth,
197                             struct vmm_gpcore_init *gpci)
198 {
199         uint32_t multiplier;
200         uint8_t vector;
201         uint32_t initial_count;
202         uint32_t divide_config_reg;
203         struct alarm_waiter *timer_waiter;
204         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
205         int apic_offset = vm_tf->tf_rcx & 0xff;
206
207         ((uint32_t *)(gpci->vapic_addr))[apic_offset] =
208                                        (uint32_t)(vm_tf->tf_rax);
209
210         /* See if we can set the timer. */
211         vector = ((uint32_t *)gpci->vapic_addr)[0x32] & 0xff;
212         initial_count = ((uint32_t *)gpci->vapic_addr)[0x38];
213         divide_config_reg = ((uint32_t *)gpci->vapic_addr)[0x3E];
214         timer_waiter = (struct alarm_waiter*)gth->user_data;
215
216         /* This is a precaution on my part, in case the guest tries to look at
217          * the current count on the lapic. I wanted it to be something other
218          * than 0 just in case. The current count will never be right short of
219          * us properly emulating it. */
220         ((uint32_t *)(gpci->vapic_addr))[0x39] = initial_count;
221
222         if (!timer_waiter)
223                 panic("NO WAITER");
224
225         /* Look at the intel manual Vol 3 10.5.4 APIC Timer */
226         multiplier = (((divide_config_reg & 0x08) >> 1) |
227                       (divide_config_reg & 0x03)) + 1;
228         multiplier &= 0x07;
229
230         unset_alarm(timer_waiter);
231
232         if (vector && initial_count) {
233                 set_awaiter_rel(timer_waiter, initial_count << multiplier);
234                 set_alarm(timer_waiter);
235         }
236         return 0;
237 }
238
239 static int emsr_apic(struct guest_thread *vm_thread,
240                      struct vmm_gpcore_init *gpci, uint32_t opcode)
241 {
242         struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
243         int apic_offset = vm_tf->tf_rcx & 0xff;
244         uint64_t value;
245         int error;
246
247         if (opcode == EXIT_REASON_MSR_READ) {
248                 if (vm_tf->tf_rcx != MSR_LAPIC_ICR) {
249                         vm_tf->tf_rax =
250                                 ((uint32_t*)(gpci->vapic_addr))[apic_offset];
251                         vm_tf->tf_rdx = 0;
252                 } else {
253                         vm_tf->tf_rax =
254                                 ((uint32_t*)(gpci->vapic_addr))[apic_offset];
255                         vm_tf->tf_rdx =
256                                 ((uint32_t*)(gpci->vapic_addr))[apic_offset +1];
257                 }
258         } else {
259                 switch (vm_tf->tf_rcx) {
260                 case MSR_LAPIC_ICR:
261                         error = apic_icr_write(vm_thread, gpci);
262                         if (error != 0)
263                                 return error;
264                         break;
265                 case MSR_LAPIC_DIVIDE_CONFIG_REG:
266                 case MSR_LAPIC_LVT_TIMER:
267                 case MSR_LAPIC_INITIAL_COUNT:
268                         apic_timer_write(vm_thread, gpci);
269                         break;
270                 default:
271                         ((uint32_t *)(gpci->vapic_addr))[apic_offset] =
272                                 (uint32_t)(vm_tf->tf_rax);
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