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.


Implemented SMP, still not functional
[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     struct guest_info *core=(struct guest_info *)p;
37     PrintDebug("core %u: Forced to exit!\n",core->cpu_id);
38 }
39
40 struct ipi_thunk_data {
41     struct vm_device * target;
42     uint64_t val;
43 };
44
45
46
47 struct apic_data {
48     struct guest_info * core;
49     struct v3_icc_ops * ops;
50     
51     void * priv_data;
52     int present;
53 };
54
55
56 struct icc_bus_state {
57     struct apic_data apics[MAX_APICS];
58     
59     uint32_t         ioapic_id;
60 };
61
62 static struct v3_device_ops dev_ops = {
63     .free = NULL,
64     .reset = NULL,
65     .start = NULL,
66     .stop = NULL,
67 };
68
69
70 static char *shorthand_str[] = { 
71     "(no shorthand)",
72     "(self)",
73     "(all)",
74     "(all-but-me)",
75  };
76
77 static char *deliverymode_str[] = { 
78     "(fixed)",
79     "(lowest priority)",
80     "(SMI)",
81     "(reserved)",
82     "(NMI)",
83     "(INIT)",
84     "(Start Up)",
85     "(reserved)",
86 };
87
88
89
90 static int deliver(uint32_t src_apic, struct apic_data *dest_apic, struct int_cmd_reg *icr, struct icc_bus_state * state) {
91
92     switch (icr->del_mode) {                                            
93
94         case 0:  //fixed
95         case 1: // lowest priority
96             PrintDebug("icc_bus: delivering IRQ to core %u\n",dest_apic->core->cpu_id); 
97             dest_apic->ops->raise_intr(dest_apic->core, icr->vec, dest_apic->priv_data); 
98             if (src_apic!=state->ioapic_id && dest_apic->core->cpu_id != src_apic) { 
99                 // Assume core # is same as logical processor for now
100                 // TODO FIX THIS FIX THIS
101                 // THERE SHOULD BE:  guestapicid->virtualapicid map,
102                 //                   cpu_id->logical processor map
103                 //     host maitains logical proc->phsysical proc
104                 PrintDebug("icc_bus: non-local core, forcing it to exit\n"); 
105                 V3_Call_On_CPU(dest_apic->core->cpu_id,v3_force_exit,(void*)(dest_apic->core));
106                 // TODO: do what the print says
107             }                                                   
108             break;                                                      
109             
110         case 2:   //SMI                 
111             PrintError("icc_bus: SMI delivery is unsupported\n");       
112             return -1;                                          
113             break;                                                      
114             
115         case 3:  //reserved                                             
116         case 7:
117             PrintError("icc_bus: Reserved delivery mode 3 is unsupported\n"); 
118             return -1;                                          
119             break;                                                      
120
121         case 4:  //NMI                                  
122             PrintError("icc_bus: NMI delivery is unsupported\n"); 
123             return -1;                                          
124             break;                                                      
125
126         case 5: { //INIT
127             struct guest_info *core = dest_apic->core;
128
129             PrintDebug("icc_bus: INIT delivery to core %u\n",core->cpu_id);
130
131             // TODO: any APIC reset on dest core (shouldn't be needed, but not sure...)
132
133             // Sanity check
134             if (core->cpu_mode!=INIT) { 
135                 PrintError("icc_bus: Warning: core %u is not in INIT state, ignored\n",core->cpu_id);
136                 // Only a warning, since INIT INIT SIPI is common
137                 break;
138             }
139
140             // We transition the target core to SIPI state
141             core->cpu_mode=SIPI;  // note: locking should not be needed here
142
143             // That should be it since the target core should be
144             // waiting in host on this transition
145             // either it's on another core or on a different preemptive thread
146             // in both cases, it will quickly notice this transition 
147             // in particular, we should not need to force an exit here
148
149             PrintDebug("icc_bus: INIT delivery done\n");
150
151         }
152             break;                                                      
153
154         case 6: { //SIPI
155             struct guest_info *core = dest_apic->core;
156             uint64_t rip = icr->vec << 12;  // vector encodes target address;
157
158             PrintDebug("icc_bus: SIPI delivery (0x%x -> rip=0x%p) to core %u\n",
159                        icr->vec, (void*)rip, core->cpu_id);
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             core->rip=rip & 0xffff;
170             core->segments.cs.selector = (rip >> 4) & 0xf000;
171             core->segments.cs.limit= 0xffff;
172             core->segments.cs.base = rip & 0xf0000;
173
174             // Maybe need to adjust the APIC?
175             
176             // We transition the target core to SIPI state
177             core->cpu_mode=REAL;  // note: locking should not be needed here
178
179             // As with INIT, we should not need to do anything else
180
181             PrintDebug("icc_bus: SIPI delivery done\n");
182
183         }
184             break;                                                      
185     }
186
187     return 0;
188
189
190
191
192 int v3_icc_send_ipi(struct vm_device * icc_bus, uint32_t src_apic, uint64_t icr_data) {
193
194     PrintDebug("icc_bus: icc_bus=%p, src_apic=%u, icr_data=%llx\n",icc_bus,src_apic,icr_data);
195
196     struct int_cmd_reg *icr = (struct int_cmd_reg *)&icr_data;
197     struct icc_bus_state * state = (struct icc_bus_state *)icc_bus->private_data;
198
199     // initial sanity checks
200     if (src_apic>=MAX_APICS || (!state->apics[src_apic].present && src_apic!=state->ioapic_id)) { 
201         PrintError("icc_bus: Apparently sending from unregistered apic id=%u\n",src_apic);
202         return -1;
203     }
204     if (icr->dst_mode==0  && !state->apics[icr->dst].present) { 
205         PrintError("icc_bus: Attempted send to unregistered apic id=%u\n",icr->dst);
206         return -1;
207     }
208     
209     struct apic_data * dest_apic =  &(state->apics[icr->dst]);
210
211
212     PrintDebug("icc_bus: IPI %s %u from %s %u to %s %u (icr=0x%llx)\n",
213                deliverymode_str[icr->del_mode], icr->vec, src_apic==state->ioapic_id ? "ioapic" : "apic",
214                src_apic, shorthand_str[icr->dst_shorthand], icr->dst,icr->val);
215
216
217
218
219     switch (icr->dst_shorthand) {
220
221         case 0:  // no shorthand
222             if (deliver(src_apic,dest_apic,icr,state)) { 
223                 return -1;
224             }
225             break;
226
227         case 1:  // self
228             if (icr->dst==state->ioapic_id) { 
229                 PrintError("icc_bus: ioapic attempting to send to itself\n");
230                 return -1;
231             }
232             if (deliver(src_apic,dest_apic,icr,state)) { 
233                 return -1;
234             }
235             break;
236
237         case 2: 
238         case 3: { // all and all-but-me
239             int i;
240             for (i=0;i<MAX_APICS;i++) { 
241                 dest_apic=&(state->apics[i]);
242                 if (dest_apic->present && (i!=src_apic || icr->dst_shorthand==2)) { 
243                     if (deliver(src_apic,dest_apic,icr,state)) { 
244                         return -1;
245                     }
246                 }
247             }
248         }
249             break;
250     }
251
252     return 0;
253 }
254
255
256
257 /* THIS IS A BIG ASSUMPTION: APIC PHYSID == LOGID == CORENUM */
258
259 int v3_icc_register_apic(struct guest_info  * core, struct vm_device * icc_bus, 
260                          uint8_t apic_num, struct v3_icc_ops * ops, void * priv_data) {
261     struct icc_bus_state * icc = (struct icc_bus_state *)icc_bus->private_data;
262     struct apic_data * apic = &(icc->apics[apic_num]);
263
264     if (apic->present == 1) {
265         PrintError("icc_bus: Attempt to re-register apic %u\n", apic_num);
266         return -1;
267     }
268     
269     apic->present = 1;
270     apic->priv_data = priv_data;
271     apic->core = core;
272     apic->ops = ops;
273    
274     PrintDebug("icc_bus: Registered apic %u\n", apic_num);
275
276     return 0;
277 }
278
279
280 int v3_icc_register_ioapic(struct v3_vm_info *vm, struct vm_device * icc_bus, uint8_t apic_num)
281 {
282     struct icc_bus_state * icc = (struct icc_bus_state *)icc_bus->private_data;
283
284     if (icc->ioapic_id) { 
285         PrintError("icc_bus: Attempt to register a second ioapic!\n");
286         return -1;
287     }
288
289     icc->ioapic_id=apic_num;
290
291     PrintDebug("icc_bus: Registered ioapic %u\n", apic_num);
292     
293
294     return 0;
295 }
296
297
298
299 static int icc_bus_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
300     PrintDebug("icc_bus: Creating ICC_BUS\n");
301
302     char * name = v3_cfg_val(cfg, "name");
303
304     struct icc_bus_state * icc_bus = (struct icc_bus_state *)V3_Malloc(sizeof(struct icc_bus_state));
305     memset(icc_bus, 0, sizeof(struct icc_bus_state));
306
307     struct vm_device * dev = v3_allocate_device(name, &dev_ops, icc_bus);
308
309     if (v3_attach_device(vm, dev) == -1) {
310         PrintError("icc_bus: Could not attach device %s\n", name);
311         return -1;
312     }
313
314     return 0;
315 }
316
317
318
319 device_register("ICC_BUS", icc_bus_init)