2012-10-12 48 views
7

Estoy trabajando en un proyecto que tiene requisitos estrictos de tiempo de arranque. La arquitectura de destino es un procesador basado en IA-32 que se ejecuta en modo protegido de 32 bits. Una de las áreas identificadas que se puede mejorar es que el sistema actual inicializa dinámicamente el IDT del procesador (tabla de descriptores de interrupción). Como no tenemos ningún dispositivo plug-and-play y el sistema es relativamente estático, quiero poder usar un IDT construido estáticamente.IDT estáticamente definido IDT

Sin embargo, esto demuestra ser problemático para el arco IA-32 ya que los descriptores de puerta de interrupción de 8 bytes dividen la dirección ISR. Los 16 bits bajos del ISR aparecen en los primeros 2 bytes del descriptor, otros bits se completan en los siguientes 4 bytes y, finalmente, los últimos 16 bits del ISR aparecen en los últimos 2 bytes.

que quería utilizar una matriz const para definir el IDT y luego simplemente apunte el registro de IDT en ello de esta manera:

typedef struct s_myIdt { 
    unsigned short isrLobits; 
    unsigned short segSelector; 
    unsigned short otherBits; 
    unsigned short isrHibits; 
} myIdtStruct; 

myIdtStruct myIdt[256] = { 
    { (unsigned short)myIsr0, 1, 2, (unsigned short)(myIsr0 >> 16)}, 
    { (unsigned short)myIsr1, 1, 2, (unsigned short)(myIsr1 >> 16)}, 

etc.

Obviamente esto no funcionará, ya que es ilegal hacer esto en C porque myIsr no es constante. Su valor es resuelto por el vinculador (que puede hacer solo una cantidad limitada de matemática) y no por el compilador.

¿Alguna recomendación u otra idea sobre cómo hacer esto?

Gracias,

+0

Mi sugerencia sería asegurarme de que su IDT e ISR están en el mismo módulo (y obviamente que el ISR está cargado en una posición fija) y luego use etiquetas. Intenté hacer esto con GCC pero no me gustó usar la sintaxis '&& myIsr0' fuera de una función y no tengo las habilidades de ensamblaje en línea para declarar el IDT usando la sintaxis' __asm__'. Probablemente solo compile este módulo usando NASM (preferencia personal) con los ISR como stub calls para las funciones C. Esa sería mi sugerencia, aunque definitivamente no puedo decir que soy un experto :) – Justin

+0

Aquí hay un tipo de sugerencia molesta que le doy: use assembly. Cuando utilice funciones de procesador muy específicas o construcciones de nivel extremadamente bajo, llame a algún ensamblaje. Lo hace más fácil. – Linuxios

+0

Este no es mi fuerte; ¿alguien puede explicarme brevemente o señalarme por qué esto es ilegal? – taz

Respuesta

2

Se encontraron con una verruga x86 bien conocido. No creo que el enlazador pueda rellenar la dirección de sus rutinas isr en la forma que se esperaba de la entrada de IDT.

Si se siente ambicioso, podría crear un script de generador IDT que haga algo como este enfoque (basado en Linux). No he probado este esquema y probablemente califica como un truco desagradable de todos modos, así que pise con cuidado.

Paso 1: Escriba un script para ejecutar 'nm' y capture el stdout.

Paso 2: En su secuencia de comandos, analice la salida nm para obtener la dirección de memoria de todas sus rutinas de servicio de interrupción.

Paso 3: Imprima un archivo binario, 'idt.bin' que tiene todos los bytes IDT configurados y listos para la instrucción LIDT. Su secuencia de comandos, obviamente, emite las direcciones ISR en la forma correcta de swizzled.

Paso 4: Convertir su binario en una sección de duende con el objcopy:

objcopy -I binary -O elf32-i386 idt.bin idt.elf

Paso 5: Ahora idt.elf archivo tiene su binario IDT con el símbolo de algo como esto:

> nm idt.elf 
000000000000000a D _binary_idt_bin_end 
000000000000000a A _binary_idt_bin_size 
0000000000000000 D _binary_idt_bin_start 

Paso 6: vuelva a vincular su binario incluyendo idt.elf. En los resquicios de ensamblaje y las secuencias de comandos del vinculador, puede consultar el símbolo _binary_idt_bin_start como base del IDT. Por ejemplo, su secuencia de comandos del enlazador puede colocar el símbolo _binary_idt_bin_start en cualquier dirección que desee.

Tenga cuidado de volver a vincular con la sección IDT no se mueve más en su binario, p. sus rutinas isr Administre esto en su script del enlazador (archivo .ld) colocando el IDT en su propia sección dedicada.

--- --- EDITAR De los comentarios, parece que hay confusión sobre el problema. El x86 32-bit IDT espera que la dirección de la rutina de servicio de interrupción se divida en dos palabras diferentes de 16 bits, así:

 
31   16 15   0 
+---------------+---------------+ 
| Address 31-16 |    | 
+---------------+---------------+ 
|    | Address 15-0 | 
+---------------+---------------+ 

Un ligador es por lo tanto incapaz de plug-in de la dirección de ISR como normales reubicación. Por lo tanto, en el momento del arranque, el software debe construir este formato dividido, lo que ralentiza el tiempo de arranque.

+0

Esta es la conclusión I han llegado a. Un script o algún otro programa tendrá que analizar la tabla de símbolos de la imagen ELF y construir el IDT con swizzled. El archivo de salida binaria es una buena idea. Estaba pensando en generar un archivo C real, pero con el método binario puedo evitar el paso de compilación, que sería una pesadilla teniendo en cuenta cómo funciona nuestro sistema de compilación. – jkayca

+0

El método binario también evita la necesidad de insertar código en su imagen de firmware ya enlazada. – srking

0

Se podría hacer algo como esto:

main.c:

#include <stdint.h> 
#include <stdio.h> 

void isr0(); 

struct idt_entry 
{ 
    uint32_t idt_a; 
    uint32_t idt_b; 
}; 

extern char idt_a_0; 
extern char idt_b_0; 

struct idt_entry idt0 = { 
    (uint32_t)&idt_a_0, 
    (uint32_t)&idt_b_0 
}; 

int main() 
{ 
    printf ("isr0: %08x\n", &isr0); 
    printf ("%08x\n", ((uint16_t*)&idt0)[0]); 
    printf ("%08x\n", ((uint16_t*)&idt0)[1]); 
    printf ("%08x\n", ((uint16_t*)&idt0)[2]); 
    printf ("%08x\n", ((uint16_t*)&idt0)[3]); 

    return 0; 
} 

link.ld:

seg_selector = 1; 
other_bits = 2; 
isr0_lo = isr0 & 0xFFFF; 
isr0_hi = isr0 >> 16; 

idt_a_0 = (seg_selector << 16) | isr0_lo; 
idt_b_0 = (isr0_hi << 16) | other_bits; 

isr0.c:

void isr0() 
{ 
} 

Makefile:

CFLAGS=-m32 
main: main.o isr0.o link.ld 
    gcc -m32 -Wl,link.ld -o [email protected] $^ 
main.o: main.c 
isr0.o: isr0.c 
Cuestiones relacionadas