Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


be5605756625c1b12450eb19e62a82cb5a82b8a4
[palacios.git] / palacios / src / devices / icc_bus.c
1 /*
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National
4  * Science Foundation and the Department of Energy.
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm_dev_mgr.h>
21 #include <palacios/vmm_sprintf.h>
22 #include <palacios/vm_guest.h>
23 #include <devices/icc_bus.h>
24 #include <devices/apic_regs.h>
25
26
27 #define MAX_APICS 256
28
29 #ifndef CONFIG_DEBUG_ICC_BUS
30 #undef PrintDebug
31 #define PrintDebug(fmt, args...)
32 #endif
33
34
35 void v3_force_exit(void *p) {
36 #ifdef CONFIG_DEBUG_ICC_BUS
37     struct guest_info *core=(struct guest_info *)p;
38 #endif
39     PrintDebug("core %u: Forced to exit!\n",core->cpu_id);
40 }
41
42 struct ipi_thunk_data {
43     struct vm_device * target;
44     uint64_t val;
45 };
46
47
48
49 struct apic_data {
50     struct guest_info * core;
51     struct v3_icc_ops * ops;
52     
53     void * priv_data;
54     int present;
55 };
56
57
58 struct icc_bus_state {
59     struct apic_data apics[MAX_APICS];
60     
61     uint32_t         ioapic_id;
62 };
63
64 static struct v3_device_ops dev_ops = {
65     .free = NULL,
66     .reset = NULL,
67     .start = NULL,
68     .stop = NULL,
69 };
70
71 #ifdef CONFIG_DEBUG_ICC_BUS
72 static char *shorthand_str[] = { 
73     "(no shorthand)",
74     "(self)",
75     "(all)",
76     "(all-but-me)",
77  };
78
79 static char *deliverymode_str[] = { 
80     "(fixed)",
81     "(lowest priority)",
82     "(SMI)",
83     "(reserved)",
84     "(NMI)",
85     "(INIT)",
86     "(Start Up)",
87     "(ExtInt)",
88 };
89 #endif
90
91
92 static int deliver(uint32_t src_apic, struct apic_data *dest_apic, struct int_cmd_reg *icr, struct icc_bus_state * state, uint32_t extirq) {
93
94     switch (icr->del_mode) {                                            
95
96         case 0:  //fixed
97         case 1: // lowest priority
98         case 7: // ExtInt
99             PrintDebug("icc_bus: delivering IRQ to core %u\n",dest_apic->core->cpu_id); 
100             dest_apic->ops->raise_intr(dest_apic->core, 
101                                        icr->del_mode!=7 ? icr->vec : extirq,
102                                        dest_apic->priv_data); 
103             if (src_apic!=state->ioapic_id && dest_apic->core->cpu_id != src_apic) { 
104                 // Assume core # is same as logical processor for now
105                 // TODO FIX THIS FIX THIS
106                 // THERE SHOULD BE:  guestapicid->virtualapicid map,
107                 //                   cpu_id->logical processor map
108                 //     host maitains logical proc->phsysical proc
109                 PrintDebug("icc_bus: non-local core, forcing it to exit\n"); 
110                 V3_Call_On_CPU(dest_apic->core->cpu_id,v3_force_exit,(void*)(dest_apic->core));
111                 // TODO: do what the print says
112             }                                                   
113             break;                                                      
114             
115         case 2:   //SMI                 
116             PrintError("icc_bus: SMI delivery is unsupported\n");       
117             return -1;                                          
118             break;                                                      
119             
120         case 3:  //reserved                                             
121             PrintError("icc_bus: Reserved delivery mode 3 is unsupported\n"); 
122             return -1;                                          
123             break;                                                      
124
125         case 4:  //NMI                                  
126             PrintError("icc_bus: NMI delivery is unsupported\n"); 
127             return -1;                                          
128             break;                                                      
129
130         case 5: { //INIT
131             struct guest_info *core = dest_apic->core;
132
133             PrintDebug("icc_bus: INIT delivery to core %u\n",core->cpu_id);
134
135             // TODO: any APIC reset on dest core (shouldn't be needed, but not sure...)
136
137             // Sanity check
138             if (core->cpu_mode != INIT) { 
139                 PrintError("icc_bus: Warning: core %u is not in INIT state, ignored\n",core->cpu_id);
140                 // Only a warning, since INIT INIT SIPI is common
141                 break;
142             }
143
144             // We transition the target core to SIPI state
145             core->cpu_mode = SIPI;  // note: locking should not be needed here
146
147             // That should be it since the target core should be
148             // waiting in host on this transition
149             // either it's on another core or on a different preemptive thread
150             // in both cases, it will quickly notice this transition 
151             // in particular, we should not need to force an exit here
152
153             PrintDebug("icc_bus: INIT delivery done\n");
154
155         }
156             break;                                                      
157
158         case 6: { //SIPI
159             struct guest_info *core = dest_apic->core;
160
161             // Sanity check
162             if (core->cpu_mode!=SIPI) { 
163                 PrintError("icc_bus: core %u is not in SIPI state, ignored!\n",core->cpu_id);
164                 break;
165             }
166
167             // Write the RIP, CS, and descriptor
168             // assume the rest is already good to go
169             //
170             // vector VV -> rip at 0
171             //              CS = VV00
172             //  This means we start executing at linear address VV000
173             //
174             // So the selector needs to be VV00
175             // and the base needs to be VV000
176             //
177             core->rip = 0;
178             core->segments.cs.selector = icr->vec << 8;
179             core->segments.cs.limit = 0xffff;
180             core->segments.cs.base = icr->vec << 12;
181
182             PrintDebug("icc_bus: SIPI delivery (0x%x -> 0x%x:0x0) to core %u\n",
183                        icr->vec, core->segments.cs.selector, core->cpu_id);
184             // Maybe need to adjust the APIC?
185             
186             // We transition the target core to SIPI state
187             core->cpu_mode = REAL;  // note: locking should not be needed here
188
189             // As with INIT, we should not need to do anything else
190
191             PrintDebug("icc_bus: SIPI delivery done\n");
192
193         }
194             break;                                                      
195     }
196
197     return 0;
198
199
200
201 //
202 // icr_data contains interrupt vector *except* for ext_int
203 // in which case it is given via irq
204 //
205 int v3_icc_send_ipi(struct vm_device * icc_bus, uint32_t src_apic, uint64_t icr_data, uint32_t extirq) {
206     struct int_cmd_reg * icr = (struct int_cmd_reg *)&icr_data;
207     struct icc_bus_state * state = (struct icc_bus_state *)icc_bus->private_data;
208     struct apic_data * dest_apic = NULL;
209     PrintDebug("icc_bus: icc_bus=%p, src_apic=%u, icr_data=%llx, extirq=%u\n", 
210                icc_bus, src_apic, icr_data, extirq);
211
212     // initial sanity checks
213     if ((src_apic >= MAX_APICS) || 
214         ((state->apics[src_apic].present == 0) && 
215          (src_apic != state->ioapic_id))) { 
216         PrintError("icc_bus: Apparently sending from unregistered apic id=%u\n",src_apic);
217         return -1;
218     }
219
220
221     if ((icr->dst_mode == 0) && (state->apics[icr->dst].present == 0)) { 
222         PrintError("icc_bus: Attempted send to unregistered apic id=%u\n", icr->dst);
223         return -1;
224     }
225     
226     dest_apic =  &(state->apics[icr->dst]);
227
228     PrintDebug("icc_bus: IPI %s %u from %s %u to %s %u (icr=0x%llx) (extirq=%u)\n",
229                deliverymode_str[icr->del_mode], icr->vec, 
230                (src_apic == state->ioapic_id) ? "ioapic" : "apic",
231                src_apic, shorthand_str[icr->dst_shorthand], icr->dst,icr->val,
232                extirq);
233
234
235
236
237     switch (icr->dst_shorthand) {
238
239         case 0:  // no shorthand
240             if (deliver(src_apic, dest_apic, icr, state, extirq)) { 
241                 return -1;
242             }
243             break;
244
245         case 1:  // self
246             if (icr->dst == state->ioapic_id) { 
247                 PrintError("icc_bus: ioapic attempting to send to itself\n");
248                 return -1;
249             }
250
251             if (deliver(src_apic, dest_apic, icr, state, extirq)) { 
252                 return -1;
253             }
254             break;
255
256         case 2: 
257         case 3: { // all and all-but-me
258             int i;
259             for (i = 0; i < MAX_APICS; i++) { 
260                 dest_apic = &(state->apics[i]);
261
262                 if ( (dest_apic->present == 1) && 
263                      ((i != src_apic) || (icr->dst_shorthand == 2)) ) { 
264                     if (deliver(src_apic, dest_apic, icr, state, extirq)) { 
265                         return -1;
266                     }
267                 }
268             }
269             break;
270         }
271
272         default:
273             return -1;
274     }
275
276     return 0;
277 }
278
279
280
281 /* THIS IS A BIG ASSUMPTION: APIC PHYSID == LOGID == CORENUM */
282
283 int v3_icc_register_apic(struct guest_info  * core, struct vm_device * icc_bus, 
284                          uint8_t apic_num, struct v3_icc_ops * ops, void * priv_data) {
285     struct icc_bus_state * icc = (struct icc_bus_state *)icc_bus->private_data;
286     struct apic_data * apic = &(icc->apics[apic_num]);
287
288     if (apic->present == 1) {
289         PrintError("icc_bus: Attempt to re-register apic %u\n", apic_num);
290         return -1;
291     }
292     
293     apic->present = 1;
294     apic->priv_data = priv_data;
295     apic->core = core;
296     apic->ops = ops;
297    
298     PrintDebug("icc_bus: Registered apic %u\n", apic_num);
299
300     return 0;
301 }
302
303
304 int v3_icc_register_ioapic(struct v3_vm_info *vm, struct vm_device * icc_bus, uint8_t apic_num)
305 {
306     struct icc_bus_state * icc = (struct icc_bus_state *)icc_bus->private_data;
307
308     if (icc->ioapic_id) { 
309         PrintError("icc_bus: Attempt to register a second ioapic!\n");
310         return -1;
311     }
312
313     icc->ioapic_id=apic_num;
314
315     PrintDebug("icc_bus: Registered ioapic %u\n", apic_num);
316     
317
318     return 0;
319 }
320
321
322
323 static int icc_bus_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
324     PrintDebug("icc_bus: Creating ICC_BUS\n");
325
326     char * dev_id = v3_cfg_val(cfg, "ID");
327
328     struct icc_bus_state * icc_bus = (struct icc_bus_state *)V3_Malloc(sizeof(struct icc_bus_state));
329     memset(icc_bus, 0, sizeof(struct icc_bus_state));
330
331     struct vm_device * dev = v3_allocate_device(dev_id, &dev_ops, icc_bus);
332
333     if (v3_attach_device(vm, dev) == -1) {
334         PrintError("icc_bus: Could not attach device %s\n", dev_id);
335         return -1;
336     }
337
338     return 0;
339 }
340
341
342
343 device_register("ICC_BUS", icc_bus_init)