X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=palacios%2Fsrc%2Fdevices%2Ficc_bus.c;h=0a74eee17f37666cdc54ef5855f3a6ddc10b918f;hb=ae4e727a7abfdb7df9cb93769a0799494ec75254;hp=f5de549646b4cf321c0ca3af78c70a940c8184d7;hpb=e1dcef8166cab60ca0304121ddd176760af0d3d6;p=palacios.git diff --git a/palacios/src/devices/icc_bus.c b/palacios/src/devices/icc_bus.c index f5de549..0a74eee 100644 --- a/palacios/src/devices/icc_bus.c +++ b/palacios/src/devices/icc_bus.c @@ -21,41 +21,28 @@ #include #include #include +#include #define MAX_APICS 256 +#ifndef CONFIG_DEBUG_ICC_BUS +#undef PrintDebug +#define PrintDebug(fmt, args...) +#endif + + +void v3_force_exit(void *p) { +#ifdef CONFIG_DEBUG_ICC_BUS + struct guest_info *core=(struct guest_info *)p; +#endif + PrintDebug("core %u: Forced to exit!\n",core->cpu_id); +} struct ipi_thunk_data { struct vm_device * target; uint64_t val; }; -struct int_cmd_reg { - union { - uint64_t val; - - struct { - uint32_t lo; - uint32_t hi; - } __attribute__((packed)); - - struct { - uint_t vec : 8; - uint_t msg_type : 3; - uint_t dst_mode : 1; - uint_t del_status : 1; - uint_t rsvd1 : 1; - uint_t lvl : 1; - uint_t trig_mode : 1; - uint_t rem_rd_status : 2; - uint_t dst_shorthand : 2; - uint64_t rsvd2 : 36; - uint32_t dst : 8; - } __attribute__((packed)); - } __attribute__((packed)); -} __attribute__((packed)); - - struct apic_data { @@ -69,6 +56,8 @@ struct apic_data { struct icc_bus_state { struct apic_data apics[MAX_APICS]; + + uint32_t ioapic_id; }; static struct v3_device_ops dev_ops = { @@ -78,60 +67,309 @@ static struct v3_device_ops dev_ops = { .stop = NULL, }; +#ifdef CONFIG_DEBUG_ICC_BUS +static char *shorthand_str[] = { + "(no shorthand)", + "(self)", + "(all)", + "(all-but-me)", + }; + +static char *deliverymode_str[] = { + "(fixed)", + "(lowest priority)", + "(SMI)", + "(reserved)", + "(NMI)", + "(INIT)", + "(Start Up)", + "(ExtInt)", +}; +#endif + + +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) { + + switch (icr->del_mode) { + + case 0: //fixed + case 1: // lowest priority + case 7: // ExtInt + PrintDebug("icc_bus: delivering IRQ to core %u\n",dest_apic->core->cpu_id); + dest_apic->ops->raise_intr(dest_apic->core, + icr->del_mode!=7 ? icr->vec : extirq, + dest_apic->priv_data); + if (src_apic!=state->ioapic_id && dest_apic->core->cpu_id != src_apic) { + // Assume core # is same as logical processor for now + // TODO FIX THIS FIX THIS + // THERE SHOULD BE: guestapicid->virtualapicid map, + // cpu_id->logical processor map + // host maitains logical proc->phsysical proc + PrintDebug("icc_bus: non-local core, forcing it to exit\n"); + V3_Call_On_CPU(dest_apic->core->cpu_id,v3_force_exit,(void*)(dest_apic->core)); + // TODO: do what the print says + } + break; + + case 2: //SMI + PrintError("icc_bus: SMI delivery is unsupported\n"); + return -1; + break; + + case 3: //reserved + PrintError("icc_bus: Reserved delivery mode 3 is unsupported\n"); + return -1; + break; + + case 4: //NMI + PrintError("icc_bus: NMI delivery is unsupported\n"); + return -1; + break; + + case 5: { //INIT + struct guest_info *core = dest_apic->core; + + PrintDebug("icc_bus: INIT delivery to core %u\n",core->cpu_id); + + // TODO: any APIC reset on dest core (shouldn't be needed, but not sure...) + + // Sanity check + if (core->cpu_mode != INIT) { + PrintError("icc_bus: Warning: core %u is not in INIT state, ignored\n",core->cpu_id); + // Only a warning, since INIT INIT SIPI is common + break; + } + + // We transition the target core to SIPI state + core->cpu_mode = SIPI; // note: locking should not be needed here + + // That should be it since the target core should be + // waiting in host on this transition + // either it's on another core or on a different preemptive thread + // in both cases, it will quickly notice this transition + // in particular, we should not need to force an exit here + + PrintDebug("icc_bus: INIT delivery done\n"); + + } + break; + + case 6: { //SIPI + struct guest_info *core = dest_apic->core; + + // Sanity check + if (core->cpu_mode!=SIPI) { + PrintError("icc_bus: core %u is not in SIPI state, ignored!\n",core->cpu_id); + break; + } + + // Write the RIP, CS, and descriptor + // assume the rest is already good to go + // + // vector VV -> rip at 0 + // CS = VV00 + // This means we start executing at linear address VV000 + // + // So the selector needs to be VV00 + // and the base needs to be VV000 + // + core->rip = 0; + core->segments.cs.selector = icr->vec << 8; + core->segments.cs.limit = 0xffff; + core->segments.cs.base = icr->vec << 12; + + PrintDebug("icc_bus: SIPI delivery (0x%x -> 0x%x:0x0) to core %u\n", + icr->vec, core->segments.cs.selector, core->cpu_id); + // Maybe need to adjust the APIC? + + // We transition the target core to SIPI state + core->cpu_mode = REAL; // note: locking should not be needed here + + // As with INIT, we should not need to do anything else + + PrintDebug("icc_bus: SIPI delivery done\n"); + + } + break; + } + return 0; +} -int v3_icc_send_irq(struct vm_device * icc_bus, uint8_t apic_num, uint32_t irq_num) { - struct icc_bus_state * state = (struct icc_bus_state *)icc_bus->private_data; - struct apic_data * apic = &(state->apics[apic_num]); - +// +// icr_data contains interrupt vector *except* for ext_int +// in which case it is given via irq +// - struct int_cmd_reg icr; - icr.lo = irq_num; +int v3_icc_send_ipi(struct vm_device * icc_bus, uint32_t src_apic, uint64_t icr_data, + uint32_t dfr_data, uint32_t extirq) { + PrintDebug("icc_bus: icc_bus=%p, src_apic=%u, icr_data=%llx, extirq=%u\n",icc_bus,src_apic,icr_data,extirq); - char * type = NULL; - char * dest = NULL; - char foo[8]; + struct int_cmd_reg *icr = (struct int_cmd_reg *)&icr_data; + struct dst_fmt_reg *dfr = (struct dst_fmt_reg*)&dfr_data; - switch (icr.dst_shorthand) { - case 0x0: - sprintf(foo, "%d", icr.dst); - dest = foo; - break; - case 0x1: - dest = "(self)"; - break; - case 0x2: - dest = "(broadcast inclusive)"; - break; - case 0x3: - dest = "(broadcast)"; - break; + struct icc_bus_state * state = (struct icc_bus_state *)icc_bus->private_data; + struct apic_data * dest_apic = NULL; + PrintDebug("icc_bus: icc_bus=%p, src_apic=%u, icr_data=%llx, extirq=%u\n", + icc_bus, src_apic, icr_data, extirq); + + // initial sanity checks + if ((src_apic >= MAX_APICS) || + ((state->apics[src_apic].present == 0) && + (src_apic != state->ioapic_id))) { + PrintError("icc_bus: Apparently sending from unregistered apic id=%u\n",src_apic); + return -1; } - switch (icr.msg_type) { - case 0x0: - type = ""; - break; - case 0x4: - type = "(NMI)"; - break; - case 0x5: - type = "(INIT)"; - break; - case 0x6: - type = "(Startup)"; - break; + + if ((icr->dst_mode == 0) && (state->apics[icr->dst].present == 0)) { + PrintError("icc_bus: Attempted send to unregistered apic id=%u\n", icr->dst); + return -1; } + dest_apic = &(state->apics[icr->dst]); - PrintDebug("Sending IPI of type %s and destination type %s from LAPIC %u to LAPIC %u.\n", - type, dest, V3_Get_CPU(), apic_num); - apic->ops->raise_intr(apic->core, irq_num & 0xff, apic->priv_data); + PrintDebug("icc_bus: IPI %s %u from %s %u to %s %s %u (icr=0x%llx, dfr=0x%x) (extirq=%u)\n", + deliverymode_str[icr->del_mode], icr->vec, + src_apic==state->ioapic_id ? "ioapic" : "apic", + src_apic, + icr->dst_mode==0 ? "(physical)" : "(logical)", + shorthand_str[icr->dst_shorthand], icr->dst,icr->val, dfr->val, + extirq); - //V3_Call_On_CPU(apic_num, icc_force_exit, (void *)(uint64_t)(val & 0xff)); + /* + + if (icr->dst==state->ioapic_id) { + PrintError("icc_bus: Attempted send to ioapic ignored\n"); + return -1; + } + */ + + + + switch (icr->dst_shorthand) { + + case 0: // no shorthand + + if (icr->dst_mode==0) { + // physical delivery + struct apic_data * dest_apic = &(state->apics[icr->dst]); + if (deliver(src_apic,dest_apic,icr,state,extirq)) { + return -1; + } + } else { + // logical delivery + uint8_t mda = icr->dst; // message destination address, not physical address + + if (dfr->model==0xf) { + // flat model + // this means we deliver the IPI each destination APIC where + // mda of sender & ldr of receiver is nonzero + // mda=0xff means broadcast to all + // + int i; + for (i=0;iapics[i]); + if (dest_apic->present && + dest_apic->ops->should_deliver_flat(dest_apic->core, + mda, + dest_apic->priv_data)) { + if (deliver(src_apic,dest_apic,icr,state,extirq)) { + return -1; + } + } + } + } else if (dfr->model==0x0) { + // cluster model + // + // there are two variants of this + // + // 1. (ancient P5/P6) All apics are on one bus + // mda[31:28] is the target cluster, + // mda[27:24] has one bit for each apic in the cluster + // mda[31:28] of sending apic == ldr[31:28] of dest apic means + // the dest apic is part of the cluster + // then mda[27:24] & ldr[27:24] nonzero means to deliver + // also, mda=0xff still means broadcast + // So, basically, you have 15 clusters of 4 apics each + broadcast + // + // 2. (current) hierarchical cluster model + // This is some hwat unclearly documented in volume 3, 9-32 + // basically, you have a hierarchy of clusters that where + // each cluster has 4 agents (APICs?) and a cluster manager. + // The cluster manager is not an apic, though, and outside of + // scope of documents. Again, you have 15 clusters of 4 apics + // each + broadcast. My impression is that this is identical + // to variant 1 for our purposes. + // + // + // if we are in lowest priorty mode, we should just pick one + // according to the arbitrarion prioty register + int i; + for (i=0;iapics[i]); + if (dest_apic->present && + dest_apic->ops->should_deliver_cluster(dest_apic->core, + mda, + dest_apic->priv_data)) { + if (deliver(src_apic,dest_apic,icr,state,extirq)) { + return -1; + } + } + } + } else { + PrintError("icc_bus: unknown logical delivery model 0x%x\n", dfr->model); + return -1; + } + + } + + break; + + case 1: // self + + if (icr->dst_mode==0) { + // physical delivery + if (icr->dst==state->ioapic_id) { + PrintError("icc_bus: ioapic attempting to send to itself\n"); + return -1; + } + struct apic_data *dest_apic=&(state->apics[src_apic]); + if (deliver(src_apic,dest_apic,icr,state,extirq)) { + return -1; + } + } else { + // logical delivery + PrintError("icc_bus: use of logical delivery in self is not yet supported.\n"); + + return -1; + } + break; + + case 2: + + case 3: { // all and all-but-me + // assuming that logical verus physical doesn't matter + // although it is odd that both are used + int i; + for (i=0;iapics[i]); + if (dest_apic->present && (i!=src_apic || icr->dst_shorthand==2)) { + if (deliver(src_apic,dest_apic,icr,state,extirq)) { + return -1; + } + } + } + } + break; + + default: + return -1; + } + return 0; } @@ -146,7 +384,7 @@ int v3_icc_register_apic(struct guest_info * core, struct vm_device * icc_bus, struct apic_data * apic = &(icc->apics[apic_num]); if (apic->present == 1) { - PrintError("Attempt to re-register apic %u\n", apic_num); + PrintError("icc_bus: Attempt to re-register apic %u\n", apic_num); return -1; } @@ -155,26 +393,42 @@ int v3_icc_register_apic(struct guest_info * core, struct vm_device * icc_bus, apic->core = core; apic->ops = ops; - PrintDebug("Registered apic%u\n", apic_num); + PrintDebug("icc_bus: Registered apic %u\n", apic_num); return 0; } +int v3_icc_register_ioapic(struct v3_vm_info *vm, struct vm_device * icc_bus, uint8_t apic_num) +{ + struct icc_bus_state * icc = (struct icc_bus_state *)icc_bus->private_data; + + if (icc->ioapic_id) { + PrintError("icc_bus: Attempt to register a second ioapic!\n"); + return -1; + } + + icc->ioapic_id=apic_num; + + PrintDebug("icc_bus: Registered ioapic %u\n", apic_num); + + + return 0; +} static int icc_bus_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { - PrintDebug("Creating ICC_BUS\n"); + PrintDebug("icc_bus: Creating ICC_BUS\n"); - char * name = v3_cfg_val(cfg, "name"); + char * dev_id = v3_cfg_val(cfg, "ID"); struct icc_bus_state * icc_bus = (struct icc_bus_state *)V3_Malloc(sizeof(struct icc_bus_state)); memset(icc_bus, 0, sizeof(struct icc_bus_state)); - struct vm_device * dev = v3_allocate_device(name, &dev_ops, icc_bus); + struct vm_device * dev = v3_allocate_device(dev_id, &dev_ops, icc_bus); if (v3_attach_device(vm, dev) == -1) { - PrintError("Could not attach device %s\n", name); + PrintError("icc_bus: Could not attach device %s\n", dev_id); return -1; }