Mengembangkan OS seperti Unix yang monolitik - GDT & IDT (5)

Pada artikel sebelumnya, kami menerapkan manajer memori dinamis.
Hari ini kita akan membahas dasar-dasar bekerja dalam mode terproteksi prosesor Intel i386.
Yaitu: tabel deskriptor global dan tabel vektor interupsi.


Daftar isi


Membangun sistem (make, gcc, gas). Boot awal (multiboot). Luncurkan (qemu). Pustaka C (strcpy, memcpy, strext).
Pustaka C (sprintf, strcpy, strcmp, strtok, va_list ...). Membangun perpustakaan dalam mode kernel dan mode aplikasi pengguna.
Log sistem kernel. Memori video Output ke terminal (kprintf, kpanic, kassert).
Memori dinamis, tumpukan (kmalloc, kfree).
Organisasi memori dan penanganan interupsi (GDT, IDT, PIC, syscall). Pengecualian
Memori virtual (direktori halaman dan tabel halaman).
Proses Perencana Multitasking. Panggilan sistem (bunuh, keluar, ps).
Sistem file kernel (initrd), elf, dan internalnya. Panggilan sistem (exec).
Driver perangkat karakter. Panggilan sistem (ioctl, fopen, fread, fwrite). Pustaka C (fopen, fclose, fprintf, fscanf).
Shell sebagai program lengkap untuk kernel.
Mode perlindungan pengguna (ring3). Segmen Status Tugas (tss).

Mengatasi linear


Prosesor Intel memiliki 2 mode operasi utama: Mode Terlindungi x32 dan IA-32e x64.
Secara umum, Zubkov menulis dengan sangat baik dan dapat dimengerti tentang hal ini, saya sarankan membacanya, meskipun pada prinsipnya Intel Manual juga dimungkinkan, itu tidak rumit, tetapi berlebihan dan besar.
Mereka memiliki volume terpisah untuk pemrograman sistem, saya sarankan dan membacanya.
Ada jauh lebih banyak informasi berbahasa Rusia pada bagian pertama, oleh karena itu, kami akan secara singkat mempertimbangkan poin utama.
Ada dua jenis pengalamatan: linear dan halaman. Linear berarti bahwa seluruh ruang fisik dideskripsikan secara terus-menerus dan bertepatan dengan ruang fisik, karena pada dasarnya basis dari deskriptor segmen adalah nol, karena lebih mudah.
Dalam hal ini, untuk mode kernel, Anda perlu membuat tiga deskriptor yang menggambarkan memori: untuk kode, tumpukan, dan data. Mereka dibedakan oleh beberapa perlindungan perangkat keras.
Setiap segmen tersebut memiliki basis nol dan batas yang ditentukan oleh ukuran maksimum kata mesin. Tumpukan tumbuh ke arah yang berlawanan, dan untuk ini juga ada bendera di deskriptor.
Jadi, dengan tiga catatan format ini kami menangani semua yang kami butuhkan:

/* * Global descriptor table entry */ struct GDT_entry_t { u16 limit_low: 16; u16 base_low: 16; u8 base_middle: 8; u8 type: 4; /* whether code (0b1010), data (0b0010), stack (0b0110) or tss (0b1001) */ u8 s: 1; /* whether system descriptor */ u8 dpl: 2; /* privilege level */ u8 p: 1; /* whether segment prensent */ u8 limit_high: 4; u8 a: 1; /* reserved for operation system */ u8 zero: 1; /* zero */ u8 db: 1; /* whether 16 or 32 segment */ u8 g: 1; /* granularity */ u8 base_high: 8; } attribute(packed); 


Setiap register segmen (cs, ds, ss) memiliki deskriptor sendiri di GDT, jadi ketika kita menulis sesuatu di bagian kode, kita mendapatkan kesalahan, karena ada perlindungan tertulis di deskriptor.
Agar ini berfungsi, kita perlu memuat struktur format berikut ke dalam register GDTR:

 /* * Global descriptor table pointer */ struct GDT_pointer_t { u16 limit; u32 base; } attribute(packed); 


Batasnya adalah akhir dari tabel GDT minus 1, basisnya dimulai dari memori.
GDT dimuat ke dalam register seperti ini:

/*
* Load global descriptor table
* void asm_gdt_load(void *gdt_ptr)
*/
asm_gdt_load:
mov 4(%esp),%eax # eax = gdt_ptr
lgdt (%eax)
mov $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
mov %ax,%ss
jmp $0x08,$asm_gdt_load_exit
asm_gdt_load_exit:
ret


Dan segera setelah itu kami memuat pemilih data kernel ke semua register segmen yang mengindikasikan ke deskriptor data (cincin perlindungan nol).
Setelah itu, semuanya siap untuk memasukkan paging, tetapi lebih pada nanti.
Omong-omong, bootloader multiboot merekomendasikan segera menyiapkan GDT mereka, meskipun mereka melakukannya sendiri, mereka mengatakannya dengan lebih andal.
Lihat cara melakukan semua ini dengan benar secara teknis dalam tutorial video.

Penanganan interupsi


Dengan analogi dengan GDT, tabel interrupt memiliki register IDTR sendiri, di mana Anda juga perlu memuat pointer yang sama tetapi sudah ada di IDT.
Tabel interupsi itu sendiri dijelaskan oleh entri berikut:

 /* * Interrupt table entry */ struct IDT_entry_t { u16 offset_lowerbits; u16 selector; u8 zero; u8 type_attr; u16 offset_higherbits; }; 


Gerbang gangguan biasanya bertindak sebagai tipe, karena kami ingin menangani gangguan secara khusus. Kami belum mempertimbangkan traps dan gateway panggilan, karena lebih dekat dengan TSS dan cincin perlindungan.
Mari kita buat antarmuka untuk bekerja dengan tabel-tabel ini dengan Anda. Mereka hanya perlu diatur dan dilupakan satu kali.

 /* * Api */ extern void gdt_init(); extern void idt_init(); 


Dan sekarang kami akan mendeklarasikan penangan interupsi yang tercantum dalam catatan IDT sendiri.
Pertama, tulis penangan kesalahan perangkat keras:

 /* * Api - IDT */ extern void ih_double_fault(); extern void ih_general_protect(); extern void ih_page_fault(); extern void ih_alignment_check(); extern void asm_ih_double_fault(); extern void asm_ih_general_protect(); extern void asm_ih_page_fault(); extern void asm_ih_alignment_check(); 


Kemudian keyboard interrupt handler:

 /* * Api - IRQ */ extern void ih_keyboard(); extern void asm_ih_keyboard(); 


Saatnya menginisialisasi tabel IDT.
Itu terlihat seperti ini:

 extern void idt_init() { size_t idt_address; size_t idt_ptr[2]; pic_init(); /* fill idt */ idt_fill_entry(INT_DOUBLE_FAULT, (size_t)asm_ih_double_fault); idt_fill_entry(INT_GENERAL_PROTECT, (size_t)asm_ih_general_protect); idt_fill_entry(INT_ALIGNMENT_CHECK, (size_t)asm_ih_alignment_check); idt_fill_entry(INT_KEYBOARD, (size_t)asm_ih_keyboard); /* load idt */ idt_address = (size_t)IDT; idt_ptr[0] = (LOW_WORD(idt_address) << 16) + (sizeof(struct IDT_entry_t) * IDT_SIZE); idt_ptr[1] = idt_address >> 16; asm_idt_load(idt_ptr); } 


Di sini kami mendaftarkan tiga penangan kesalahan perangkat keras dan satu interupsi.
Agar ini mulai bekerja, kita perlu memuat pointer khusus dengan basis dan batas ke dalam register IDTR:

/*
* Load interrupt table
* void asm_idt_load(unsigned long *addr)
*/
asm_idt_load:
push %edx
mov 8(%esp), %edx
lidt (%edx)
pop %edx
ret


Batas diperlukan untuk memahami berapa banyak catatan dalam tabel.
Saatnya untuk menulis pengendali interupsi keyboard:

/*
* Handle IRQ1
* void asm_ih_keyboard(unsigned int)
*/
asm_ih_keyboard:
pushal
call ih_keyboard
popal
iretl


Catatan: selanjutnya dan di mana-mana dalam kode ini, "bagian bawah" setara dengan "bagian atas" di Linux. Dan "atas", masing-masing, kebalikannya. Saya minta maaf, yang terjadi adalah sebaliknya: D

Sebenarnya itu akan meneruskan kode ke pengendali tingkat tinggi.
Itu, pada gilirannya, akan memanggil pawang bagian bawah dari driver yang sesuai yang mendaftarkan permintaan untuk memproses interupsi ini.
Dalam kasus kami, itu akan menjadi pengandar perangkat karakter.
Bagian bawah diperlukan untuk dengan cepat memproses interupsi tanpa memperlambat yang lain, dan kemudian, ketika ada waktu, prosesor bagian atas secara bertahap akan melakukan pekerjaan tambahan, karena prosesor seperti itu sudah dapat dipadati (terputus).

 /* * Api - Keyboard interrupt handler */ extern void ih_keyboard() { printf("[IH]: irq %u\n", 1); u_char status = asm_read_port(KEYBOARD_STATUS_PORT); if (status & 0x01) { char keycode = asm_read_port(KEYBOARD_DATA_PORT); if (keycode < 1) { goto end; } /* call low half (bottom) interrupt handler */ } end: asm_write_port(PIC1_CMD_PORT, 0x20); /* end of interrupt */ } 


Sekarang, ketika kita menekan tombol keyboard, setiap kali kita akan melihat entri yang sesuai di log sistem kernel.

Referensi


Sekarang, buka tutorial video untuk artikel ini.
Dan perhatikan repositori git secara paralel (Anda membutuhkan cabang lesson5)

Referensi


1. James Molloy. Gulung mainan Anda sendiri UNIX-clone OS.
2. Gigi. Assembler untuk DOS, Windows, Unix
3. Kalashnikov. Assembler mudah!
4. Tanenbaum. Sistem operasi. Implementasi dan pengembangan.
5. Robert Love. Kernel Linux Deskripsi proses pengembangan.

Source: https://habr.com/ru/post/id467289/


All Articles