2012-10-12 18 views
7

Sto lavorando a un progetto che ha requisiti di avvio rigidi. L'architettura mirata è un processore basato su IA-32 in esecuzione in modalità protetta a 32 bit. Una delle aree identificate che possono essere migliorate è che il sistema corrente inizializza dinamicamente l'IDT del processore (tabella dei descrittori degli interrupt). Dal momento che non abbiamo alcun dispositivo plug-and-play e il sistema è relativamente statico, voglio poter usare un IDT costruito staticamente.IDT definito staticamente

Tuttavia, questo risulta essere problematico per l'arco IA-32 poiché i descrittori del gate di interruzione a 8 byte suddividono l'indirizzo ISR. I 16 bit bassi dell'ISR appaiono nei primi 2 byte del descrittore, altri bit riempiono i successivi 4 byte e infine gli ultimi 16 bit dell'ISR appaiono negli ultimi 2 byte.

ho voluto utilizzare un array const per definire l'IDT e poi semplicemente puntare registro IDT in questo modo:

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)}, 

ecc

Ovviamente questo non funziona come è illegale per fare ciò in C perché myIsr non è costante. Il suo valore è risolto dal linker (che può fare solo un numero limitato di calcoli matematici) e non dal compilatore.

Eventuali raccomandazioni o altre idee su come farlo?

Grazie,

+0

Il mio suggerimento sarebbe quello di assicurarsi che i vostri IDT e ISR siano nello stesso modulo (e ovviamente che l'ISR sia caricato in una posizione fissa) e quindi utilizzare le etichette. Ho provato a farlo con GCC, ma non mi piace usare la sintassi '&& myIsr0' al di fuori di una funzione e non ho le abilità di assemblaggio inline per dichiarare l'IDT usando la sintassi' __asm__'. Probabilmente mi piacerebbe semplicemente compilare questo modulo usando NASM (preferenza personale) con gli ISR ​​che sono chiamate stub alle funzioni C. Questo sarebbe il mio suggerimento, anche se non posso assolutamente affermare di essere un esperto :) – Justin

+0

Ecco una sorta di suggerimento fastidioso che vorrei dare: usare l'assemblaggio. Quando si utilizzano caratteristiche del processore molto specifiche o costrutti di livello estremamente basso, chiamare in alcuni assembly. Lo rende più facile. – Linuxios

+0

Questo non è il mio forte; qualcuno può spiegarmi brevemente o indicarmi perché è illegale? – taz

risposta

2

è stato eseguito in un ben noto verruca x86. Non credo che il linker possa riempire l'indirizzo delle tue routine isr nella forma swizzled prevista dalla voce IDT.

Se ti senti ambizioso, puoi creare uno script di build IDT che faccia qualcosa di simile a questo approccio (basato su Linux). Non ho testato questo schema e probabilmente si qualifica comunque come un brutto attacco, quindi calpesti attentamente.

Passaggio 1: scrivere uno script per eseguire "nm" e acquisire lo stdout.

Passaggio 2: nello script, analizzare l'output nm per ottenere l'indirizzo di memoria di tutte le routine di servizio di interrupt.

Passaggio 3: emettere un file binario, 'idt.bin' che ha i byte IDT tutti impostati e pronti per l'istruzione LIDT. Il tuo script emette ovviamente gli indirizzi isr nel modulo swizzled corretto.

Fase 4: Convertire il suo binario non elaborato in una sezione elfo con objcopy:

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

Fase 5: Ora di file idt.elf ha il tuo binario IDT con il simbolo qualcosa di simile:

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

Passo 6: ricollega il tuo binario incluso idt.elf. Negli stub dell'assieme e negli script del linker, è possibile fare riferimento al simbolo _binary_idt_bin_start come base dell'IDT. Ad esempio, lo script linker può posizionare il simbolo _binary_idt_bin_start in qualsiasi indirizzo che ti piace.

Fare attenzione che il ricollegamento con la sezione IDT non si sposta in nessun altro nel proprio file binario, ad es. le tue routine isr. Gestisci questo nel tuo script linker (file .ld) inserendo l'IDT nella sua sezione dedicata.

--- EDIT --- Dai commenti sembra esserci confusione sul problema. X86 32 bit IDT prevede l'indirizzo della routine di servizio di interruzione da dividere in due diverse parole di 16 bit, in questo modo:

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

Un linker è quindi in grado di plug-in indirizzo ISR come un normale delocalizzazione. Quindi, al momento dell'avvio, il software deve costruire questo formato diviso, che rallenta il tempo di avvio.

+0

Questa è la conclusione I sono venuto a. Uno script o qualche altro programma dovrà analizzare la tabella dei simboli dell'immagine ELF e costruire l'IDT swizzled. Il file di output binario è una buona idea. Stavo pensando di generare un vero file C ma con il metodo binario posso evitare la fase di compilazione che sarebbe un po 'un incubo considerando come funziona il nostro sistema di compilazione. – jkayca

+0

Il metodo binario evita inoltre la necessità di inserire codice nell'immagine firmware già collegata. – srking

0

Si potrebbe fare qualcosa di simile:

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