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.


Semi-functional SMP (boots Kitten guest with two cores)
[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
207     PrintDebug("icc_bus: icc_bus=%p, src_apic=%u, icr_data=%llx, extirq=%u\n",icc_bus,src_apic,icr_data,extirq);
208
209     struct int_cmd_reg *icr = (struct int_cmd_reg *)&icr_data;
210     struct icc_bus_state * state = (struct icc_bus_state *)icc_bus->private_data;
211
212     // initial sanity checks
213     if (src_apic>=MAX_APICS || (!state->apics[src_apic].present && src_apic!=state->ioapic_id)) { 
214         PrintError("icc_bus: Apparently sending from unregistered apic id=%u\n",src_apic);
215         return -1;
216     }
217     if (icr->dst_mode==0  && !state->apics[icr->dst].present) { 
218         PrintError("icc_bus: Attempted send to unregistered apic id=%u\n",icr->dst);
219         return -1;
220     }
221     
222     struct apic_data * dest_apic =  &(state->apics[icr->dst]);
223
224     PrintDebug("icc_bus: IPI %s %u from %s %u to %s %u (icr=0x%llx) (extirq=%u)\n",
225                deliverymode_str[icr->del_mode], icr->vec, src_apic==state->ioapic_id ? "ioapic" : "apic",
226                src_apic, shorthand_str[icr->dst_shorthand], icr->dst,icr->val,
227                extirq);
228
229
230
231
232     switch (icr->dst_shorthand) {
233
234         case 0:  // no shorthand
235             if (deliver(src_apic,dest_apic,icr,state,extirq)) { 
236                 return -1;
237             }
238             break;
239
240         case 1:  // self
241             if (icr->dst==state->ioapic_id) { 
242                 PrintError("icc_bus: ioapic attempting to send to itself\n");
243                 return -1;
244             }
245             if (deliver(src_apic,dest_apic,icr,state,extirq)) { 
246                 return -1;
247             }
248             break;
249
250         case 2: 
251         case 3: { // all and all-but-me
252             int i;
253             for (i=0;i<MAX_APICS;i++) { 
254                 dest_apic=&(state->apics[i]);
255                 if (dest_apic->present && (i!=src_apic || icr->dst_shorthand==2)) { 
256                     if (deliver(src_apic,dest_apic,icr,state,extirq)) { 
257                         return -1;
258                     }
259                 }
260             }
261         }
262             break;
263     }
264
265     return 0;
266 }
267
268
269
270 /* THIS IS A BIG ASSUMPTION: APIC PHYSID == LOGID == CORENUM */
271
272 int v3_icc_register_apic(struct guest_info  * core, struct vm_device * icc_bus, 
273                          uint8_t apic_num, struct v3_icc_ops * ops, void * priv_data) {
274     struct icc_bus_state * icc = (struct icc_bus_state *)icc_bus->private_data;
275     struct apic_data * apic = &(icc->apics[apic_num]);
276
277     if (apic->present == 1) {
278         PrintError("icc_bus: Attempt to re-register apic %u\n", apic_num);
279         return -1;
280     }
281     
282     apic->present = 1;
283     apic->priv_data = priv_data;
284     apic->core = core;
285     apic->ops = ops;
286    
287     PrintDebug("icc_bus: Registered apic %u\n", apic_num);
288
289     return 0;
290 }
291
292
293 int v3_icc_register_ioapic(struct v3_vm_info *vm, struct vm_device * icc_bus, uint8_t apic_num)
294 {
295     struct icc_bus_state * icc = (struct icc_bus_state *)icc_bus->private_data;
296
297     if (icc->ioapic_id) { 
298         PrintError("icc_bus: Attempt to register a second ioapic!\n");
299         return -1;
300     }
301
302     icc->ioapic_id=apic_num;
303
304     PrintDebug("icc_bus: Registered ioapic %u\n", apic_num);
305     
306
307     return 0;
308 }
309
310
311
312 static int icc_bus_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
313     PrintDebug("icc_bus: Creating ICC_BUS\n");
314
315     char * name = v3_cfg_val(cfg, "name");
316
317     struct icc_bus_state * icc_bus = (struct icc_bus_state *)V3_Malloc(sizeof(struct icc_bus_state));
318     memset(icc_bus, 0, sizeof(struct icc_bus_state));
319
320     struct vm_device * dev = v3_allocate_device(name, &dev_ops, icc_bus);
321
322     if (v3_attach_device(vm, dev) == -1) {
323         PrintError("icc_bus: Could not attach device %s\n", name);
324         return -1;
325     }
326
327     return 0;
328 }
329
330
331
332 device_register("ICC_BUS", icc_bus_init)