2012-10-12 20 views
7

Pracuję nad projektem, który ma napięte wymagania dotyczące czasu rozruchu. Docelową architekturą jest procesor oparty na IA-32 działający w 32-bitowym trybie chronionym. Jednym z zidentyfikowanych obszarów, które można ulepszyć, jest to, że obecny system dynamicznie inicjuje IDT procesora (tabela deskryptorów przerwań). Ponieważ nie mamy żadnych urządzeń typu "plug-and-play", a system jest stosunkowo statyczny, chcę móc używać statycznie zbudowanego IDT.Statycznie zdefiniowany IDT

Jednak to okazało się kłopotliwe dla łuku IA-32, ponieważ 8-bajtowe deskryptory bramek podziału dzieli adres ISR. Niskie 16 bitów ISR pojawia się w pierwszych 2 bajtach deskryptora, inne bity wypełniają kolejne 4 bajty, a na końcu ostatnie 16 bitów ISR pojawia się w ostatnich 2 bajtach.

chciałem użyć const tablicy do określenia IDT, a następnie po prostu wskazać rejestr IDT na to tak:

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

itp

Oczywiście to nie będzie działać, ponieważ jest to niezgodne z prawem zrobić to w C, ponieważ myIsr nie jest stały. Jego wartość jest rozwiązywana przez linker (który może wykonać tylko ograniczoną ilość matematyki), a nie przez kompilator.

Wszelkie zalecenia lub inne pomysły, jak to zrobić?

Dzięki,

+0

Moja sugestia polegałaby na upewnieniu się, że IDT i ISR ​​znajdują się w tym samym module (i oczywiście, że ISR jest załadowany w stałej pozycji), a następnie używają etykiet. Próbowałem to zrobić z GCC, ale nie lubiłem używać składni '&& myIsr0' poza funkcją i nie posiadałem wbudowanych umiejętności składania, aby zadeklarować IDT używając składni" __asm__ ". Najprawdopodobniej skompilowałbym ten jeden moduł przy użyciu NASM (osobiste preferencje), a ISR-y są wywoływaniem skrótów do funkcji C. To byłaby moja sugestia, chociaż zdecydowanie nie mogę twierdzić, że jestem ekspertem :) – Justin

+0

Oto rodzaj denerwującej sugestii, którą dałbym: użyj zestawu. Podczas korzystania z bardzo specyficznych funkcji procesora lub konstruktów o bardzo niskim poziomie, wywołaj niektóre podzespoły. To ułatwia. – Linuxios

+0

To nie jest moja mocna strona; Czy ktoś może mi krótko wyjaśnić lub wskazać, dlaczego jest to nielegalne? – taz

Odpowiedz

2

Wpadłeś na dobrze znaną brodawkę x86. Nie wierzę, że linker może wymusić adres twoich procedur isr w zapętlonej formie oczekiwanej przez wpis IDT.

Jeśli czujesz się ambitny, możesz utworzyć skrypt budujący IDT, który działa w podobny sposób (oparty na systemie Linux). Nie testowałem tego schematu i prawdopodobnie kwalifikuje się on jako paskudny hack, więc idź ostrożnie.

Krok 1: Napisz skrypt, aby uruchomić "nm" i uchwycić standardowe wyjście.

Krok 2: W swoim skrypcie przeanalizuj wyjście nm, aby uzyskać adres pamięci wszystkich procedur obsługi przerwania.

Krok 3: Wysyłanie pliku binarnego "idt.bin", który ma wszystkie bajty IDT, wszystko gotowe i gotowe do wykonania instrukcji LIDT. Twój skrypt w oczywisty sposób wypisuje adresy isr w poprawnej formie ze swizzlem.

Krok 4: Konwersja swoje pierwotne binarne w sekcji elf z objcopy:

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

Krok 5: Teraz idt.elf plik ma swój binarny IDT symbolem coś takiego:

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

Krok 6: Połącz ponownie plik binarny, w tym idt.elf. W twoich skrótach montażowych i skryptach linkera możesz odwoływać się do symbolu _binary_idt_bin_start jako podstawy IDT. Na przykład skrypt linkera może umieścić symbol _binary_idt_bin_start pod dowolnym adresem, który ci się podoba.

Należy uważać, aby ponowne łączenie z sekcją IDT nie poruszyło się w innym miejscu pliku binarnego, np. twoje procedury isr. Zarządzaj tym w swoim skryptu linkera (plik .ld), umieszczając IDT w swojej dedykowanej sekcji.

--- EDYTUJ --- Wydaje się, że z komentarzy wynika zamieszanie. 32-bitowy x86 IDT oczekuje adres procedury obsługi przerwania być podzielone na dwie 16-bitowych słów, tak jak poniżej:

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

Łącznik jest w ten sposób w stanie zatkać, adres ISR jako normalne przeniesienie. Tak więc, przy rozruchu, oprogramowanie musi skonstruować ten podzielony format, co spowalnia czas uruchamiania.

+0

Jest to wniosek przyszedłem. Skrypt lub inny program będzie musiał przeanalizować tabelę symboli obrazu ELF i skonstruować zawężone IDT. Wyjściowym plikiem binarnym jest dobry pomysł. Myślałem o wygenerowaniu rzeczywistego pliku C, ale dzięki metodzie binarnej mogę uniknąć etapu kompilacji, który byłby koszmarem, biorąc pod uwagę sposób działania naszego systemu kompilacji. – jkayca

+0

Metoda binarna pozwala również uniknąć konieczności wprowadzania kodu do już połączonego obrazu oprogramowania układowego. – srking

0

Można zrobić coś takiego:

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 
Powiązane problemy