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.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
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.
14 * Author: Jack Lange <jarusl@cs.northwestern.edu>
16 * This is free software. You are permitted to use,
17 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
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>
29 #ifndef CONFIG_DEBUG_ICC_BUS
31 #define PrintDebug(fmt, args...)
35 void v3_force_exit(void *p) {
36 #ifdef CONFIG_DEBUG_ICC_BUS
37 struct guest_info *core=(struct guest_info *)p;
39 PrintDebug("core %u: Forced to exit!\n",core->cpu_id);
42 struct ipi_thunk_data {
43 struct vm_device * target;
50 struct guest_info * core;
51 struct v3_icc_ops * ops;
58 struct icc_bus_state {
59 struct apic_data apics[MAX_APICS];
64 static struct v3_device_ops dev_ops = {
71 #ifdef CONFIG_DEBUG_ICC_BUS
72 static char *shorthand_str[] = {
79 static char *deliverymode_str[] = {
92 static int deliver(uint32_t src_apic, struct apic_data *dest_apic, struct int_cmd_reg *icr, struct icc_bus_state * state) {
94 switch (icr->del_mode) {
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
113 PrintError("icc_bus: SMI delivery is unsupported\n");
119 PrintError("icc_bus: Reserved delivery mode 3 is unsupported\n");
124 PrintError("icc_bus: NMI delivery is unsupported\n");
129 struct guest_info *core = dest_apic->core;
131 PrintDebug("icc_bus: INIT delivery to core %u\n",core->cpu_id);
133 // TODO: any APIC reset on dest core (shouldn't be needed, but not sure...)
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
142 // We transition the target core to SIPI state
143 core->cpu_mode=SIPI; // note: locking should not be needed here
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
151 PrintDebug("icc_bus: INIT delivery done\n");
157 struct guest_info *core = dest_apic->core;
158 uint64_t rip = icr->vec << 12; // vector encodes target address;
160 PrintDebug("icc_bus: SIPI delivery (0x%x -> rip=0x%p) to core %u\n",
161 icr->vec, (void*)rip, core->cpu_id);
164 if (core->cpu_mode!=SIPI) {
165 PrintError("icc_bus: core %u is not in SIPI state, ignored!\n",core->cpu_id);
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;
176 // Maybe need to adjust the APIC?
178 // We transition the target core to SIPI state
179 core->cpu_mode=REAL; // note: locking should not be needed here
181 // As with INIT, we should not need to do anything else
183 PrintDebug("icc_bus: SIPI delivery done\n");
194 int v3_icc_send_ipi(struct vm_device * icc_bus, uint32_t src_apic, uint64_t icr_data) {
196 PrintDebug("icc_bus: icc_bus=%p, src_apic=%u, icr_data=%llx\n",icc_bus,src_apic,icr_data);
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;
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);
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);
211 struct apic_data * dest_apic = &(state->apics[icr->dst]);
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);
221 switch (icr->dst_shorthand) {
223 case 0: // no shorthand
224 if (deliver(src_apic,dest_apic,icr,state)) {
230 if (icr->dst==state->ioapic_id) {
231 PrintError("icc_bus: ioapic attempting to send to itself\n");
234 if (deliver(src_apic,dest_apic,icr,state)) {
240 case 3: { // all and all-but-me
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)) {
259 /* THIS IS A BIG ASSUMPTION: APIC PHYSID == LOGID == CORENUM */
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]);
266 if (apic->present == 1) {
267 PrintError("icc_bus: Attempt to re-register apic %u\n", apic_num);
272 apic->priv_data = priv_data;
276 PrintDebug("icc_bus: Registered apic %u\n", apic_num);
282 int v3_icc_register_ioapic(struct v3_vm_info *vm, struct vm_device * icc_bus, uint8_t apic_num)
284 struct icc_bus_state * icc = (struct icc_bus_state *)icc_bus->private_data;
286 if (icc->ioapic_id) {
287 PrintError("icc_bus: Attempt to register a second ioapic!\n");
291 icc->ioapic_id=apic_num;
293 PrintDebug("icc_bus: Registered ioapic %u\n", apic_num);
301 static int icc_bus_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
302 PrintDebug("icc_bus: Creating ICC_BUS\n");
304 char * name = v3_cfg_val(cfg, "name");
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));
309 struct vm_device * dev = v3_allocate_device(name, &dev_ops, icc_bus);
311 if (v3_attach_device(vm, dev) == -1) {
312 PrintError("icc_bus: Could not attach device %s\n", name);
321 device_register("ICC_BUS", icc_bus_init)