W tej piątej części zobaczymy tabelę dość podobną do GDT zarówno w teorii, jak iw użyciu, odnosimy się do IDT. IDT oznacza Tabela opisów przerwań y to tablica używana do obsługi występujących przerwań. Na przykład, ktoś dzieli przez 0, wywoływana jest funkcja odpowiedzialna za przetwarzanie. Te funkcje to ISR (Procedury obsługi przerwań). Stwórzmy więc IDT i dodajmy trochę ISR.
Najpierw zadeklarujemy struktury odpowiadające IDT:
struct Entry{
uint16_t base_low;
uint16_t sel;
uint8_t always0;
uint8_t flags;
uint16_t base_high;
} __attribute__((packed));
struct Ptr{
uint16_t limit;
uint32_t base;
} __attribute__((packed));
Jak widać, porównując go z GDT, struktura Ptr jest identyczna, a Entry dość podobna. Dlatego funkcje wstawiania wpisu (SetGate) i instalowania (Instaluj) są bardzo podobne.
void ND::IDT::SetGate(uint8_t num,uint32_t base,uint16_t sel, uint8_t flags)
{
idt[num].base_low=(base & 0xFFFF);
idt[num].base_high=(base >> 16) & 0xFFFF;
idt[num].sel=sel;
idt[num].always0=0;
idt[num].flags=flags;
}
Zainstalować:
idtptr.limit=(sizeof(struct ND::IDT::Entry)*256)-1;
idtptr.base=(uint32_t)&idt;
ND::Memory::Set(&idt,0,sizeof(struct ND::IDT::Entry)*256);
ND::IDT::Flush();
Jeśli spojrzymy, zobaczymy, że funkcja install używa funkcji ND :: Memory :: Set, którą zadeklarowaliśmy w innym poście. Możemy również docenić to, że nie wykonujemy jeszcze żadnych wywołań SetGate i wywołujemy ND :: IDT :: Flush, dla tej funkcji ponownie używamy instrukcji volatile asm:
asm volatile("lidtl (idtptr)");
Jeśli wszystko pójdzie dobrze i wykonamy estetyczną aranżację, powinno to wyglądać tak:
OK, teraz zaczniemy wypełniać IDT przerwaniami. Tutaj utworzę tylko jeden, ale dla reszty będzie tak samo. Mam zamiar podzielić przez zero. Jak dobrze wiesz z matematyki, liczby nie można podzielić przez 0. Jeśli tak się dzieje w procesorze, generowany jest wyjątek, ponieważ nie może kontynuować. W IDT pierwsze przerwanie na liście (0) odpowiada temu zdarzeniu.
Dodajemy to między ustawieniem pamięci a opróżnieniem w ramach funkcji instalacji IDT:
ND::IDT::SetGate(0,(unsigned)ND::ISR::ISR1,0x08,0x8E);
Funkcją zwrotną będzie ND :: ISR :: ISR1, co jest dość proste, chociaż musimy użyć ASM:
void ND::ISR::ISR1()
{
asm volatile(
"cli \n"
"pushl 0 \n"
"pushl 0 \n"
"jmp ND_ISR_Common \n"
);
}
ND_ISR_Common zdefiniujemy to jako funkcję w języku C. Aby zapisać pliki i poprawić czytelność, możemy użyć extern «C» {}:
extern "C"
void ND_ISR_Common()
{
asm volatile(
"pusha \n"
"push %ds \n"
"push %es \n"
"push %fs \n"
"push %gs \n"
"movw $0x10, %ax \n"
"movw %ax, %ds \n"
"movw %ax, %es \n"
"movw %ax, %fs \n"
"movw %ax, %gs \n"
"movl %esp, %eax \n"
"push %eax \n"
"movl $ND_ISR_Handler, %eax \n"
"call *%eax \n"
"popl %eax \n"
"popl %ds \n"
"popl %es \n"
"popl %fs \n"
"popl %gs \n"
"popa \n"
"addl 8, %esp \n"
"iret \n"
);
}
Ten kod w ASM może być nieco trudny do zrozumienia, ale dzieje się tak, ponieważ zamierzamy zadeklarować strukturę w C, aby uzyskać dostęp do danych wygenerowanych przez przerwanie. Oczywiście, jeśli tego nie chcesz, możesz po prostu wywołać Kernel Panic w ND :: ISR :: ISR1 lub coś w tym rodzaju. Konstrukcja ma taki kształt, że:
struct regs{
uint32_t ds;
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
uint32_t int_no, err_code;
uint32_t eip, cs, eflags, useresp, ss;
};
Na koniec tworzymy funkcję ND_ISR_Handler (również z linkiem C), w której pokazujemy panikę jądra i mały opis błędu zgodnie z tym, który mamy na liście błędów.
extern "C"
void ND_ISR_Handler(struct regs *r)
{
if(r->int_no < 32) { ND::Panic::Show(exception_messages[r->int_no]);
for(;;);
}
}
Dobrze i dzięki temu jesteśmy już w stanie poradzić sobie z tą przerwą. Z resztą przerwań byłoby podobnie, z wyjątkiem tego, że są takie, które zwracają parametry i użylibyśmy struktury reg, aby je uzyskać. Jednak możesz się zastanawiać, skąd wiemy, czy to naprawdę działa. Aby sprawdzić, czy to działa, wprowadzimy prostą linię po ND :: IDT :: Install ():
int sum=10/0;
Jeśli skompilujemy, da nam to ostrzeżenie, a jeśli spróbujemy go wykonać, otrzymamy ładny ekran:
I tym kończy się ten post, myślę, że jest jednym z najbardziej rozbudowanych, ale całkiem funkcjonalnych.
Przerzuciłem się na LFS, jest bardziej ciągły.
Święty ... W każdym razie tutoriale są dobre.
Bardzo dobrze, śledzę to od początku. Czy mógłbyś dołączyć kody do każdej przyczepy?
Masz cały kod źródłowy dostępny na GitHub: http://github.com/AdrianArroyoCalle/next-divel Stamtąd możesz pobrać ZIP, TAR.GZ lub po prostu użyć git.
Hahaha, bardzo dobrze! Zatwierdzać! 🙂