2012-10-12 16 views
7

Ich arbeite an einem Projekt mit engen Boot-Zeitanforderungen. Die Zielarchitektur ist ein IA-32-basierter Prozessor, der im 32-Bit-geschützten Modus läuft. Einer der identifizierten Bereiche, der verbessert werden kann, besteht darin, dass das aktuelle System die IDT (Interrupt-Deskriptor-Tabelle) des Prozessors dynamisch initialisiert. Da wir keine Plug-and-Play-Geräte haben und das System relativ statisch ist, möchte ich ein statisch aufgebautes IDT verwenden können.Statisch definiert IDT

Dies erweist sich jedoch als problematisch für den IA-32-Arch, da die 8-Byte-Interrupt-Gate-Deskriptoren die ISR-Adresse aufteilen. Die niedrigen 16 Bits des ISR erscheinen in den ersten 2 Bytes des Deskriptors, einige andere Bits füllen die nächsten 4 Bytes aus und dann erscheinen schließlich die letzten 16 Bits des ISR in den letzten 2 Bytes.

Ich wollte einen const-Array verwenden, um den IDT und dann einfach zeige das IDT-Register an, sie zu definieren, wie so:

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

usw.

Offensichtlich wird dies nicht funktionieren, da es illegal ist, um dies in C zu tun, weil myIsr nicht konstant ist. Sein Wert wird durch den Linker (der nur eine begrenzte Menge an Mathe ausführen kann) und nicht durch den Compiler aufgelöst.

Alle Empfehlungen oder andere Ideen, wie dies zu tun?

Danke,

+0

Mein Vorschlag wäre, sicherzustellen, dass Ihre IDT und ISRs im selben Modul sind (und natürlich, dass der ISR an einer festen Position geladen wird) und dann Etiketten verwenden. Ich habe versucht, dies mit GCC zu tun, aber es nicht wie mit der '&& myIsr0' Syntax außerhalb einer Funktion und ich habe nicht die Inline-Assembler-Fähigkeiten, um die IDT mit' __asm__' Syntax zu erklären. Ich würde wahrscheinlich nur dieses eine Modul mit NASM (persönliche Präferenz) kompilieren, wobei die ISRs Stub-Aufrufe an C-Funktionen sind. Das wäre mein Vorschlag, obwohl ich kann auf jeden Fall nicht behaupten, ein Experte zu sein :) – Justin

+0

Hier ist eine Art ärgerlich Vorschlag, den ich geben würde: Verwendung Montag. Wenn Sie sehr spezifische Prozessor-Features oder extrem niedrige Level-Konstrukte verwenden, rufen Sie eine Assembly auf. Es macht es einfacher. – Linuxios

+0

Das ist nicht meine Stärke; Kann mir jemand kurz erklären oder wieso das illegal ist? – taz

Antwort

2

Sie lief in eine gut x86 Warze bekannt. Ich glaube nicht, dass der Linker die Adresse Ihrer ISR-Routinen in der swizzled Form füllen kann, die vom IDT-Eintrag erwartet wird.

Wenn Sie sich ehrgeizig fühlen, könnten Sie ein IDT-Builder-Skript erstellen, das so etwas wie diesen (Linux-basierten) Ansatz ausführt. Ich habe dieses Schema nicht getestet und es ist wahrscheinlich sowieso ein hässlicher Hack, also gehe vorsichtig vor.

Schritt 1: Schreiben Sie ein Skript ‚nm‘ und erfassen die stdout zu laufen.

Schritt 2: Analysieren Sie in Ihrem Skript die nm-Ausgabe, um die Speicheradresse aller Interrupt-Service-Routinen zu ermitteln.

Schritt 3: Ausgabe einer Binärdatei, 'idt.bin', die die IDT-Bytes eingerichtet und für die LIDT-Anweisung bereit ist. Ihr Skript gibt offensichtlich die ISR-Adressen in der richtigen swizzled Form aus.

Schritt 4: Konvertieren seine Raw-Binary in einen elf Abschnitt mit objcopy:

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

Schritt 5: Nun idt.elf Datei hat Ihr IDT binär mit dem Symbol etwas wie folgt aus:

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

Schritt 6: Verknüpfen Sie Ihre Binärdatei einschließlich idt.elf neu. In Ihren Assembly-Stubs und Linker-Skripts können Sie auf das Symbol _binary_idt_bin_start als Basis des IDT verweisen. Zum Beispiel kann Ihr Linker-Skript das Symbol _binary_idt_bin_start an jeder beliebigen Adresse platzieren.

Achten Sie darauf, dass mit dem IDT-Abschnitt Neuverknüpfung nicht anyting sonst in den Binärdatei bewegen, zum Beispiel Ihre ISR-Routinen. Verwalten Sie dies in Ihrem Linker-Skript (.ld-Datei), indem Sie das IDT in einen eigenen dedizierten Abschnitt einfügen.

--- EDIT --- Von Kommentaren scheint es Verwirrung über das Problem zu geben. Die 32-Bit x86 IDT die Adresse der Interrupt-Service-Routine erwartet, aufgeteilt in zwei unterschiedliche 16-Bit-Wörter werden, etwa so:

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

Ein Linker ist somit nicht in der Lage Plug-in der ISR-Adresse als eine normale Umzug. Zur Boot-Zeit muss die Software also dieses geteilte Format erstellen, das die Startzeit verlangsamt.

+0

Dies ist die Schlussfolgerung, die ich sind gekommen. Ein Skript oder ein anderes Programm wird die Symboltabelle des ELF Bildes haben, um zu analysieren und die IDT umgestellt zu konstruieren. Die binäre Ausgabedatei ist eine gute Idee. Ich dachte an eine tatsächliche C-Datei zu erzeugen, aber mit der binären Methode kann ich die Kompilierung Schritt vermeiden, die so etwas wie ein Alptraum wären zu überlegen, wie unser Build-System funktioniert. – jkayca

+0

Die binäre Verfahren vermeidet auch die Notwendigkeit, Code in Ihre bereits verknüpft Firmware-Image zu injizieren. – srking

0

Man könnte so etwas tun:

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