Sei sulla pagina 1di 44

(C)ULPGC 1

INTERRUPCIONES
HARDWARE EN LINUX
S. Candela

Universidad de Las Palmas de Gran Canaria
(C)ULPGC 2

1. Introduccin

El mecanismo que tienen los usuarios y los
dispositivos para que la unidad de control y
proceso de un computador CPU los atienda es
generar una solicitud de interrupcin.

Cuando la CPU reconoce y estima la
interrupcin (INTA), deja de ejecutar el proceso
en curso y atiende al causante de la interrupcin
ejecutando una funcin especfica llamada
manejador de la interrupcin de quien
produce la interrupcin.
(C)ULPGC 3
clasificacin atendiendo a la fuente
Interrupcion software, se produce cuando un proceso
de usuario solicita un recurso del sistema.

Interrupciones hardware, los dispositivos hardware
requiere la atencin de la CPU para que se ejecute su
manejador.

Excepciones, Cuando dentro de la propia CPU se
produce un evento especial que requiere la ejecucin de
un software que maneje ese evento, por ejemplo una
divisin por cero o un fallo de
(C)ULPGC 4
Las interrupciones hardware son producidas por varias
fuentes: teclado, reloj, impresora, puerto serie, disquete, etc.
Una seal (INT) informa a la CPU que el
dispositivo requiere su atencin.

La CPU reconoce la interrupcin (INTA) y para
el proceso que est ejecutando para atender la
interrupcin.

Cuando la interrupcin termina, la CPU
reanuda la ejecucin en donde fue interrumpida,
pudiendo ejecutar el proceso parado
originalmente o bien otro proceso.
(C)ULPGC 5
hardware especifico para interrumpir
La CPU tiene la entrada INT y la salida INTA
para reconocer la interrupcin.
La placa base del computador utiliza un
hardware programable especial para:
Decodificar las interrupciones.
Colocar en el bus de datos informacin de que
dispositivo interrumpi.
Activar la entrada INT de interrupcin a la CPU.
Proteger a la CPU y aislarla de los dispositivos
que interrumpen.
Proporcionar flexibilidad al diseo del sistema.
Mediante un registro de estado permite o inhibe
las interrupciones en el sistema.
(C)ULPGC 6
Controladores de Interrupciones
Programables PIC





Interrupcin IRQ0 (clock)
IRQ1 (teclado)
IRQ3 (tty 2)
IRQ4 (tty 1)
IRQ5(XT winchester)
IRQ6 (floppy)
Reconocimiento IRQ7 (printer)
interrupcin

IRQ8 (clock de tiempo real)
IRQ9 (direccionamos IRQ2)
IRQ10
IRQ11
IRQ12
IRQ13 (FDU exception)
IRQ14 (AT Winchester)
IRQ15



INT
CPU
INTA



b
u
s

d
e


d
a
t
o
s

INT
ACK
ACK
Controlador
Master
INT
Controlador
Slave
(C)ULPGC 7
IRQ interrupt request
Los IRQ son las notificaciones de las
interrupciones enviadas desde los dispositivos
hardware a la CPU.
Los IRQ se encuentran numerados, y cada
dispositivo hardware se encuentra asociado a
un nmero IRQ.
Los Controladores controlan la prioridad de las
interrupciones. El reloj (en IRQ 0) tiene una
prioridad ms alta que el teclado (IRQ 1).
el IRQ 2 del primer PIC, valida o invalida las
entradas del Segundo PIC (8 a 15).
(C)ULPGC 8

3 Interrupciones hardware en Linux.

Linux proporciona un conjunto de
estructuras de datos y de funciones para
el sistema de manejo de interrupciones.
Linux en la medida de lo posible, tratar
de que sea independiente de la mquina
en la que reside el sistema.
Veamos
1. Estructuras de datos
2. Funciones que las manejan
3. Flujo de una interrupcin

(C)ULPGC 9
3.1 Estructuras de datos
irqaction almacena la direccin de la funcin de
manejo de interrupciones.
hw_interrupt_type contiene las funciones que
manejan un controlador de interrupciones
particular, es dependiente de la arquitectura.
irq_desc vector con una entrada para cada una
de las interrupciones que pueden ser atendidas.
IDT tabla de interrupciones.
GDT tabla global de descriptores.
(C)ULPGC 10
Estructuras de datos
irq_desc_t

status
handler
action
depth

irq_desc[]
hw_interrupt_type

typename
startup()
shutdown()
handle() do_8259A_IRQ()
enable()
disable()


handle_IRQ_event()
handler()
flags
mask
name
dev_id
next

irqaction
next
next
manejador
de
interrupcin
driver
0
1
15
(C)ULPGC 11
3.1.1 irqaction include/linux/interrupt.h
struct irqaction almacena un puntero a la
direccin de la funcin que hay que llevar a
cabo cada vez que una interrupcin se produce.

struct irqaction {
void ( * handler ) (int, void *, struct pt _regs *);
unsigned long flags ;
unsigned long mask;
const char *name;
void * dev_id;
struct irqaction *next;
};
(C)ULPGC 12
Campos de irqaction
handler un puntero a la funcin que atiende a la interrupcin, es
inicializado por el manejador.
flags informacin sobre cmo tratar en ciertas situaciones la
interrupcin. Los valores que puede tomar estn declarados en el
archivo /include/asm-i386/signal.h y son los siguientes:
SA_INTERRUPT Indica que esta interrupcin puede ser interrumpida por
otra.
SA_SAMPLE_RANDOM Esta interrupcin puede ser considerada de
naturaleza aleatoria.
SA_SHIRQ Esta IRQ puede ser compartida por diferentes struct
irqaction
mask Este campo no se usa en la arquitectura i386
name Un nombre asociado con el dispositivo.
dev_id nmero identificador nico grabado por el fabricante.
#define PCI_DEVICE_ID_S3_868 0x8880
#define PCI_DEVICE_ID_S3_928 0x88b0
next puntero que apunta a la prxima struct irqaction en la cola,
slo tiene sentido cuando la IRQ se encuentra compartida.
(C)ULPGC 13

3.1.2 hw_interrupt_type arch/i386/kernel/irq.h

Describe el decodificador de interrupciones y las
funciones que lo manejan a bajo nivel.

struct hw_interrupt_type {
const char * typename;
void (*startup) (unsigned int irq);
void (*shutdown) (unsigned int irq);
void (*handle) (unsigned int irq, struct pt_regs * regs);
void (*enable) (unsigned int irq);
void (* disable) (unsigned int irq);
} ;
(C)ULPGC 14
Campos de hw_interrupt_type
Esta estructura contiene todas las operaciones
especficas de un determinado controlador de
interrupciones
typename Un nombre para el controlador.
startup Permite que una entrada irq pueda
interrumpir.
shutdown Deshabilita una entrada irq.
handle Puntero a la funcin que maneja e
inicializa el decodificador.
enable y disable Igual que startup/shutdown
respectivamente.
(C)ULPGC 15


3.1.3 irq_desc arch/i386/kernel/irq.h


Es un vector de estructuras de tipo irq_desc_t. Por
cada interrupcin hardware del sistema habr un
elemento en el array irq_desc[ ].

typedef struct {
unsigned int status; /* estado, interrupcin en
progreso, inhibida */
struct hw_interrupt_type *handler; /* permite, inhibe,
funciones */
struct irqaction *action; /* lista de irq */
unsigned int depth; /* para interrupciones coincidentes
en la misma irq */
} irq_desc_t
(C)ULPGC 16
Campos de irq_desc
status Estado en el que se encuentra la lnea de interrupcin IRQ:

#define IRQ_INPROGRESS 1 /* IRQ activa, no entrar! */
#define IRQ_DISABLED 2 /*IRQ no permitida, no entrar! */
#define IRQ_PENDING 4 /* IRQ pendiente, reintentar */
#define IRQ_REPLAY 8 /* IRQ reintenta pero todava no atendida */
#define IRQ_AUTODETECT 16 /* IRQ esta siendo auto detectada */
#define IRQ_WAITING 32 /* IRQ no lista para auto deteccin */

handler Un puntero a la estructura hw_interrupt_type, que ser el
controlador que est asociada la IRQ.

action Un puntero a la cabeza de la lista de irqactions. Una irq puede
ser compartida por varios equipos.

depth El nmero de equipos enlazados a esta irq.
(C)ULPGC 17

3.1.4 Tabla descriptora de interrupciones IDT

Es una tabla que junto con la tabla global de descriptores GDT, nos va a llevar a las
rutinas de manejo de interrupciones
hard
excepciones
syscall 0x80
S D A
S D A
S D A
S D A
S D A
S D A
S D A
S D A 255
32
2
1
0
B L
IDTR
IDT
S: selector o indice
D: desplazamiento
A: atributos
B: base
L: lmite
(C)ULPGC 18
3.2 Procedimientos de Inicializacin /arch/i386/kernel/irq.c
3.2.1 init_IRQ().
llamada por el programa de inicio del sistema init start_kernel (), en
init.c Inicializa la tabla IDT
set_intr_gate
init_irq
IDT
INIT
irq_desc
init_ISA_irqs
(C)ULPGC 19
Inicializa el vector de interrupciones irq_desc[0..15],
por medio de init_ISA_irqs().
Inicializa la tabla descriptora de interrupciones IDT con
set_intr_gate().

__initfunc( void init_IRQ (void) ){
int i;
init_ISA_irqs (); /* inicializa el vector irq_desc[0..15] */

/* Para todas la interrupciones Inicializa la tabla IDT.*/
for (i = 0; i < NR_IRQS; i++) {
int vector = FIRST_EXTERNAL_VECTOR + i;
if (vector !=SYSCALL_VECTOR)
set_intr_gate (vector, interrupt[i]);
}
/* interrupt[i] desplazamiento a la primera funcin donde salta
cuando llega la interrupcin, IRQ0xNN_interrupt*/

(C)ULPGC 20
/* pregunta por el primer 8259 */
request_region (0x20,0x20,"pic1");
/* pregunta por el segundo 8259 */
request _region (0xa0, 0x20," pic2" );

/** Inicia el reloj a 100 Hz, ahora ya tenemos un vector valido */
outb_p (0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
outb_p (LATCH & 0xff , 0x40); /* LSB */
outb (LATCH >>8, 0x40); /* MSB */

/* activa entrada irq2 para el 2 8259 en cascada */
setup_x86_irq (2, & irq2 );
/* activa la entrada irq13 fpu error matematico, no se usa */
setup_x86_irq (13, &irq13);
}
(C)ULPGC 21
3.2.2 init_ISA_irqs()

Inicializa el vector irq_desc[0..15], para mquinas con el bus ISA
init_ISA_irqs

irq_desc[]
(C)ULPGC 22
init_ISA_irqs
Rellena la estructura irq_desc encargada de mantener
una relacin del tipo de decodificador con cada una de
las interrupciones
Todas las interrupciones estn inicialmente
deshabilitadas y no tienen ninguna accin asociada.
Las 16 primeras entradas de la estructura son las irq's
correspondientes al sistema de interrupciones hardware
y se rellenan con el manejador de interrupciones i8259A.
El resto sern inicializadas con un manejador que no
hace nada. En un sistema con Bus PCI estas
interrupciones sern modificadas ms adelante.
(C)ULPGC 23
init_ISA_irqs
void init_ISA_irqs (void)
{
int i;
for (i = 0; i <NR.J.RQS; i++) {
irq_desc [i ].status = IRQ_DISABLED;
irq_desc[i].action = 0;
irq_desc[i].depth = 0;
if (i < 16) {
/* 16 viejo estilo INTA-ciclo en interrupciones */
irq_desc [i]. handler = &i8259A_irq_type;
} else {
/* * 'altas' PCI IRQs llenadas por demanda */
irq_desc [i].handler = &no_irq_type;
}
}
}
(C)ULPGC 24
set_intr_gate
Rellena la tabla de descriptores de interrupciones (IDT)
por medio de la macro _set_gate.

void set_intr_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n, 14, 0 ,addr);
}

idt_table es la versin no mapeada de la IDT
el parmetro n va apuntando a las distintas entradas de la IDT
el parmetro *addr es el desplazamiento que le va a llevar a la
funcin IRQxNN_interrupt



(C)ULPGC 25
_set_gate
#define _set_gate(gate_addr, type, dpl, addr)
do {
int __d0, __d1;
__asm__ __volatile__ ("movw %%dx,%%ax\n\t"
"movw % 4, % % dx \ n \ t"
"movl % % eax, % 0 \ n \ t"
"movl % % edx, % 1"
"=m" ( * ( ( long *) (gate_addr) ) ),
"=m" ( * (1+(long *) (gate_addr) ) ), "=&a" (__d0),
"=&d" (__d1)
"i" ( (short ) (0x8000 + (dpl<<13) + (type<<8) ) ),
"3" ( ( char * ) ( addr ) ), "2" (__KERNEL_CS << 16) );
} while (0)

(C)ULPGC 26

3.2.3 request_irq

Los manejadores de dispositivos usan las funcines
request_irq y setup_x86_irq para colocar la direccin
de la funcin manejadora de interrupcin propia.

irqaction
request_irq

setup_x86_irq
MANEJADOR
funcin
manejadora de
la interrupcin
(C)ULPGC 27
request_irq
Crea un nuevo nodo en la lista irqaction con los valores suministrados para
una IRQ
int request_irq (unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
const char * devname,
void *dev_id)
{
int retval;
struct irqaction * action;
/* comprueba los valores de entrada irq y handler */
if (irq >= NR_IRQS)
return -EINVAL;
if (!handler)
return -EINVAL;
/* con la funcin kmalloc asigna memoria dinamica para el nuevo nodo */
action = (struct irqaction *)
kmalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;

(C)ULPGC 28
request_irq
/* llena los campos de action con la nueva funcin
action->handler = handler;
action->flags = irqflags;
action->mask = 0;
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
/* aade el nuevo nodo a la lista con setup_x86_irq
retval = setup_x86_irq(irq, action);

if (retval)
kfree(action);
return retval;
}

(C)ULPGC 29

3.2.4 setup_x86_irq
recibe una estructura irq_action y un nmero de interrupcin y crea la lista
rellenando la estructura irq_desc
int setup_x86_irq (unsigned int irq, struct irqaction * new)
{
int shared = 0;
struct irqaction * old, **p;
unsigned long flags;
/*
* Algunos manejadores como serial.c usan request_irq() frecuentemente,
* hay que tener cuidado de no interferir con un sistema en ejecucin.
*/
if (new->flags & SA_SAMPLE_RANDOM) {
/*
* Esta funcin inicializa el /dev/random y /dev/urandom que
* contienen caracteres aleatorios.
*/
rand_initialize_irq (irq);
}
(C)ULPGC 30
* El siguiente bloque de cdigo ha sido ejecutado indivisiblemente */
spin_lock_irqsave (&irq_controller_lock, flags );
p = &irq_desc[irq].action;
if ((old = *p) != NULL) {/* nodo de la lista no vaco, existe ya una
interrupcin */
/* No pueden compartir interrupcines a menos que estn de
acuerdo */
if (!(old->flags & new->flags & SA_SHIRQ)) {
spin_unlock_irqrestore (&irq_controller_lock,flags); /* verifica
que irq puede compartirse */
return - EBUSY ;
}
/* aade una nueva interrupcin al final de la cola irq
* coloca a uno el flag shared */
do {
p = &old->next ;
old = *p;
} while (old);
shared = 1;
}
setup_x86_irq

(C)ULPGC 31
/* p ahora apunta al ltimo elemento de la lista campo
next.
* si la IRQ va a compartirse o irq_desc[irq].action no esta
compartida. */
*p = new
/* si no se habin establecido acciones en esta irq
inicializar el resto de campos */
if (!shared) {
irq_desc [irq].depth = 0;
irq_desc [irq].status &= ~(IRQ_DISABLED |
IRQ_INPROGRESS);
irq_desc [irq].handler->startup (irq); /* permite
interrumpir por irq */
}
spin_unlock_irqrestore (&irq_controller_lock ,flags);
return 0;
}
setup_x86_irq

(C)ULPGC 32
3.2.5 free_irq
Llamada por los manejadores para quitar un nodo de la lista irqaction,
es la accin inversa de request_irq
void free_irq(unsigned int irq, void *dev_id)
{
struct irqaction * action, **p;
unsigned long flags;
/* Comprueba que irq est en el rango */
if (irq >= NR_IRQS)
return;

spin_lock_irqsave(&irq_controller_lock,flags);
/* Obtiene la entrada correspondiente en irq_desc y comienza el bucle
de acciones */
for (p = &irq_desc[irq].action;
(action = *p) != NULL; p = &action->next) {
/* comprueba el ID del dispositivo */
if (action->dev_id != dev_id)
continue;
(C)ULPGC 33
/* Elimina el elemento de la lista y libera memoria */
*p = action->next;
kfree(action);
/* si la cola tena un solo elemento la entrada irq es desabilitada */
if (!irq_desc[irq].action) {
irq_desc[irq].status |= IRQ_DISABLED;
irq_desc[irq].handler->shutdown(irq);
}
goto out;
}
/* si se ha alcanzado este punto es porque no se ha encontrado en la
lista ningn
* dev_id, y hay un error de liberar una irq.
* Si se encontr el dispositivo, el goto salta este printk
*/
printk("Trying to free free IRQ%d\n",irq);
out:
spin_unlock_irqrestore(&irq_controller_lock,flags);
}
free_irq
(C)ULPGC 34

3.3 Procesado de interrupciones
La funcin set_intr_gate inicializar la IDT,
colocando el desplazamiento que nos lleva al
vector interrupt[] que contiene el cdigo
siguiente:
IRQ 0x ##_interrupt:
pushl 0x ## - 256
jmp common_interrupt

donde ## corresponde con el ndice del vector.

common_interrupt es una funcin en ensamblador
que sencillamente llama a do_IRQ indicndole
cmo hay que retornar de la irq.
(C)ULPGC 35
3.3.1 Flujo general













BUILD_IRQ IRQ0xNN_interrrupt
common_interrupt

SAVE_ALL

ret_from_intr
do_IRQ

irq_desc[irq].handler->handler()
do_8259A_IRQ

action->handler

MANEJADOR


handle_IRQ_event
IDT GDT
+
interrupcin
CPU

IDTR
GDTR
(C)ULPGC 36

3.3.1 Flujo general
1. El decodificador de interrupciones recibe una interrupcin (la
patilla IRQ a nivel alto), este interrumpe a la CPU, que
automticamente mediante la entrada que corresponde a esta
interrupcin y con la ayuda de la IDT y la GDT nos lleva a
IRQ0xNN_interrupt que salta a common_interrupt.

2. common_interrupt despues de guardar el estado del proceso
(save_all) llama a do_irq y le indica que cuando regrese pase el
control a ret_from_intr.

3. do_IRQ llama al cdigo especfico de la arquitectura del
decodificador de interrupciones que en nuestro caso ser
do_8259A_IRQ.

4. do_8259A_IRQ esta funcin desactiva la entrada IRQ que se
est ejecutando, y llama a handle_IRQ_event y a la vuelta vuelve a
activar la interrupcin.
(C)ULPGC 37
3.3.1 Flujo general
5. handle_IRQ_event se encarga de activar las
interrupciones si la interrupcin que se atiende es de
tipo lento, posteriormente llama a las funciones
necesarias para atender la interrupcin action->handler
dentro del manejador, desactiva otra vez las
interrupciones y vuelve a do_irq (pasando por
do_8259A_IRQ).

6. do_IRQ ejecuta cualquier bottom_half que quede
pendiente y devuelve el control a ret_from_intr.

7. ret_from_intr restaura el estado de la CPU y
contina con la ejecucin normal.
(C)ULPGC 38
3.3.2 Flujo paso a paso
IRQ 0x ##_interrupt common_interrupt
IRQ 0x ##_interrupt:
pushl 0x ## - 256
jmp common_interrupt
donde ## corresponde con el ndice del vector.

common_interrupt es una funcin en ensamblador que
llama a do_IRQ
#define BUILD_COMMON_IRQ() \
__asm__( \
"\n" __ALIGN_STR" \ n" \
"common_interrupt: \ n \ t" \
SAVE_ALL \
"pushl $ret_from_intr \ n \ t" \
"jmp "SYMBOL_NAME_STR ( do_IRQ ) );
(C)ULPGC 39
3.3.2 Flujo paso a paso
do_IRQ
asmlinkage void do_IRQ (struct pt _regs regs)
{
/* 0 devuelto por la funcin significa que esta irq est desabilitada o
* esta siendo manejada por otra CPU. */

int irq = regs.orig_eax & 0xff;
int cpu = smp_processor_id ();
kstat.irqs [cpu] [irq]++;
irq_desc [irq].handler->handle (irq, & regs );

/** Dependiendo de una condicin retornada por el manejador de
* la interrupcin ejecutaremos o no el software de bottom half . */
if (1) {
if (bh_active & bh_mask)
do_bottom_half();
}
}
(C)ULPGC 40
do_8259A_IRQ
se encarga de comprobar, de forma atmica, si la interrupcin est
habilitada y en este caso llamar a handle_IRQ_event con la accin
extrada de la tabla irq_desc.

static void do_8259A_IRQ (unsigned int irq, struct pt_regs * regs)
{
struct irqaction * action;
irq_desc_t *desc = irq_desc + irq;
spin_Iock (&irq_controller_lock );
{
unsigned int status;
mask_and_ack_8259A (irq);
status = desc->status & ~ (IRQ_REPLAY I IRQ_WAITING);
action = NULL;
if (! ( status & (IRQ_DISABLED I IRQ_lNPROGRESS))) {
action = desc->action;
status |= IRQ_INPROGRESS;
}
desc->status = status;
}
(C)ULPGC 41
do_8259A_IRQ
spin_unlock ( &irq_controller_lock );
/* Salir si no hay nada que hacer o est desabilitada */
if (! action)
return;

handle_IRQ_event (irq, regs, action);
spin_Iock ( &irq_controller_lock) ;
{
unsigned int status = desc->status & ~IRQ_INPROGRESS;
desc->status = status;
if (! (status & IRQ_DISABLED))
enable_8259A_irq (irq);
}
spin_unlock ( &irq_controller_lock);
}

(C)ULPGC 42
handle_IRQ_event
realiza la llamada a action_handler para cada una de las irq_actions en la
cola de la irq_actions

int handle_IRQ_event (unsigned int irq, struct pt_regs * regs, struct irqaction)
{
int status;
int cpu = smp_processor_id ();
irq_enter (cpu, irq);
status = 1; /* coloca a uno el bit de "bottom halves" */
do {
if (! (action->flags & SA_INTERRUPT))
--sti ();
else
--cli ();
status |= action->flags;
action->handler ( irq, action->dev_id, regs);
action = action->next;
} while (action);
if (status & SABAMPLE-RANDOM)
add_interrupt_randomness (irq);
--cli ();
irq_exit (cpu, irq);
return status;
}
(C)ULPGC 43
3.4 Bottom Halves
El ncleo de Linux define dos clases de
interrupciones, prioritarias (Fast) y secundarias
(Slow)
Cuando se est ejecutando una interrupcin
secundaria esta puede ser interrumpida por una
interrupcin prioritaria.
Cuando se est ejecutando una interrupcin
prioritaria, toda interrupcin tanto prioritaria
como secundaria debe esperar.

(C)ULPGC 44

3.4 Bottom Halves
Los bottom halves (tambin llamados
soft IRQs, es un mecanismo que
posibilita la ejecucin condicional de
partes del cdigo de la funcin
manejadora de la interrupcin.
Cuando se produce una interrupcin se
ejecuta la parte superior la "top half" de la
interrupcin, esta parte del cdigo es la
que indicar si ser necesario ejecutar
ms adelante si se considera necesario la
"bottom half" (mitad inferior) de la misma.

Potrebbero piacerti anche