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.


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