Embeddedxen中断申请
先看xen的中断申请
申请中断离不开request_irq和setup_irq
xen只申请了两个irq一个是timer的,另一个是console.
xen/arch/arm/mach-pxa/time.c
void __init xen_pxa_timer_init(void)
{
unsigned long clock_tick_rate;
OIER = 0;
OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3;
if (cpu_is_pxa25x())
clock_tick_rate = 3686400;
else if (machine_is_mainstone())
clock_tick_rate = 3249600;
else
clock_tick_rate = 3250000;
set_oscr2ns_scale(clock_tick_rate);
ckevt_pxa_osmr0.mult =
div_sc(clock_tick_rate, NSEC_PER_SEC, ckevt_pxa_osmr0.shift);
ckevt_pxa_osmr0.max_delta_ns =
clockevent_delta2ns(0x7fffffff, &ckevt_pxa_osmr0);
ckevt_pxa_osmr0.min_delta_ns =
clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_pxa_osmr0) + 1;
cksrc_pxa_oscr0.mult =
clocksource_hz2mult(clock_tick_rate, cksrc_pxa_oscr0.shift);
setup_irq(IRQ_OST0, &pxa_ost0_irq);
/* timebase_freq = CLOCK_TICK_RATE: source clock rate in Hz */
//timebase_freq = 1000000LL;
timebase_freq = CLOCK_TICK_RATE;
system_timer_clocksource = &cksrc_pxa_oscr0;
system_timer_clockevent = &ckevt_pxa_osmr0;
ckevt_pxa_osmr0.set_mode(CLOCK_EVT_MODE_PERIODIC, &ckevt_pxa_osmr0);
}
所安装的irqaction是pxa_ost0_irq,xen/arch/arm/mach-pxa/time.c
static struct irqaction pxa_ost0_irq = {
.name = "ost0",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = pxa_ost0_interrupt,
.dev_id = &ckevt_pxa_osmr0,
};
xen/arch/arm/hypervisor/irq.c
int setup_irq(unsigned int irq, struct irqaction *new)
{
int rc = 0;
int shared = 0;
struct irqaction *old, **p;
unsigned long flags;
struct irqdesc *desc;
if(irq >= NR_IRQS) {
printk("BAD IRQ = %d\n", irq);
while(1);
}
desc = get_irq_descriptor(irq);
spin_lock_irqsave(&desc->lock, flags);
p = &desc->action;
if ((old = *p) != NULL) {
/* Can't share interrupts unless both agree to */
if (!(old->flags & new->flags & IRQF_SHARABLE) ||
((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
printk("IRQ sharing failed.\n");
rc = -EBUSY;
goto out;
}
/* add new interrupt at end of irq queue */
do {
p = &old->next;
old = *p;
} while (old);
shared = 1;
}
*p = new;
if (!shared) {
if(new->flags & IRQF_TRIGGER_MASK) {
if(desc->chip->set_type) {
desc->chip->set_type(irq, new->flags & IRQF_TRIGGER_MASK);
} else {
printk("No set_irq_type function for irq %d\n", irq);
rc = -1;
goto out;
}
}
desc->status &= ~(IRQ_AUTO_DETECT | IRQ_WAITING | IRQ_IN_PROGRESS);
if(!(desc->status & IRQ_NO_AUTO_ENABLE)) {
desc->disable_depth = 0;
desc->status &= ~IRQ_DISABLED;
desc->chip->unmask(irq);
} else {
desc->disable_depth = 1;
}
}
desc->flags &= ~IRQF_GUEST_BOUND;
out:
spin_unlock_irqrestore(&desc->lock, flags);
return rc;
}
setup_irq timer后,就开始setup_irq console了。
console的设置稍微复杂了一些:
在__start_xen中:xen/arch/arm/hypersivor/setup.c
pxaserial.baud = 115200;
pxaserial.data_bits = 8;
pxaserial.parity = 'n';
pxaserial.stop_bits = 1;
pxaserial.io_base = 1;
pxaserial.irq = IRQ_FFUART;
//pxaserial.io_base = 0x3f8;
pxaserial_init(0, &pxaserial);
pxaserial的结构体:xen/drivers/char/pxa.c
static struct pxaserial {int baud, data_bits, parity, stop_bits, irq;unsigned long io_base; /* I/O port or memory-mapped I/O address. */char *remapped_io_base; /* Remapped virtual address of mmap I/O. *//* UART with IRQ line: interrupt-driven I/O. */struct irqaction irqaction;/* UART with no IRQ line: periodically-polled I/O. */struct timer timer;unsigned int timeout_ms;} pxaserial_com[2] = { { 0 }, { 0 } };
pxaserial_init xen/drivers/char/pxa.cvoid __init pxaserial_init(int index, struct pxaserial_defaults *defaults){struct pxaserial *uart = &pxaserial_com[index];if ( (index < 0) || (index> 1) )return;if ( defaults != NULL ){uart->baud = defaults->baud;uart->data_bits = defaults->data_bits;uart->parity = parse_parity_char(defaults->parity);uart->stop_bits = defaults->stop_bits;uart->irq = defaults->irq;uart->io_base = defaults->io_base;}pxaserial_parse_port_config(uart, index);}
---pxaserial_parse_port_config xen/drivers/char/pxa.cstatic void __init pxaserial_parse_port_config(struct pxaserial *uart, int index){int baud;const char *conf = ((index == 0) ? opt_com1 : opt_com2);/* (LSU) Defined in xen/lib/vsprintf.c */extern unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base);/* No user-specified configuration? */if ( (conf == NULL) || (*conf == '\0') ){/* Some platforms may automatically probe the UART configuartion. */if ( uart->baud != 0 )goto config_parsed;return;}if ( strncmp(conf, "auto", 4) == 0 ){uart->baud = BAUD_AUTO;conf += 4;}else if ( (baud = simple_strtoul(conf, (char **) &conf, 10)) != 0 )uart->baud = baud;if ( *conf != ',' )goto config_parsed;conf++;uart->data_bits = simple_strtoul(conf, (char **) &conf, 10);uart->parity = parse_parity_char(*conf);conf++;uart->stop_bits = simple_strtoul(conf,(char **) &conf, 10);if ( *conf == ',' ){conf++;uart->io_base = simple_strtoul(conf, (char **) &conf, 0);if ( *conf == ',' ){conf++;uart->irq = simple_strtoul(conf, (char **) &conf, 10);}}config_parsed:/* Sanity checks. */if ( (uart->baud != BAUD_AUTO) && ((uart->baud < 1200) || (uart->baud> 115200)) )PARSE_ERR("Baud rate %d outside supported range.", uart->baud);if ( (uart->data_bits < 5) || (uart->data_bits> 8) )PARSE_ERR("%d data bits are unsupported.", uart->data_bits);if ( (uart->stop_bits < 1) || (uart->stop_bits> 2) )PARSE_ERR("%d stop bits are unsupported.", uart->stop_bits);if ( uart->io_base == 0 )PARSE_ERR("I/O base address must be specified.");/* Register with generic serial driver. */serial_register_uart(0, &pxaserial_driver, uart);}
上面的这个pxaserial_parse_port_config函数有点长,不过,除了最后一句有用外,其他的都在扯淡。特别是中间那一大段完全没有被执行,所以不要有压力哈。
serial_register_uart xen/drivers/char/serial.cvoid __init serial_register_uart(int idx, struct uart_driver *driver, void *uart){/* Store UART-specific info. */com[idx].driver = driver;com[idx].uart = uart;/* Default is no transmit FIFO. */com[idx].tx_fifo_size = 1;}
其实这个函数不再是重点了,重点的是其中的值:
我们看一下,com[0]的类型是:struct serial_port {/* Uart-driver parameters. */struct uart_driver *driver;void *uart;/* Number of characters the port can hold for transmit. */int tx_fifo_size;/* Transmit data buffer (interrupt-driven uart). */char *txbuf;unsigned int txbufp, txbufc;/* Force synchronous transmit. */int sync;/* Receiver callback functions (asynchronous receivers). */serial_rx_fn rx_lo, rx_hi, rx;/* Receive data buffer (polling receivers). */char rxbuf[SERIAL_RXBUFSZ];unsigned int rxbufp, rxbufc;/* Serial I/O is concurrency-safe. */spinlock_t rx_lock, tx_lock;};
也就是说,设置了com[0]的两个成员变量,一个是driver,另一个是uart.我们在回头看一下pxaserial_parse_port_config(struct pxaserial *uart, int index),其实这个函数调用serial_register_uart(0, &pxaserial_driver, uart)的并不优雅,它将原本是index确定为了0.不过对于本项目显然是没有问题的。可以看看linux是怎样实现的。
在函数serial_register_uart里面讲3个成员变量定以后,就剩下其他的小东西了。这些东西紧接着就定义了,不要急。我们先来看一下struct uart_driver *driver=&pxaserial_driver.
改变定义在xen/drivers/char/pxa.c文件中static struct uart_driver pxaserial_driver = {.init_preirq = pxa_initpreirq,.putc = pxaserial_putc,.getc = pxaserial_getc,.irq = pxaserial_irq,.init_postirq = pxaserial_init_postirq,.endboot = NULL,.tx_empty = serial_pxa_tx_empty,};
以上分别定义了struct pxaserial类型的pxaserial,定义了struct uart_driver类型的pxaserial_driver.然后将它们赋值给了struct serial_port类型的com[0].
我们回过头来看看,实际上,并没有定义struct pxaserial pxaserial的全部成员,比如irqaction,比如timer.也许是以后还会有。我们需要耐心。
好,这里已经定义好了serial_port的3个重要的成员变量了,革命尚未成功,同志仍需努力!
返回到__start_xen的主程序,接下来开始执行serial_init_preirq(void);void __init serial_init_preirq(void){int i;for ( i = 0; i < ARRAY_SIZE(com); i++ )if ( com[i].driver && com[i].driver->init_preirq )com[i].driver->init_preirq(&com[i]);}可以看到,实际上执行的是pxa_initpreirq(&com[0]),com[1]没有初始化,因此com[1]并没有被执行。/* from startup...*//* (LSU) Applied awaited signature *///static int pxa_initpreirq(struct serial_port *port) {static void pxa_initpreirq(struct serial_port *port) {struct uart_pxa_port *up = &serial_pxa_port;unsigned long flags;// int retval;if (up->line == 3) /* HWUART */up->mcr |= UART_MCR_AFE;elseup->mcr = 0;/** Clear the FIFO buffers and disable them.* (they will be reenabled in set_termios())*/serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR| UART_FCR_CLEAR_XMIT);serial_out(up, UART_FCR, 0);/** Clear the interrupt registers.*/(void) serial_in(up, UART_LSR);(void) serial_in(up, UART_RX);(void) serial_in(up, UART_IIR);(void) serial_in(up, UART_MSR);/** Now, initialize the UART*/serial_out(up, UART_LCR, UART_LCR_WLEN8);spin_lock_irqsave(&up->lock, flags);up->mctrl |= TIOCM_OUT2;serial_pxa_set_mctrl(port, up->mctrl);spin_unlock_irqrestore(&up->lock, flags);//return 0;}
只里面又包含了已经被定义好的东东 xen/drivers/char/pxa.cstatic struct uart_pxa_port serial_pxa_port = {.name = "FFUART",.cken = CKEN6_FFUART,.membase = (void *)&FFUART,.mapbase = __PREG(FFUART),.irq = IRQ_FFUART,.uartclk = 921600 * 16,.line = 0,};struct uart_pxa_port {spinlock_t lock;unsigned char ier;unsigned char lcr;unsigned char mcr;unsigned int mctrl;unsigned int lsr_break_flag;unsigned int cken;int line;char *name;unsigned long iobase; /* io base address */void __iomem *membase; /* ioremap cookie or NULL */unsigned long mapbase; /* resource base */unsigned int irq; /* interrupt number */unsigned int uartclk; /* UART clock rate */unsigned char regshift; /* register shift */unsigned char iotype; /* UPIO_* */unsigned char hub6;upf_t flags; /* UPF_* flags */};
从上面看来,应该是一些硬件参数信息了。不过和pxaserial那个结构体似乎有某种联系。
还是看那个函数,有用的只有这么一行:serial_pxa_set_mctrl(port, up->mctrl);
我们进去看看:static void serial_pxa_set_mctrl(struct serial_port *port, unsigned int mctrl) {struct uart_pxa_port *up = &serial_pxa_port;unsigned char mcr = 0;if (mctrl & TIOCM_RTS)mcr |= UART_MCR_RTS;if (mctrl & TIOCM_DTR)mcr |= UART_MCR_DTR;if (mctrl & TIOCM_OUT1)mcr |= UART_MCR_OUT1;if (mctrl & TIOCM_OUT2)mcr |= UART_MCR_OUT2;if (mctrl & TIOCM_LOOP)mcr |= UART_MCR_LOOP;mcr |= up->mcr;serial_out(up, UART_MCR, mcr);}
晕倒啊,将port传进来有意义么?根本就没有用到port,还是用的原来的serial_pxa_port
然后就是serial_out(up,UART_MCR,mcr).static inline void serial_out(struct uart_pxa_port *up, int offset, int value){offset <<= 2;writel(value, up->port.membase + offset);}
drivers/serial/pxa.cstruct uart_pxa_port {struct uart_port port;unsigned char ier;unsigned char lcr;unsigned char mcr;unsigned int lsr_break_flag;unsigned int cken;char *name;};
这里的uart_portstruct uart_port {spinlock_t lock; /* port lock */unsigned int iobase; /* in/out[bwl] */unsigned char __iomem *membase; /* read/write[bwl] */unsigned int irq; /* irq number */unsigned int uartclk; /* base uart clock */unsigned int fifosize; /* tx fifo size */unsigned char x_char; /* xon/xoff char */unsigned char regshift; /* reg offset shift */unsigned char iotype; /* io access style */unsigned char unused1;#define UPIO_PORT (0)#define UPIO_HUB6 (1)#define UPIO_MEM (2)#define UPIO_MEM32 (3)#define UPIO_AU (4) /* Au1x00 type IO */#define UPIO_TSI (5) /* Tsi108/109 type IO */unsigned int read_status_mask; /* driver specific */unsigned int ignore_status_mask; /* driver specific */struct uart_info *info; /* pointer to parent info */struct uart_icount icount; /* statistics */struct console *cons; /* struct console, if any */#ifdef CONFIG_SERIAL_CORE_CONSOLEunsigned long sysrq; /* sysrq timeout */#endifupf_t flags;#define UPF_FOURPORT ((__force upf_t) (1 << 1))#define UPF_SAK ((__force upf_t) (1 << 2))#define UPF_SPD_MASK ((__force upf_t) (0x1030))#define UPF_SPD_HI ((__force upf_t) (0x0010))#define UPF_SPD_VHI ((__force upf_t) (0x0020))#define UPF_SPD_CUST ((__force upf_t) (0x0030))#define UPF_SPD_SHI ((__force upf_t) (0x1000))#define UPF_SPD_WARP ((__force upf_t) (0x1010))#define UPF_SKIP_TEST ((__force upf_t) (1 << 6))#define UPF_AUTO_IRQ ((__force upf_t) (1 << 7))#define UPF_HARDPPS_CD ((__force upf_t) (1 << 11))#define UPF_LOW_LATENCY ((__force upf_t) (1 << 13))#define UPF_BUGGY_UART ((__force upf_t) (1 << 14))#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))#define UPF_DEAD ((__force upf_t) (1 << 30))#define UPF_IOREMAP ((__force upf_t) (1 << 31))#define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))unsigned int mctrl; /* current modem ctrl settings */unsigned int timeout; /* character-based timeout */unsigned int type; /* port type */const struct uart_ops *ops;unsigned int custom_divisor;unsigned int line; /* port index */unsigned long mapbase; /* for ioremap */struct device *dev; /* parent device */unsigned char hub6; /* this should be in the 8250 driver */unsigned char unused[3];};
有这么长。。。
好了,搞这这么久,实际上根本与Port屁事不关。我擦~~~
回到__start_xen的主程序,继续我们未完成的千秋大业。
init_console();void __init init_console(void){extern void add_taint(unsigned flag);char *p;/* Where should console output go? */for ( p = opt_console; p != NULL; p = strchr(p, ',') ){if ( *p == ',' )p++;if ( strncmp(p, "com", 3) == 0 )sercon_handle = serial_parse_handle(p);else if ( strncmp(p, "vga", 3) == 0 )vga_init();}serial_set_rx_handler(sercon_handle, serial_rx);/* HELLO WORLD --- start-of-day banner text. */printk(xen_banner());printk(" http://www.cl.cam.ac.uk/netos/xen\n");printk(" University of Cambridge Computer Laboratory\n\n");printk(" Xen version %d.%d%s (%s@%s) (%s) %s\n",xen_major_version(), xen_minor_version(), xen_extra_version(),xen_compile_by(), xen_compile_domain(),xen_compiler(), xen_compile_date());printk(" EmbeddedXEN/PENAR Project - Reconfigurable Embedded Digital System (REDS) Institute from HEIG-VD/Switzerland\n");printk(" http://reds.heig-vd.ch\n\n");printk(" Latest ChangeSet: %s\n\n", xen_changeset());set_printk_prefix("(XEN) ");if ( opt_sync_console ){serial_start_sync(sercon_handle);add_taint(TAINT_SYNC_CONSOLE);printk("Console output is synchronous.\n");}}
这里面有一句挺重要:
serial_set_rx_handler(sercon_handle, serial_rx);void __init serial_set_rx_handler(int handle, serial_rx_fn fn){struct serial_port *port = &com[handle & SERHND_IDX];unsigned long flags;if ( handle == -1 )return;spin_lock_irqsave(&port->rx_lock, flags);if ( port->rx != NULL )goto fail;if ( handle & SERHND_LO ){if ( port->rx_lo != NULL )goto fail;port->rx_lo = fn;}else if ( handle & SERHND_HI ){if ( port->rx_hi != NULL )goto fail;port->rx_hi = fn;}else{if ( (port->rx_hi != NULL) || (port->rx_lo != NULL) )goto fail;port->rx = fn;}spin_unlock_irqrestore(&port->rx_lock, flags);return;fail:spin_unlock_irqrestore(&port->rx_lock, flags);printk("ERROR: Conflicting receive handlers for COM%d\n",handle & SERHND_IDX);}
这里将port的成员变量rx_lo,rx_hi,rx_rx全部定位了fn,也就是serial_rx
然后在start_of_day()中,com实际上才开始注册了。在start_of_day中
serial_init_postirq();void __init serial_init_postirq(void){int i;for ( i = 0; i < ARRAY_SIZE(com); i++ )if ( com[i].driver && com[i].driver->init_postirq )com[i].driver->init_postirq(&com[i]);}
实际上是pxaserial_init_postirq.--这在前面赋值的时候,已经被赋值了。xen/drivers/char/pxa.cstatic void __init pxaserial_init_postirq(struct serial_port *port){struct pxaserial *uart = port->uart;struct uart_pxa_port *up = &serial_pxa_port;int rc;//int bits;if ( uart->irq < 0 )return;serial_async_transmit(port);/* We do not consider polling mode (DRE) */uart->irqaction.handler = pxaserial_interrupt;uart->irqaction.name = "pxaserial";uart->irqaction.dev_id = port;if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 )printk("ERROR: Failed to allocate ns16550 IRQ %d\n", uart->irq);/** Finally, enable interrupts. Note: Modem status interrupts* are set via set_termios(), which will be occurring imminently* anyway, so we don't enable them here.*/up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;serial_out(up, UART_IER, up->ier);/** And clear the interrupt registers again for luck.*/(void) serial_in(up, UART_LSR);(void) serial_in(up, UART_RX);(void) serial_in(up, UART_IIR);(void) serial_in(up, UART_MSR);}
看到uart->irqaction了吧,被赋值了,呵呵,终于被赋值了,不过timer还没有被赋值,没关系,我们以后再看,至此,本文基本上也就完成一半了。
现在我们来看一下DOM方面的中断申请,不例外,肯定是用reqeust_irq和setup_irq.我们先来看一下request_irq:xen_guest/core/evtchn.c
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char * devname, void *dev_id){struct irqaction * action;int retval;
/** Sanity-check: shared interrupts must pass in a real dev-ID,* otherwise we'll have trouble later trying to figure out* which interrupt is which (messes up the interrupt freeing* logic etc).*/if ((irqflags & SA_SHIRQ) && !dev_id)return -EINVAL;if (irq >= NR_IRQS)return -EINVAL;if (!handler)return -EINVAL;
action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);if (!action)return -ENOMEM;
action->handler = handler;action->flags = irqflags;cpus_clear(action->mask);action->name = devname;action->next = NULL;action->dev_id = dev_id;
retval = setup_irq(irq, action);
if (retval)kfree(action);
return retval;}
setup_irq()xen_guest/core/evtchn.c
int setup_irq(unsigned int irq, struct irqaction * new)
{
struct evtchn_desc *desc = evtchn_desc + irq;
struct irqaction *old, **p;
unsigned long flags;
int shared = 0;
// the following should be commented: jyhwang 2007 July 15
// printk("irq=%d, NR_IRQS=%d\n", irq, NR_IRQS);
if (irq >= NR_IRQS)
return -EINVAL;
if (desc->handler == NULL)
return -ENOSYS;
/*
* Some drivers like serial.c use request_irq() heavily,
* so we have to be careful not to interfere with a
* running system.
*/
if (new->flags & SA_SAMPLE_RANDOM) {
/*
* This function might sleep, we want to call it first,
* outside of the atomic block.
* Yes, this might clear the entropy pool if the wrong
* driver is attempted to be loaded, without actually
* installing a new handler, but is this really a problem,
* only the sysadmin is able to do this.
*/
rand_initialize_irq(irq);
}
/*
* The following block of code has to be executed atomically
*/
spin_lock_irqsave(&desc->lock,flags);
p = &desc->action;
if ((old = *p) != NULL) {
/* Can't share interrupts unless both agree to */
if (!(old->flags & new->flags & SA_SHIRQ)) {
spin_unlock_irqrestore(&desc->lock,flags);
return -EBUSY;
}
/* add new interrupt at end of irq queue */
do {
p = &old->next;
old = *p;
} while (old);
shared = 1;
}
*p = new;//action赋值给evtchn_desc
if (!shared) {
desc->depth = 0;
desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT |
IRQ_WAITING | IRQ_INPROGRESS);
if (desc->handler->startup)
desc->handler->startup(irq);
else
desc->handler->enable(irq);
}
spin_unlock_irqrestore(&desc->lock,flags);
new->irq = irq;
new->dir = NULL;
return 0;
}
看到setup_irq,似乎与Linux没有什么太大的不同,可是这也正是移植的妙处,总是要尽量的和原来的系统兼容,对吧。
我们来看看desc->handler->startup(irq);这句吧,这才是核心。我们知道在DOM进行初始化的时候,是怎么初始化呢?如果是pirq的话,分配的handler是pirq_type,这个handler的startup是被startup_pirq()实例化的。那么我们就来看看startup_pirq吧:
#define probing_irq(_irq) (evtchn_desc[(_irq)].action == NULL)
static unsigned int startup_pirq(unsigned int irq){// evtchn_op_t op = { .cmd = EVTCHNOP_bind_pirq };evtchn_bind_pirq_t op;int evtchn = evtchn_from_irq(irq), ret = 0;
if (VALID_EVTCHN(evtchn)) {goto out;}
op.pirq = irq;/* NB. We are happy to share unless we are probing. */op.flags = probing_irq(irq) ? 0 : BIND_PIRQ__WILL_SHARE;
ret = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &op);if (ret != 0) {if (!probing_irq(irq))printk(KERN_INFO "Failed to obtain physical IRQ %d\n", irq);return 0;}evtchn = op.port;
pirq_query_unmask(irq_to_pirq(irq));
bind_evtchn_to_cpu(evtchn, 0);evtchn_to_irq[evtchn] = irq;irq_info[irq] = mk_irq_info(IRQT_PIRQ, irq, evtchn);
out:unmask_evtchn(evtchn);pirq_unmask_notify(irq_to_pirq(irq));
return 0;}
由于我们现在工作于DOMU的内核中,如果在x86架构中的话,是在ring1态,但这是在arm架构中,因此仍然是在ring3。不过,如果要申请进入ring0,也要进行HYPERVISOR_event_channel_opxen/common/event_channel.clong do_event_channel_op(int cmd, XEN_GUEST_HANDLE(void) arg){...case EVTCHNOP_bind_pirq: {struct evtchn_bind_pirq bind_pirq;if ( copy_from_guest(&bind_pirq, arg, 1) != 0 )return -EFAULT;rc = evtchn_bind_pirq(&bind_pirq);if ( (rc == 0) && (copy_to_guest(arg, &bind_pirq, 1) != 0) )rc = -EFAULT; /* Cleaning up here would be a mess! */break;}...}evtchn_bind_pirq(&bind_pirq);xen/common/event_channel.c
static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind){struct evtchn *chn;struct domain *d = current->domain;int port, pirq = bind->pirq;long rc;
if ( (pirq < 0) || (pirq >= ARRAY_SIZE(d->pirq_to_evtchn)) )return -EINVAL;
if ( !irq_access_permitted(d, pirq) )return -EPERM;
spin_lock(&d->evtchn_lock);
if ( d->pirq_to_evtchn[pirq] != 0 )ERROR_EXIT(-EEXIST);
if ( (port = get_free_port(d)) < 0 )//得到evtchn号ERROR_EXIT(port);
chn = evtchn_from_port(d, port);//从evtchn号得到chn数据结构
d->pirq_to_evtchn[pirq] = port;rc = pirq_guest_bind(d->vcpu[0], pirq, !!(bind->flags & BIND_PIRQ__WILL_SHARE));if ( rc != 0 ){d->pirq_to_evtchn[pirq] = 0;goto out;}
chn->state = ECS_PIRQ;//充实这个数据结构chn->u.pirq = pirq;//充实
bind->port = port;//充实bind,要带回去的
out:spin_unlock(&d->evtchn_lock);return rc;}port与pirqport属于DOM的,pirq属于系统的。当irq来了以后,实际上就是pirq,找到port,然后找到chn,然后就设定vcpu响应的Port了.(这些在中断响应文章中都有叙述)也就是说xen->dom的interface是通过port来进行的,而在申请的时候,是通过结构体evtchn_bind_pirq
void send_guest_pirq(struct domain *d, int pirq){int port = d->pirq_to_evtchn[pirq];struct evtchn *chn;ASSERT(port != 0);chn = evtchn_from_port(d, port);evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);}然后当DOM执行时,evtchn_do_upcallasmlinkage void evtchn_do_upcall(struct pt_regs *regs){unsigned long l1, l2;unsigned int l1i, l2i, port;int irq, cpu = smp_processor_id();shared_info_t *s = HYPERVISOR_shared_info;vcpu_info_t *vcpu_info = &s->vcpu_info[cpu];retry:xchg(&vcpu_info->evtchn_upcall_pending,0);l1 = xchg(&vcpu_info->evtchn_pending_sel, 0);while (l1 != 0) {l1i = __ffs(l1);l1 &= ~(1UL << l1i);while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {l2i = __ffs(l2);port = (l1i * BITS_PER_LONG) + l2i;if ((irq = evtchn_to_irq[port]) != -1) { //evtchn号转化为irq号irq_enter();do_IRQ(irq, regs); //guest分发中断函数irq_exit();} else {evtchn_device_upcall(port);}}}if(vcpu_info->evtchn_upcall_pending) {//当有多个中断的时候goto retry;}}好了,这部分的回顾,就到此打住。
int pirq_guest_bind(struct vcpu *v, int irq, int will_share){struct irqdesc *desc;irq_guest_action_t *action;unsigned long flags;int rc = 0;
if ( (irq < 0) || (irq >= NR_IRQS) )return -EINVAL;
desc = get_irq_descriptor(irq);
spin_lock_irqsave(&desc->lock, flags);
action = (irq_guest_action_t *)desc->action;//
if (!(desc->flags & IRQF_GUEST_BOUND)) {if (desc->action != NULL ) {printk("Cannot bind IRQ %d to guest. In use by %s.\n",(int)irq, desc->action->name);rc = -EBUSY;goto out;}//:
action = xmalloc(irq_guest_action_t);if ((desc->action = (struct irqaction *)action) == NULL ) {printk("Cannot bind IRQ %d to guest. Out of memory.\n", irq);rc = -ENOMEM;goto out;}
action->shareable = 1;action->nr_guests = 0;action->in_flight = 0;action->ack_type = pirq_ack_type(irq);
desc->disable_depth = 0;desc->flags |= IRQF_GUEST_BOUND;if(will_share) {desc->flags |= IRQF_SHARABLE;}
desc->chip->unmask(irq);
} else if ( !will_share || !action->shareable ) {printk("Cannot bind IRQ %d to guest. Will not share with others.\n", irq);rc = -EBUSY;goto out;}
if ( action->nr_guests == IRQ_MAX_GUESTS ) {printk("Cannot bind IRQ %d to guest. Already at max share.\n", irq);rc = -EBUSY;goto out;}
action->guest[action->nr_guests++] = v->domain;
out:
spin_unlock_irqrestore(&desc->lock, flags);
return rc;}在上面的这个pirq_guest_bind需要注意的是action的初始化。这里的action和domu的action是两个概念。不是同一回事,一个是irqdesc,一个是evtchn_desc_t。也就是说,一个是中断描述符,另一个是事件通道描述符。当中断由下往上传输的时候,先有irqdesc工作,然后,交给DOMU的时候,evtchn_desc_t开始工作。我们看到,在request_irq中已经将action实例化了因此,在 pirq_guest_bind(struct vcpu *v, int irq, int will_share)中,直接更改action->guest[acton->nr_guests++]=v->domain,然后返回。好了,接下来,我们会分析一个实例,来看一下,具体是怎样利用接口request_irq()申请中断.
评论
发表评论