Bagaimana cara prosesor menemukan kode kernel setelah interupsi?

13

Ketika sebuah interupsi terjadi, prosesor akan memproses terlebih dahulu proses saat ini dan memanggil kode kernel untuk menangani interupsi tersebut. Bagaimana cara prosesor tahu di mana harus memasukkan kernel?

Saya mengerti bahwa ada penangan interupsi yang dapat diinstal untuk setiap jalur interupsi. Tetapi karena prosesor hanya mengeksekusi 'logika bawaan', harus ada beberapa tempat yang telah ditentukan yang menunjuk ke salah satu penangan interupsi itu sendiri, atau beberapa kode yang dijalankan sebelum penangan (karena dapat ada banyak penangan untuk satu jalur interupsi, saya berasumsi bahwa terakhir).

Philipp Murry
sumber

Jawaban:

13

Pada saat startup, kernel akan menginisialisasi tabel vektor interupsi (disebut tabel deskriptor interupsi atau IDT pada x86) yang menunjuk ke pengendali interupsi untuk setiap baris.

Sebelum 80286, IDT selalu disimpan di alamat tetap; dimulai dengan 80286, IDT diambil menggunakan LIDTinstruksi.

Tabel vektor interrupt menunjuk ke handler tunggal per garis interupsi; yang dikatakan, sebuah kernel dapat memilih untuk, misalnya, menyediakan pengendali interupsi yang menjalankan beberapa rutin interupsi lain, atau menyediakan satu pengendali tunggal yang mencakup beberapa atau semua interupsi. Linux melakukan hal-hal ini dengan menyediakan pengendali interupsi generik yang menentukan jalur interupsi yang dipanggil dan menemukan penangan downstream yang sesuai untuk dipanggil.

Adam Maras
sumber
1
jadi prosesor menggunakan jalur interupsi sebagai indeks ke IDT, meletakkan entri di PC dan mulai menjalankan? tetapi bukankah ada fungsi generik yang berjalan sebelum semua penangan interrupt? untuk linux adalah do_IRQ (). apakah ini fungsi yang ditunjuk oleh setiap IDT, tidak peduli jalur interupsi?
Philipp Murry
@ PilipMurry ya. Kernel kemudian menggunakan seperangkat penangan interruptnya sendiri (yang bisa ada lebih dari satu per baris) untuk benar-benar menangani interupsi, sebelum kembali ke kode yang sebelumnya dijalankan.
Adam Maras
oke, jadi sebenarnya ada dua jenis penangan interupsi: yang dipanggil oleh prosesor (selalu do_IRQ ()), dan yang dipanggil oleh kernel (yang saya daftarkan melalui request_irq ()). dapatkah Anda menambahkan ini ke jawaban Anda? saya pikir saya akan menerimanya :) terima kasih banyak
Philipp Murry
1
@ PhilippMurry Saya bukan ahli tentang bagaimana Linux menangani interupsi (dan bagaimana pengembang kernel memanfaatkan sistem itu), tetapi saya menambahkan beberapa informasi lebih lanjut dalam pengertian yang lebih luas tentang bagaimana kernel dapat memiliki manajemen ISR mereka sendiri.
Adam Maras
12

Ya, ada tempat yang telah ditentukan yang berisi alamat kode untuk melompat ke: vektor interupsi . Bergantung pada prosesor, ini dapat berupa lokasi spesifik dalam memori fisik (8088), lokasi spesifik dalam memori virtual, register prosesor, lokasi dalam memori yang ditunjukkan oleh register (ARM, 386), ...

Detail bervariasi pada prosesor yang berbeda, tetapi elemen umum utama untuk menangani interupsi dalam prosesor adalah:

  • Mask interupsi (sehingga interupsi berikutnya harus menunggu).
  • Atur mode prosesor ke mode kernel atau interrupt (jika prosesor memiliki mode seperti itu).
  • Simpan nilai penghitung program ke tempat yang diketahui (daftar atau memori).
  • Mungkin menyimpan nilai register lain, atau beralih di antara bank register).
  • Jalankan instruksi selanjutnya (pada nilai PC baru).
Gilles 'SANGAT berhenti menjadi jahat'
sumber
1

Dua jawaban lainnya (pada saat penulisan) berbicara tentang interupsi dan IDT. Namun ini benar, pada CPU Intel-esque modern, tidak kurang dari tiga cara untuk memanggil kernel.

Metode # 1: Mengganggu.

Ini dijelaskan di atas. Anda mengatur entri dalam tabel deskriptor interupsi / vektor interupsi, dan kemudian jalankan interupsi perangkat lunak untuk masuk ke kernel.

Keuntungan utama dari metode ini adalah bahwa kernel tipikal perlu dapat menangani interupsi, dan itu bekerja pada perangkat keras kuno.

Metode # 2: Panggil gerbang.

Gerbang panggilan adalah jenis khusus pemilih segmen. Target panggilan harus dimuat dalam tabel deskriptor segmen global atau lokal (masing-masing GDT dan LDT). Jika Anda kemudian melakukan instruksi panggilan jauh menggunakan gerbang panggilan sebagai segmen (offset panggilan diabaikan), ini memungkinkan Anda untuk memanggil kode yang lebih istimewa. Gerbang panggilan sangat fleksibel; arsitektur IA-32 memiliki empat level privilege, dan gerbang panggilan memungkinkan Anda memanggil level apa pun.

Saya tidak percaya bahwa Linux pernah menggunakan gerbang panggilan, tetapi Windows 95 melakukannya. Layanan kernel Win95 ( krnl386.exedan kernel.dll) benar-benar berjalan dalam mode pengguna (dering 3). Level privilege tertinggi (ring 0) hanya digunakan untuk driver dan microkernel yang hanya melakukan proses switching. Memanggil ke driver dilakukan menggunakan gerbang panggilan. Ini memungkinkan kode 16-bit lama (yang banyak!) Untuk menggunakan driver Win95 hanya menggunakan panggilan jarak jauh standar, seperti yang selalu mereka lakukan.

Perlindungan tabel deskriptor global yang tidak memadai adalah penyebab dari beberapa eksploitasi Windows 95, yang berhasil memasang gerbang panggilan sendiri dengan menulis lebih dari memori.

Metode # 3: SYSCALL / SYSRET dan SYSENTER / SYSEXIT

Ini adalah dua set instruksi, yang diciptakan secara independen oleh AMD dan Intel, tetapi pada dasarnya mereka melakukan hal yang sama. SYSCALL / SYSRET yang lebih dulu dan hanya AMD, SYSENTER / SYSEXIT adalah Intel, tetapi AMD mengimplementasikannya sekarang. Jadi saya akan menjelaskan SYSENTER / SYSEXIT.

Tidak seperti gerbang panggilan, SYSENTER hanya dapat digunakan untuk mentransfer ke dering 0, dan hanya dapat mentransfer ke satu lokasi. Namun, ini memiliki keuntungan menjadi latensi sangat rendah karena tidak seperti panggilan atau interupsi, ia tidak menyentuh tumpukan.

Lokasi transfer diatur menggunakan tiga register khusus model: satu untuk informasi segmen, dan masing-masing untuk penunjuk instruksi dan penumpukan penunjuk kode kernel. Karena tidak ada yang "didorong" ke stack, kode mode pengguna bertanggung jawab untuk memberi tahu kernel ke mana harus kembali dengan melewatkan pointer instruksi pengembalian dan stack pointer dalam register. Kernel bertanggung jawab untuk mengembalikan stack pointer, dan instruksi SYSEXIT mengembalikan pointer instruksi.

Informasi lebih lanjut tentang instruksi SYSENTER dan SYSEXIT.

Nama samaran
sumber