]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/xen/xensystem.c
ether8169: support rtl8402 variant
[plan9front.git] / sys / src / 9 / xen / xensystem.c
1 /*
2  * xensystem.c
3  *
4  * TODO: we could handle mmu updates more efficiently by
5  * using a multicall.
6  * XXX perhaps we should check return values and panic on failure?
7  */
8 #include        "u.h"
9 #include        "../port/lib.h"
10 #include        "mem.h"
11 #include        "dat.h"
12 #include        "fns.h"
13 #include        "io.h"
14 #include        "ureg.h"
15
16 #define LOG(a)
17
18 /*
19  * These functions replace all the inlines that are used on Linux systems
20  */
21
22 /* in xen.s */
23 int xencall1(int op);
24 int xencall2(int op, ulong arg1);
25 int xencall3(int op, ulong arg1, ulong arg2);
26 int xencall4(int op, ulong arg1, ulong arg2, ulong arg3);
27 int xencall5(int op, ulong arg1, ulong arg2, ulong arg3, ulong arg4);
28 int xencall6(int op, ulong arg1, ulong arg2, ulong arg3, ulong arg4, ulong arg5);
29
30 int
31 HYPERVISOR_update_va_mapping(ulong va, uvlong newval, ulong flags)
32 {
33         int ret;
34
35         ret = xencall5(__HYPERVISOR_update_va_mapping, va, newval, newval>>32, flags);
36         if(ret < 0)
37                 panic("update_va_mapping failed");
38         return ret;
39 }
40
41 long
42 HYPERVISOR_set_timer_op(uvlong timeout)
43 {
44         ulong hi, lo;
45
46         hi = timeout>>32;
47         lo = timeout;
48         return xencall3(__HYPERVISOR_set_timer_op, lo, hi);
49 }
50
51 int 
52 HYPERVISOR_set_trap_table(trap_info_t *table)
53 {
54         return xencall2(__HYPERVISOR_set_trap_table, (ulong)table);
55 }
56
57 int
58 HYPERVISOR_mmu_update(mmu_update_t *req, int count,
59         int *success_count, domid_t domid)
60 {
61         return xencall5(__HYPERVISOR_mmu_update, (ulong)req, count, (ulong)success_count, domid);
62 }
63
64 int
65 HYPERVISOR_mmuext_op(struct mmuext_op *op, int count, int *scount, domid_t domid)
66 {
67         return xencall5(__HYPERVISOR_mmuext_op, (ulong)op, count, (ulong)scount, domid);
68 }
69
70 int 
71 HYPERVISOR_set_gdt(unsigned long *frame_list, int entries)
72 {
73         return xencall3(__HYPERVISOR_set_gdt, (ulong)frame_list, entries);
74 }
75
76 int
77 HYPERVISOR_stack_switch(ulong ss, ulong esp)
78 {
79         return xencall3(__HYPERVISOR_stack_switch, ss, esp);
80 }
81
82 /* XXX match evfunc and fsfunc prototypes? */
83 int
84 HYPERVISOR_set_callbacks(ulong evss, ulong evfunc, ulong fsss, ulong fsfunc)
85 {
86         return xencall5(__HYPERVISOR_set_callbacks, evss, evfunc, fsss, fsfunc);
87 }
88
89 int
90 HYPERVISOR_fpu_taskswitch(void)
91 {
92         return xencall1(__HYPERVISOR_fpu_taskswitch);
93 }
94
95 int
96 HYPERVISOR_yield(void)
97 {
98         return xencall3(__HYPERVISOR_sched_op, SCHEDOP_yield, 0);
99 }
100
101 int
102 HYPERVISOR_block(void)
103 {
104         return xencall3(__HYPERVISOR_sched_op, SCHEDOP_block, 0);
105 }
106
107 int 
108 HYPERVISOR_shutdown(int reboot)
109 {
110         sched_shutdown_t arg;
111
112         arg.reason = reboot? SHUTDOWN_reboot : SHUTDOWN_poweroff;
113         return xencall3(__HYPERVISOR_sched_op, SCHEDOP_shutdown, (ulong)&arg);
114 }
115
116 int
117 HYPERVISOR_multicall(void *call_list, int nr_calls)
118 {
119         return xencall3(__HYPERVISOR_multicall, (ulong)call_list, nr_calls);
120 }
121
122
123 int 
124 HYPERVISOR_event_channel_op(void *op)
125 {
126         return xencall2(__HYPERVISOR_event_channel_op, (ulong)op);
127 }
128
129 int
130 HYPERVISOR_xen_version(int cmd, void *arg)
131 {
132         return xencall3(__HYPERVISOR_xen_version, cmd, (ulong)arg);
133 }
134
135 int
136 HYPERVISOR_console_io(int cmd, int count, char *str)
137 {
138         return xencall4(__HYPERVISOR_console_io, cmd, count, (ulong)str);
139 }
140
141 int
142 HYPERVISOR_grant_table_op(int cmd, gnttab_setup_table_t *setup, int count)
143 {
144         return xencall4(__HYPERVISOR_grant_table_op, cmd, (ulong)setup, count);
145 }
146
147 int
148 HYPERVISOR_memory_op(int cmd, struct xen_memory_reservation *arg)
149 {
150         return xencall3(__HYPERVISOR_memory_op, cmd, (ulong)arg);
151 }
152
153 /* 
154  * XXX this comment is leftover from old code.  revisit and update.
155  *
156  * The use of 'barrier' in the following reflects their use as local-lock
157  * operations. Reentrancy must be prevented (e.g., __cli()) /before/ following
158  * critical operations are executed. All critical operatiosn must complete
159  * /before/ reentrancy is permitted (e.g., __sti()). Alpha architecture also
160  * includes these barriers, for example.
161  */
162
163 /*
164  * conversions to machine page numbers, pages and addresses
165  */
166 #define MFN(pa)         (patomfn[(pa)>>PGSHIFT])
167 #define MFNPG(pa)               ((uvlong)MFN(pa)<<PGSHIFT)
168 #define PA2MA(pa)               (MFNPG(pa) | PGOFF(pa))
169 #define VA2MA(va)               PA2MA(PADDR(va))
170 #define VA2MFN(va)              MFN(PADDR(va))
171
172 ulong hypervisor_virt_start;
173 ulong xentop;
174 start_info_t *xenstart;
175 shared_info_t *HYPERVISOR_shared_info;
176 ulong *patomfn;
177 ulong *matopfn;
178
179 int
180 xenpdptpin(ulong va)
181 {
182         struct mmuext_op op;
183         ulong mfn;
184
185         mfn = MFN(PADDR(va));
186         LOG(dprint("pdptpin %lux %lux\n", va, mfn);)
187         print("pdptpin %lux %lux\n", va, mfn);
188         /* mark page readonly first */
189         HYPERVISOR_update_va_mapping(va, ((uvlong)mfn<<PGSHIFT)|PTEVALID, UVMF_INVLPG|UVMF_LOCAL);
190
191         /*  L3 here refers to page directory pointer table (PAE mode) */
192         op.cmd = MMUEXT_PIN_L3_TABLE;
193         op.arg1.mfn = mfn;
194         if (HYPERVISOR_mmuext_op(&op, 1, 0, DOMID_SELF) == 0)
195                 return 1;
196         HYPERVISOR_update_va_mapping(va, ((uvlong)mfn<<PGSHIFT)|PTEVALID|PTEWRITE, UVMF_INVLPG|UVMF_LOCAL);
197         return 0;
198 }
199
200 int
201 xenpgdpin(ulong va)
202 {
203         struct mmuext_op op;
204         ulong mfn;
205
206         mfn = MFN(PADDR(va));
207         LOG(dprint("pdpin %lux %lux\n", va, mfn);)
208         /* mark page readonly first */
209         HYPERVISOR_update_va_mapping(va, ((uvlong)mfn<<PGSHIFT)|PTEVALID, UVMF_INVLPG|UVMF_LOCAL);
210
211         /* to confuse you, L2 here refers to page directories */
212         op.cmd = MMUEXT_PIN_L2_TABLE;
213         op.arg1.mfn = mfn;
214         if (HYPERVISOR_mmuext_op(&op, 1, 0, DOMID_SELF) == 0)
215                 return 1;
216         HYPERVISOR_update_va_mapping(va, ((uvlong)mfn<<PGSHIFT)|PTEVALID|PTEWRITE, UVMF_INVLPG|UVMF_LOCAL);
217         return 0;
218 }
219
220 int
221 xenptpin(ulong va)
222 {
223         struct mmuext_op op;
224         ulong mfn;
225
226         mfn = MFN(PADDR(va));
227         LOG(dprint("pin %lux %lux\n", va, mfn);)
228         /* mark page readonly first */
229         HYPERVISOR_update_va_mapping(va, ((uvlong)mfn<<PGSHIFT)|PTEVALID, UVMF_INVLPG|UVMF_LOCAL);
230
231         /* to confuse you, L1 here refers to page tables */
232         op.cmd = MMUEXT_PIN_L1_TABLE;
233         op.arg1.mfn = mfn;
234         if (HYPERVISOR_mmuext_op(&op, 1, 0, DOMID_SELF) == 0)
235                 return 1;
236         HYPERVISOR_update_va_mapping(va, ((uvlong)mfn<<PGSHIFT)|PTEVALID|PTEWRITE, UVMF_INVLPG|UVMF_LOCAL);
237         return 0;
238 }
239
240 void
241 xenptunpin(ulong va)
242 {
243         struct mmuext_op op;
244         ulong mfn;
245
246         mfn = MFN(PADDR(va));
247         LOG(dprint("unpin %lux %lux\n", va, mfn);)
248         op.cmd = MMUEXT_UNPIN_TABLE;
249         op.arg1.mfn = mfn;
250         if(HYPERVISOR_mmuext_op(&op, 1, 0, DOMID_SELF)<0)
251                 panic("xenptunpin va=%lux called from %lux", va, getcallerpc(&va));
252
253         /* mark page read-write */
254         HYPERVISOR_update_va_mapping(va, ((uvlong)mfn<<PGSHIFT)|PTEVALID|PTEWRITE, UVMF_INVLPG|UVMF_LOCAL);
255 }
256
257 void
258 xenptswitch(ulong pa)
259 {
260         struct mmuext_op op;
261
262         op.cmd = MMUEXT_NEW_BASEPTR;
263         op.arg1.mfn = MFN(pa);
264         if(HYPERVISOR_mmuext_op(&op, 1, 0, DOMID_SELF)<0)
265                 panic("xenptswitch");
266 }
267
268 void
269 xentlbflush(void)
270 {
271         struct mmuext_op op;
272
273         op.cmd = MMUEXT_TLB_FLUSH_LOCAL;
274         HYPERVISOR_mmuext_op(&op, 1, 0, DOMID_SELF);
275 }
276
277 /* update a pte using a machine page frame number */
278 void 
279 xenupdatema(ulong *ptr, uvlong val)
280 {
281         mmu_update_t u;
282
283         u.ptr = VA2MA(ptr);
284         u.val = val;
285         if(HYPERVISOR_mmu_update(&u, 1, 0, DOMID_SELF) < 0)
286                 panic("xenupdatema - pte %lux value %llux (was %llux) called from %lux", (ulong)ptr, val, *(uvlong*)ptr, getcallerpc(&ptr));
287 }
288
289 /* update a pte using a guest "physical" page number */
290 void 
291 xenupdate(ulong *ptr, ulong val)
292 {
293         mmu_update_t u;
294
295         u.ptr = VA2MA(ptr);
296         u.val = PA2MA(val);
297         if(HYPERVISOR_mmu_update(&u, 1, 0, DOMID_SELF) < 0)
298                 panic("xenupdate - pte %lux value %lux (%llux) called from %lux", (ulong)ptr, val, PA2MA(val), getcallerpc(&ptr));
299 }
300
301 void
302 acceptframe(int ref, void *va)
303 {
304         ulong mfn;
305
306         mfn = xengrantend(ref);
307         if (mfn == 0)
308                 panic("can't accept page frame");
309         LOG(dprint("acceptframe ref %d va %lux mfn %lux\n", ref, (ulong)va, mfn);)
310         VA2MFN(va) = mfn;
311         mmumapframe((ulong)va, mfn);
312 }
313
314 int
315 donateframe(int domid, void *va)
316 {
317         ulong mfn;
318         int ref;
319         ulong *pte;
320         struct xen_memory_reservation mem;
321
322         mfn = VA2MFN(va);
323         ref = xengrant(domid, mfn, GTF_accept_transfer);
324         LOG(dprint("grant transfer %lux (%lux) -> %d\n", (ulong)va, mfn, ref);)
325         pte = mmuwalk(m->pdb, (ulong)va, 2, 0);
326         xenupdatema(pte, 0);
327         set_xen_guest_handle(mem.extent_start, &mfn);
328         mem.nr_extents = 1;
329         mem.extent_order = 0;
330         mem.address_bits = 0;
331         mem.domid = DOMID_SELF;
332         if (HYPERVISOR_memory_op(XENMEM_decrease_reservation, &mem) != 1)
333                 panic("XENMEM_decrease_reservation");
334         VA2MFN(va) = ~0;
335         return ref;
336 }
337
338 int
339 shareframe(int domid, void *va, int write)
340 {
341         ulong mfn;
342         int ref;
343         int flags;
344
345         mfn = VA2MFN(va);
346         flags = GTF_permit_access;
347         if (!write)
348                 flags |= GTF_readonly;
349         ref = xengrant(domid, mfn, flags);
350         LOG(dprint("grant shared %lux (%lux) -> %d\n", (ulong)va, mfn, ref);)
351         return ref;
352 }
353
354 /*
355  * Upcall from hypervisor, entered with evtchn_upcall_pending masked.
356  */
357 void
358 xenupcall(Ureg *ureg)
359 {
360         vcpu_info_t *vcpu;
361         shared_info_t *s;
362         ulong sel1, sel2, n1, n2, port;
363
364         ureg->ecode = 0;
365         s = HYPERVISOR_shared_info;
366         vcpu = &HYPERVISOR_shared_info->vcpu_info[0];
367         for (;;) {
368                 vcpu->evtchn_upcall_pending = 0;
369                 sel1 = xchgl((uint*)&vcpu->evtchn_pending_sel, 0);
370                 while(sel1) {
371                         n1 = ffs(sel1);
372                         sel1 &= ~(1<<n1);
373                         sel2 = xchgl((uint*)&s->evtchn_pending[n1], 0);
374                         while(sel2) {
375                                 n2 = ffs(sel2);
376                                 sel2 &= ~(1<<n2);
377                                 port = (n1<<5) + n2;
378                                 ureg->trap = 100+port;
379                                 trap(ureg);
380                         }
381                 }
382                 if (vcpu->evtchn_upcall_pending)
383                         continue;
384                 vcpu->evtchn_upcall_mask = 0;
385                 if (vcpu->evtchn_upcall_pending == 0)
386                         break;
387                 vcpu->evtchn_upcall_mask = 1;
388         }
389         
390 }
391
392 static int
393 xenirqenable(Vctl *v, int shared)
394 {
395         if(!shared){
396                 uint port = v->vno-100;
397                 HYPERVISOR_shared_info->evtchn_mask[port/32] &= ~(1<<(port%32));
398         }
399         return 0;
400 }
401
402 static int
403 xenirqdisable(Vctl *v, int shared)
404 {
405         if(!shared){
406                 uint port = v->vno-100;
407                 HYPERVISOR_shared_info->evtchn_mask[port/32] |= (1<<(port%32));
408         }
409         return 0;
410 }
411
412 /*
413  * tbdf field is abused to distinguish virqs from channels:
414  *
415  * tbdf=BUSUNKNOWN -> irq is a virq to be bound to a channel
416  * tbdf=0 -> irq is a channel number
417  */
418 int
419 xenintrassign(Vctl *v)
420 {
421         evtchn_op_t op;
422         uint port;
423
424         if (v->tbdf != BUSUNKNOWN) {
425                 op.cmd = EVTCHNOP_bind_virq;
426                 op.u.bind_virq.virq = v->irq;
427                 op.u.bind_virq.vcpu = m->machno;
428                 if(HYPERVISOR_event_channel_op(&op) != 0){
429                         print("xenintrenable: bind %d failed", v->irq);
430                         return -1;
431                 }
432                 port = op.u.bind_virq.port;
433         } else
434                 port = v->irq;
435         if (port > 155)
436                 return -1;
437         v->enable = xenirqenable;
438         v->disable = xenirqdisable;
439         return 100+port;
440 }
441
442 int
443 xenintrvecno(int irq)
444 {
445         return irq;
446 }
447
448 int
449 islo(void)
450 {
451         vcpu_info_t *cpu;
452
453         cpu = &HYPERVISOR_shared_info->vcpu_info[m->machno];    // XXX m->shared
454         return (cpu->evtchn_upcall_mask == 0);
455 }
456
457 /*
458  * Note: Portable code expects spllo <= spl* <= spldone for
459  * accounting purposes.  Lets hope the compiler doesn't reorder
460  * us.
461  */
462 int 
463 spllo(void)
464 {
465         vcpu_info_t *cpu = &HYPERVISOR_shared_info->vcpu_info[m->machno];       // XXX m->shared
466
467         if(cpu->evtchn_upcall_mask == 0)
468                 return 0;
469         m->splpc = 0;
470         cpu->evtchn_upcall_mask = 0;
471
472         /*
473          * If an event arrived while masked off,
474          * use a dummy call to trigger delivery
475          */
476         if (cpu->evtchn_upcall_pending)
477                 HYPERVISOR_xen_version(0, 0);
478
479         return 1;
480 }
481
482 int 
483 splhi(void)
484 {
485         ulong dummy;
486         vcpu_info_t *cpu = &HYPERVISOR_shared_info->vcpu_info[m->machno];       // XXX m->shared
487         int oldmask;
488
489         oldmask = xchgb(&cpu->evtchn_upcall_mask, 1);
490         if (cpu->evtchn_upcall_mask != 1)
491                 panic("xchgb");
492         /* XXX ad-hoc ¨getcallerpc" because we have no arguments */
493         m->splpc = (&dummy)[1];
494         return oldmask;
495 }
496
497 void
498 splx(int x)
499 {
500         if(x)
501                 splhi();
502         else
503                 spllo();
504 }
505
506 /* marker for profiling in portable code */
507 void
508 spldone(void)
509 {
510 }
511
512 /* allocate an event channel */
513 int
514 xenchanalloc(int dom)
515 {
516         evtchn_op_t op;
517
518         op.cmd = EVTCHNOP_alloc_unbound;
519         op.u.alloc_unbound.dom = DOMID_SELF;
520         op.u.alloc_unbound.remote_dom = dom;
521         if (HYPERVISOR_event_channel_op(&op) != 0)
522                 panic("xenchanalloc");
523         return op.u.alloc_unbound.port;
524 }
525
526 /* notify over an event channel */
527 void
528 xenchannotify(int port)
529 {
530         evtchn_op_t op;
531
532         op.cmd = EVTCHNOP_send;
533         op.u.send.port = port;
534         HYPERVISOR_event_channel_op(&op);
535 }
536
537 void
538 halt(void)
539 {
540         extern int nrdy;
541
542         splhi();
543         if (nrdy) {
544                 spllo();
545                 return;
546         }
547         HYPERVISOR_block();
548 }
549
550 void
551 mb(void)
552 {
553         coherence();
554 }