version 1.47, 2010/12/20 00:25:46 |
version 1.47.6.1, 2011/06/03 13:27:42 |
Line 61 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 61 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include "pci.h" |
#include "pci.h" |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
|
#include <sys/cpu.h> |
#include <sys/kernel.h> |
#include <sys/kernel.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
#include <sys/device.h> |
#include <sys/device.h> |
Line 82 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 83 __KERNEL_RCSID(0, "$NetBSD$"); |
|
* This lock protects updates to the following mapping and reference-count |
* This lock protects updates to the following mapping and reference-count |
* arrays. The lock does not need to be acquired to read the mapping tables. |
* arrays. The lock does not need to be acquired to read the mapping tables. |
*/ |
*/ |
static struct simplelock irq_mapping_update_lock = SIMPLELOCK_INITIALIZER; |
static struct simplelock evtchn_lock = SIMPLELOCK_INITIALIZER; |
|
|
/* event handlers */ |
/* event handlers */ |
struct evtsource *evtsource[NR_EVENT_CHANNELS]; |
struct evtsource *evtsource[NR_EVENT_CHANNELS]; |
|
|
/* Reference counts for bindings to event channels */ |
/* Reference counts for bindings to event channels XXX: redo for SMP */ |
static uint8_t evtch_bindcount[NR_EVENT_CHANNELS]; |
static uint8_t evtch_bindcount[NR_EVENT_CHANNELS]; |
|
|
|
/* event-channel <-> VCPU mapping for IPIs. XXX: redo for SMP. */ |
|
static evtchn_port_t vcpu_ipi_to_evtch[MAX_VIRT_CPUS]; |
|
|
|
/* event-channel <-> VCPU mapping for VIRQ_TIMER. XXX: redo for SMP. */ |
|
static int virq_timer_to_evtch[MAX_VIRT_CPUS]; |
|
|
/* event-channel <-> VIRQ mapping. */ |
/* event-channel <-> VIRQ mapping. */ |
static int virq_to_evtch[NR_VIRQS]; |
static int virq_to_evtch[NR_VIRQS]; |
|
|
Line 137 events_default_setup(void) |
|
Line 144 events_default_setup(void) |
|
{ |
{ |
int i; |
int i; |
|
|
|
/* No VCPU -> event mappings. */ |
|
for (i = 0; i < MAX_VIRT_CPUS; i++) |
|
vcpu_ipi_to_evtch[i] = -1; |
|
|
|
/* No VIRQ_TIMER -> event mappings. */ |
|
for (i = 0; i < MAX_VIRT_CPUS; i++) |
|
virq_timer_to_evtch[i] = -1; |
|
|
/* No VIRQ -> event mappings. */ |
/* No VIRQ -> event mappings. */ |
for (i = 0; i < NR_VIRQS; i++) |
for (i = 0; i < NR_VIRQS; i++) |
virq_to_evtch[i] = -1; |
virq_to_evtch[i] = -1; |
|
|
events_init(void) |
events_init(void) |
{ |
{ |
debug_port = bind_virq_to_evtch(VIRQ_DEBUG); |
debug_port = bind_virq_to_evtch(VIRQ_DEBUG); |
|
KASSERT(debug_port != -1); |
|
|
aprint_verbose("debug virtual interrupt using event channel %d\n", |
aprint_verbose("debug virtual interrupt using event channel %d\n", |
debug_port); |
debug_port); |
/* |
/* |
Line 197 evtchn_do_event(int evtch, struct intrfr |
|
Line 214 evtchn_do_event(int evtch, struct intrfr |
|
if (evtch == IRQ_DEBUG) |
if (evtch == IRQ_DEBUG) |
printf("evtchn_do_event: evtch %d\n", evtch); |
printf("evtchn_do_event: evtch %d\n", evtch); |
#endif |
#endif |
ci = &cpu_info_primary; |
ci = curcpu(); |
|
|
/* |
/* |
* Shortcut for the debug handler, we want it to always run, |
* Shortcut for the debug handler, we want it to always run, |
Line 231 evtchn_do_event(int evtch, struct intrfr |
|
Line 248 evtchn_do_event(int evtch, struct intrfr |
|
ci->ci_ilevel = evtsource[evtch]->ev_maxlevel; |
ci->ci_ilevel = evtsource[evtch]->ev_maxlevel; |
iplmask = evtsource[evtch]->ev_imask; |
iplmask = evtsource[evtch]->ev_imask; |
sti(); |
sti(); |
|
simple_lock(&evtsource[evtch]->ev_lock); |
ih = evtsource[evtch]->ev_handlers; |
ih = evtsource[evtch]->ev_handlers; |
while (ih != NULL) { |
while (ih != NULL) { |
if (ih->ih_level <= ilevel) { |
if (ih->ih_level <= ilevel) { |
Line 242 evtchn_do_event(int evtch, struct intrfr |
|
Line 260 evtchn_do_event(int evtch, struct intrfr |
|
hypervisor_set_ipending(iplmask, |
hypervisor_set_ipending(iplmask, |
evtch >> LONG_SHIFT, evtch & LONG_MASK); |
evtch >> LONG_SHIFT, evtch & LONG_MASK); |
/* leave masked */ |
/* leave masked */ |
|
simple_unlock(&evtsource[evtch]->ev_lock); |
goto splx; |
goto splx; |
} |
} |
iplmask &= ~IUNMASK(ci, ih->ih_level); |
iplmask &= ~IUNMASK(ci, ih->ih_level); |
Line 250 evtchn_do_event(int evtch, struct intrfr |
|
Line 269 evtchn_do_event(int evtch, struct intrfr |
|
ih_fun(ih->ih_arg, regs); |
ih_fun(ih->ih_arg, regs); |
ih = ih->ih_evt_next; |
ih = ih->ih_evt_next; |
} |
} |
|
simple_unlock(&evtsource[evtch]->ev_lock); |
cli(); |
cli(); |
hypervisor_enable_event(evtch); |
hypervisor_enable_event(evtch); |
splx: |
splx: |
|
|
return 0; |
return 0; |
} |
} |
|
|
|
#define PRIuCPUID "lu" /* XXX: move this somewhere more appropriate */ |
|
|
|
evtchn_port_t |
|
bind_vcpu_to_evtch(cpuid_t vcpu) |
|
{ |
|
evtchn_op_t op; |
|
evtchn_port_t evtchn; |
|
int s; |
|
|
|
s = splhigh(); |
|
simple_lock(&evtchn_lock); |
|
|
|
evtchn = vcpu_ipi_to_evtch[vcpu]; |
|
if (evtchn == -1) { |
|
op.cmd = EVTCHNOP_bind_ipi; |
|
op.u.bind_ipi.vcpu = (uint32_t) vcpu; |
|
if (HYPERVISOR_event_channel_op(&op) != 0) |
|
panic("Failed to bind ipi to VCPU %"PRIuCPUID"\n", vcpu); |
|
evtchn = op.u.bind_ipi.port; |
|
|
|
vcpu_ipi_to_evtch[vcpu] = evtchn; |
|
} |
|
|
|
evtch_bindcount[evtchn]++; |
|
|
|
simple_unlock(&evtchn_lock); |
|
splx(s); |
|
|
|
return evtchn; |
|
} |
|
|
int |
int |
bind_virq_to_evtch(int virq) |
bind_virq_to_evtch(int virq) |
{ |
{ |
Line 299 bind_virq_to_evtch(int virq) |
|
Line 350 bind_virq_to_evtch(int virq) |
|
int evtchn, s; |
int evtchn, s; |
|
|
s = splhigh(); |
s = splhigh(); |
simple_lock(&irq_mapping_update_lock); |
simple_lock(&evtchn_lock); |
|
|
evtchn = virq_to_evtch[virq]; |
/* |
|
* XXX: The only per-cpu VIRQ we currently use is VIRQ_TIMER. |
|
* Please re-visit this implementation when others are used. |
|
* Note: VIRQ_DEBUG is special-cased, and not used or bound on APs. |
|
* XXX: event->virq/ipi can be unified in a linked-list |
|
* implementation. |
|
*/ |
|
struct cpu_info *ci = curcpu(); |
|
|
|
if (virq == VIRQ_DEBUG && ci != &cpu_info_primary) { |
|
return -1; |
|
} |
|
|
|
if (virq == VIRQ_TIMER) { |
|
evtchn = virq_timer_to_evtch[ci->ci_cpuid]; |
|
} |
|
else { |
|
evtchn = virq_to_evtch[virq]; |
|
} |
if (evtchn == -1) { |
if (evtchn == -1) { |
op.cmd = EVTCHNOP_bind_virq; |
op.cmd = EVTCHNOP_bind_virq; |
op.u.bind_virq.virq = virq; |
op.u.bind_virq.virq = virq; |
op.u.bind_virq.vcpu = 0; |
op.u.bind_virq.vcpu = ci->ci_cpuid; |
if (HYPERVISOR_event_channel_op(&op) != 0) |
if (HYPERVISOR_event_channel_op(&op) != 0) |
panic("Failed to bind virtual IRQ %d\n", virq); |
panic("Failed to bind virtual IRQ %d\n", virq); |
evtchn = op.u.bind_virq.port; |
evtchn = op.u.bind_virq.port; |
Line 315 bind_virq_to_evtch(int virq) |
|
Line 384 bind_virq_to_evtch(int virq) |
|
|
|
evtch_bindcount[evtchn]++; |
evtch_bindcount[evtchn]++; |
|
|
simple_unlock(&irq_mapping_update_lock); |
simple_unlock(&evtchn_lock); |
splx(s); |
splx(s); |
|
|
return evtchn; |
return evtchn; |
|
|
unbind_virq_from_evtch(int virq) |
unbind_virq_from_evtch(int virq) |
{ |
{ |
evtchn_op_t op; |
evtchn_op_t op; |
int evtchn = virq_to_evtch[virq]; |
int evtchn; |
int s = splhigh(); |
int s; |
|
|
|
struct cpu_info *ci = curcpu(); |
|
|
simple_lock(&irq_mapping_update_lock); |
if (virq == VIRQ_TIMER) { |
|
evtchn = virq_timer_to_evtch[ci->ci_cpuid]; |
|
} |
|
else { |
|
evtchn = virq_to_evtch[virq]; |
|
} |
|
|
|
if (evtchn == -1) { |
|
return -1; |
|
} |
|
|
|
s = splhigh(); |
|
simple_lock(&evtchn_lock); |
|
|
evtch_bindcount[evtchn]--; |
evtch_bindcount[evtchn]--; |
if (evtch_bindcount[evtchn] == 0) { |
if (evtch_bindcount[evtchn] == 0) { |
Line 340 unbind_virq_from_evtch(int virq) |
|
Line 423 unbind_virq_from_evtch(int virq) |
|
virq_to_evtch[virq] = -1; |
virq_to_evtch[virq] = -1; |
} |
} |
|
|
simple_unlock(&irq_mapping_update_lock); |
simple_unlock(&evtchn_lock); |
splx(s); |
splx(s); |
|
|
return evtchn; |
return evtchn; |
Line 358 bind_pirq_to_evtch(int pirq) |
|
Line 441 bind_pirq_to_evtch(int pirq) |
|
} |
} |
|
|
s = splhigh(); |
s = splhigh(); |
simple_lock(&irq_mapping_update_lock); |
simple_lock(&evtchn_lock); |
|
|
evtchn = pirq_to_evtch[pirq]; |
evtchn = pirq_to_evtch[pirq]; |
if (evtchn == -1) { |
if (evtchn == -1) { |
Line 377 bind_pirq_to_evtch(int pirq) |
|
Line 460 bind_pirq_to_evtch(int pirq) |
|
|
|
evtch_bindcount[evtchn]++; |
evtch_bindcount[evtchn]++; |
|
|
simple_unlock(&irq_mapping_update_lock); |
simple_unlock(&evtchn_lock); |
splx(s); |
splx(s); |
|
|
return evtchn; |
return evtchn; |
Line 390 unbind_pirq_from_evtch(int pirq) |
|
Line 473 unbind_pirq_from_evtch(int pirq) |
|
int evtchn = pirq_to_evtch[pirq]; |
int evtchn = pirq_to_evtch[pirq]; |
int s = splhigh(); |
int s = splhigh(); |
|
|
simple_lock(&irq_mapping_update_lock); |
simple_lock(&evtchn_lock); |
|
|
evtch_bindcount[evtchn]--; |
evtch_bindcount[evtchn]--; |
if (evtch_bindcount[evtchn] == 0) { |
if (evtch_bindcount[evtchn] == 0) { |
Line 402 unbind_pirq_from_evtch(int pirq) |
|
Line 485 unbind_pirq_from_evtch(int pirq) |
|
pirq_to_evtch[pirq] = -1; |
pirq_to_evtch[pirq] = -1; |
} |
} |
|
|
simple_unlock(&irq_mapping_update_lock); |
simple_unlock(&evtchn_lock); |
splx(s); |
splx(s); |
|
|
return evtchn; |
return evtchn; |
|
|
event_set_handler(int evtch, int (*func)(void *), void *arg, int level, |
event_set_handler(int evtch, int (*func)(void *), void *arg, int level, |
const char *evname) |
const char *evname) |
{ |
{ |
struct cpu_info *ci = &cpu_info_primary; |
struct cpu_info *ci = curcpu(); /* XXX: pass in ci ? */ |
struct evtsource *evts; |
struct evtsource *evts; |
struct intrhand *ih, **ihp; |
struct intrhand *ih, **ihp; |
int s; |
int s; |
Line 517 event_set_handler(int evtch, int (*func) |
|
Line 600 event_set_handler(int evtch, int (*func) |
|
M_DEVBUF, M_WAITOK|M_ZERO); |
M_DEVBUF, M_WAITOK|M_ZERO); |
if (evts == NULL) |
if (evts == NULL) |
panic("can't allocate fixed interrupt source"); |
panic("can't allocate fixed interrupt source"); |
|
|
evts->ev_handlers = ih; |
evts->ev_handlers = ih; |
|
simple_lock_init(&evts->ev_lock); |
evtsource[evtch] = evts; |
evtsource[evtch] = evts; |
if (evname) |
if (evname) |
strncpy(evts->ev_evname, evname, |
strncpy(evts->ev_evname, evname, |
Line 530 event_set_handler(int evtch, int (*func) |
|
Line 615 event_set_handler(int evtch, int (*func) |
|
} else { |
} else { |
evts = evtsource[evtch]; |
evts = evtsource[evtch]; |
/* sort by IPL order, higher first */ |
/* sort by IPL order, higher first */ |
|
simple_lock(&evts->ev_lock); |
for (ihp = &evts->ev_handlers; ; ihp = &((*ihp)->ih_evt_next)) { |
for (ihp = &evts->ev_handlers; ; ihp = &((*ihp)->ih_evt_next)) { |
if ((*ihp)->ih_level < ih->ih_level) { |
if ((*ihp)->ih_level < ih->ih_level) { |
/* insert before *ihp */ |
/* insert before *ihp */ |
Line 542 event_set_handler(int evtch, int (*func) |
|
Line 628 event_set_handler(int evtch, int (*func) |
|
break; |
break; |
} |
} |
} |
} |
|
simple_unlock(&evts->ev_lock); |
} |
} |
|
|
intr_calculatemasks(evts); |
intr_calculatemasks(evts); |
Line 553 event_set_handler(int evtch, int (*func) |
|
Line 640 event_set_handler(int evtch, int (*func) |
|
void |
void |
event_set_iplhandler(struct intrhand *ih, int level) |
event_set_iplhandler(struct intrhand *ih, int level) |
{ |
{ |
struct cpu_info *ci = &cpu_info_primary; |
struct cpu_info *ci = curcpu(); |
struct iplsource *ipls; |
struct iplsource *ipls; |
|
|
if (ci->ci_isources[level] == NULL) { |
if (ci->ci_isources[level] == NULL) { |
Line 579 event_remove_handler(int evtch, int (*fu |
|
Line 666 event_remove_handler(int evtch, int (*fu |
|
struct evtsource *evts; |
struct evtsource *evts; |
struct intrhand *ih; |
struct intrhand *ih; |
struct intrhand **ihp; |
struct intrhand **ihp; |
struct cpu_info *ci = &cpu_info_primary; |
struct cpu_info *ci = curcpu(); |
|
|
evts = evtsource[evtch]; |
evts = evtsource[evtch]; |
if (evts == NULL) |
if (evts == NULL) |
return ENOENT; |
return ENOENT; |
|
|
|
simple_lock(&evts->ev_lock); |
for (ihp = &evts->ev_handlers, ih = evts->ev_handlers; |
for (ihp = &evts->ev_handlers, ih = evts->ev_handlers; |
ih != NULL; |
ih != NULL; |
ihp = &ih->ih_evt_next, ih = ih->ih_evt_next) { |
ihp = &ih->ih_evt_next, ih = ih->ih_evt_next) { |
if (ih->ih_fun == func && ih->ih_arg == arg) |
if (ih->ih_fun == func && ih->ih_arg == arg) |
break; |
break; |
} |
} |
if (ih == NULL) |
if (ih == NULL) { |
|
simple_unlock(&evts->ev_lock); |
return ENOENT; |
return ENOENT; |
|
} |
*ihp = ih->ih_evt_next; |
*ihp = ih->ih_evt_next; |
|
simple_unlock(&evts->ev_lock); |
|
|
ipls = ci->ci_isources[ih->ih_level]; |
ipls = ci->ci_isources[ih->ih_level]; |
for (ihp = &ipls->ipl_handlers, ih = ipls->ipl_handlers; |
for (ihp = &ipls->ipl_handlers, ih = ipls->ipl_handlers; |