OS1Rust for x86のプリミティブカヌネル。 パヌト2. VGA、GDT、IDT

前線


最初の蚘事にはただ冷める時間がありたせんでしたので、興味をそそり、続線を曞かないこずにしたした。


そのため、前の蚘事で、リンク、カヌネルファむルの読み蟌み、およびプラむマリ初期化に぀いお説明したした。 いく぀かの有甚なリンクを提䟛し、ロヌドされたカヌネルがメモリ内に配眮される方法、ブヌト時に仮想アドレスず物理アドレスを比范する方法、およびペヌゞメカニズムのサポヌトを有効にする方法を説明したした。 最埌に、Rustで曞かれたカヌネルのkmain関数に制埡が枡されたした。 次は、りサギの穎の深さを調べたしょう


ノヌトのこの郚分では、Rustの構成に぀いお簡単に説明し、䞀般的にはVGAでの情報の出力に぀いお説明し、セグメントず割り蟌みのセットアップに぀いお詳しく説明したす 。 カットに興味があるすべおの人に尋ねるず、私たちは始めたす。


さびのセットアップ


䞀般に、この手順には特に耇雑なこずはありたせん。詳现に぀いおは、 Philippeブログに連絡しおください。 ただし、いく぀かの時点で停止したす。


Stable Rustは䜎レベルの開発に必芁な䞀郚の機胜をただサポヌトしおいないため、暙準ラむブラリを無効にしおBare Bonesでビルドするには、Rustが毎晩必芁です。 泚意しおください。最新版にアップグレヌドした埌、完党に動䜜しないコンパむラを取埗し、最も近い安定したコンパむラにロヌルバックする必芁がありたした。 コンパむラが昚日は動䜜しおいたが、曎新されお動䜜しないこずが確実な堎合は、必芁な日付を眮き換えおコマンドを実行したす


rustup override add nightly-YYYY-MM-DD 

メカニズムの詳现に぀いおは、 こちらからお問い合わせください 。


次に、目的のプラットフォヌムを構成したす。 私はPhilip Oppermanのブログに基づいおいたので、このセクションの倚くのこずは圌から取られ、骚によっお分解され、私のニヌズに適合したした。 Phillipは圌のブログでx64向けに開発䞭です。圓初はx32を遞択しおいたしたので、target.jsonは少し異なりたす。 私は圌を完党に連れお行きたす


 { "llvm-target": "i686-unknown-none", "data-layout": "em:ep:32:32-f64:32:64-f80:32-n8:16:32-S128", "arch": "x86", "target-endian": "little", "target-pointer-width": "32", "target-c-int-width": "32", "os": "none", "executables": true, "linker-flavor": "ld.lld", "linker": "rust-lld", "panic-strategy": "abort", "disable-redzone": true, "features": "-mmx,-sse,+soft-float" } 

ここで最も難しい郚分は、「 data-layout 」パラメヌタヌです。 LLVMのドキュメントには、これらが「-」で区切られたデヌタレむアりトオプションであるこずが瀺されおいたす。 最初の「e」キャラクタヌはむンド性を担圓したす。この䟋では、プラットフォヌムが必芁ずするリトル゚ンディアンです。 2番目の文字はm、「歪み」です。 レむアりト䞭のキャラクタヌ名を担圓したす。 出力圢匏はELFであるためビルドスクリプトを参照、倀「me」を遞択したす。 3番目の文字は、ビット単䜍のポむンタヌのサむズずABIアプリケヌションバむナリむンタヌフェむスです。 ここではすべおが単玔で、32ビットであるため、「p3232」ず倧胆に配眮したす。 次は浮動小数点数です。 アラむンメント64-「f643264」のABI 32に準拠した64ビット数ず、デフォルトでアラむンメントのある80ビット数-「f8032」をサポヌトするず報告しおいたす。 次の芁玠は敎数です。 8ビットから開始し、プラットフォヌムの最倧32ビット「n81632」に移動したす。 最埌はスタックの敎列です。 128ビット敎数も必芁なので、S128にしたす。 いずれにせよ、LLVMはこのパラメヌタヌを安党に無芖できたす。これが私たちの奜みです。


残りのパラメヌタヌに぀いおは、フィリップを芗くこずができたす、圌はすべおをうたく説明しおいたす。


たた、cargo-xbuildも必芁です。これは、なじみのないタヌゲットプラットフォヌムでビルドするずきに、rust-coreをクロスコンパむルできるツヌルです。
むンストヌル。


 cargo install cargo-xbuild 

このように組み立おたす。


 cargo xbuild -Z unstable-options --manifest-path=kernel/Cargo.toml --target kernel/targets/$(ARCH).json --out-dir=build/lib 

ルヌトディレクトリから起動され、カヌネルはカヌネルディレクトリにあるため、Makeの正しい操䜜のためにマニフェストが必芁でした。


マニフェストの機胜のうち、 crate-type = ["staticlib"]のみを遞択できたす。これにより、出力にリンク可胜なファむルが提䟛されたす。 私たちは圌をLLDで逊いたす。


kmainず初期蚭定


Rustの芏則によれば、静的ラむブラリたたは「フラット」なバむナリファむルを䜜成する堎合、クレヌトのルヌトにぱントリポむントであるファむルlib.rsが含たれおいる必芁がありたす。 その䞭に、属性の助けを借りお、蚀語機胜が構成され、たた倧切なkmainも芋぀けられたす。


そのため、最初のステップでは、stdラむブラリを無効にする必芁がありたす。 これはマクロで行われたす。


 #![no_std] 

このような簡単な手順で、マルチスレッド、動的メモリ、および暙準ラむブラリのその他の楜しみをすぐに忘れおしたいたす。 さらに、printlnマクロも自分自身から奪うため、自分で実装する必芁がありたす。 次回はその方法を説明したす。


この堎所のどこかにある倚くのチュヌトリアルは、「Hello World」の出力で終わり、生き方を説明しおいたせん。 私たちは他の方法で行きたす。 たず、プロテクトモヌドのコヌドずデヌタセグメントを蚭定し、VGAを構成し、割り蟌みを構成する必芁がありたす。


 #![no_std] #[macro_use] pub mod debug; #[cfg(target_arch = "x86")] #[path = "arch/i686/mod.rs"] pub mod arch; #[no_mangle] extern "C" fn kmain(pd: usize, mb_pointer: usize, mb_magic: usize) { arch::arch_init(pd); ...... } #[panic_handler] fn panic(_info: &PanicInfo) -> ! { println!("{}", _info); loop {} } 

ここで䜕が起こっおいたすか 私が蚀ったように、暙準ラむブラリをオフにしたす。 たた、2぀の非垞に重芁なモゞュヌル、debug画面に曞き蟌むずarchすべおのプラットフォヌム䟝存のマゞックが存圚するを発衚したす。 Rust機胜を構成ず共に䜿甚しお、異なるアヌキテクチャヌ実装で同じむンタヌフェヌスを宣蚀し、それらを最倧限に䜿甚したす。 ここでは、x86でのみ停止し、それに぀いおのみ説明したす。


Rustが必芁ずする完党に原始的なパニックハンドラヌを宣蚀したした。 その埌、それを倉曎するこずが可胜になりたす。


kmainは3぀の匕数を受け入れ、名前の歪みのないC衚蚘で゚クスポヌトされるため、リンカは関数を_loaderからの呌び出しに正しく関連付けるこずができたす。これに぀いおは、前の蚘事で説明したした。 最初の匕数はPDペヌゞテヌブルのアドレス、2番目はメモリカヌドを取埗するGRUB構造の物理アドレス、3番目はマゞックナンバヌです。 将来的には、Multiboot 2サポヌトず独自のブヌトロヌダヌの䞡方を実装したいので、マゞックナンバヌを䜿甚しおブヌト方法を識別したす。


最初のkmain呌び出しは、プラットフォヌム固有の初期化です。 䞭に入りたす。 arch_init関数はarch / i686 / mod.rsファむルにあり、32ビットx86固有のパブリックであり、次のようになりたす。


 pub fn arch_init(pd: usize) { unsafe { vga::VGA_WRITER.lock().init(); gdt::setup_gdt(); idt::init_idt(); paging::setup_pd(pd); } } 

ご芧のずおり、x86の堎合、出力、セグメンテヌション、割り蟌み、およびペヌゞングは​​順番に初期化されたす。 VGAから始めたしょう。


VGA初期化


各チュヌトリアルでは、Hello Worldを印刷するこずが矩務付けられおいるため、どこでもVGAを操䜜する方法がわかりたす。 このため、できるだけ簡単に説明したす。自分で䜜ったチップにのみ焊点を圓おたす。 lazy_staticの䜿甚に぀いおは、Philippeのブログに送信したすが、詳现に぀いおは説明したせん。 const fnはただリリヌスされおいないため、矎しく静的な初期化はただ行えたせん。 たた、スピンロックを远加しお、混乱しないようにしたす。


 use lazy_static::lazy_static; use spin::Mutex; lazy_static! { pub static ref VGA_WRITER : Mutex<Writer> = Mutex::new(Writer { cursor_position: 0, vga_color: ColorCode::new(Color::LightGray, Color::Black), buffer: unsafe { &mut *(0xC00B8000 as *mut VgaBuffer) } }); } 

ご存じのように、画面バッファヌは物理アドレス0xB8000にあり、サむズは80x25x2バむト画面の幅ず高さ、文字ごずのバむトず属性色、ちら぀きです。 すでに仮想メモリを有効にしおいるため、このアドレスにアクセスするずクラッシュするため、3 GBを远加したす。 たた、安党ではない生のポむンタを逆参照したすが、䜕をしおいるのかはわかっおいたす。
このファむルの興味深い点は、おそらく、Writer構造の実装だけです。この構造では、文字を連続しお衚瀺できるだけでなく、スクロヌル、画面䞊の任意の堎所ぞの移動、その他の楜しい操䜜も可胜です。


VGAラむタヌ
 pub struct Writer { cursor_position: usize, vga_color: ColorCode, buffer: &'static mut VgaBuffer, } impl Writer { pub fn init(&mut self) { let vga_color = self.vga_color; for y in 0..(VGA_HEIGHT - 1) { for x in 0..VGA_WIDTH { self.buffer.chars[y * VGA_WIDTH + x] = ScreenChar { ascii_character: b' ', color_code: vga_color, } } } self.set_cursor_abs(0); } fn set_cursor_abs(&mut self, position: usize) { unsafe { outb(0x3D4, 0x0F); outb(0x3D5, (position & 0xFF) as u8); outb(0x3D4, 0x0E); outb(0x3D4, ((position >> 8) & 0xFF) as u8); } self.cursor_position = position; } pub fn set_cursor(&mut self, x: usize, y: usize) { self.set_cursor_abs(y * VGA_WIDTH + x); } pub fn move_cursor(&mut self, offset: usize) { self.cursor_position = self.cursor_position + offset; self.set_cursor_abs(self.cursor_position); } pub fn get_x(&mut self) -> u8 { (self.cursor_position % VGA_WIDTH) as u8 } pub fn get_y(&mut self) -> u8 { (self.cursor_position / VGA_WIDTH) as u8 } pub fn scroll(&mut self) { for y in 0..(VGA_HEIGHT - 1) { for x in 0..VGA_WIDTH { self.buffer.chars[y * VGA_WIDTH + x] = self.buffer.chars[(y + 1) * VGA_WIDTH + x] } } for x in 0..VGA_WIDTH { let color_code = self.vga_color; self.buffer.chars[(VGA_HEIGHT - 1) * VGA_WIDTH + x] = ScreenChar { ascii_character: b' ', color_code } } } pub fn ln(&mut self) { let next_line = self.get_y() as usize + 1; if next_line >= VGA_HEIGHT { self.scroll(); self.set_cursor(0, VGA_HEIGHT - 1); } else { self.set_cursor(0, next_line) } } pub fn write_byte_at_xy(&mut self, byte: u8, color: ColorCode, x: usize, y: usize) { self.buffer.chars[y * VGA_WIDTH + x] = ScreenChar { ascii_character: byte, color_code: color } } pub fn write_byte_at_pos(&mut self, byte: u8, color: ColorCode, position: usize) { self.buffer.chars[position] = ScreenChar { ascii_character: byte, color_code: color } } pub fn write_byte(&mut self, byte: u8) { if self.cursor_position >= VGA_WIDTH * VGA_HEIGHT { self.scroll(); self.set_cursor(0, VGA_HEIGHT - 1); } self.write_byte_at_pos(byte, self.vga_color, self.cursor_position); self.move_cursor(1); } pub fn write_string(&mut self, s: &str) { for byte in s.bytes() { match byte { 0x20...0xFF => self.write_byte(byte), b'\n' => self.ln(), _ => self.write_byte(0xfe), } } } } 

巻き戻すずきは、メモリのセクションを画面幅のサむズだけ逆方向にコピヌし、新しい行を空癜で埋めたすこれが、私がクリヌニングを行う方法です。 Outb呌び出しはもう少し興味深いです-I / Oポヌトを操䜜する以倖にカヌ゜ルを移動するこずは䞍可胜です。 ただし、ポヌトを介した入出力が必芁なため、別のパッケヌゞで提䟛され、安党なラッパヌでラップされおいたす。 以䞋のネタバレの䞋にアセンブラヌコヌドがありたす。 今のずころ、それを知るだけで十分です



out.asm

スタックで枡された倉数を操䜜するこずに泚意しおください。 スタックはスペヌスの最埌から始たり、関数が呌び出されたずきにスタックポむンタヌを枛らすため、パラメヌタヌ、リタヌンポむントなどを取埗するために、スタックのアラむメントに合わせお匕数のサむズをESPレゞスタこの堎合は4バむトに远加する必芁がありたす。


 global writeb global writew global writed section .text writeb: push ebp mov ebp, esp mov edx, [ebp + 8] ;port in stack: 8 = 4 (push ebp) + 4 (parameter port length is 2 bytes but stack aligned 4 bytes) mov eax, [ebp + 8 + 4] ;value in stack - 8 = see ^, 4 = 1 byte value aligned 4 bytes out dx, al ;write byte by port number an dx - value in al mov esp, ebp pop ebp ret writew: push ebp mov ebp, esp mov edx, [ebp + 8] ;port in stack: 8 = 4 (push ebp) + 4 (parameter port length is 2 bytes but stack aligned 4 bytes) mov eax, [ebp + 8 + 4] ;value in stack - 8 = see ^, 4 = 1 word value aligned 4 bytes out dx, ax ;write word by port number an dx - value in ax mov esp, ebp pop ebp ret writed: push ebp mov ebp, esp mov edx, [ebp + 8] ;port in stack: 8 = 4 (push ebp) + 4 (parameter port length is 2 bytes but stack aligned 4 bytes) mov eax, [ebp + 8 + 4] ;value in stack - 8 = see ^, 4 = 1 double word value aligned 4 bytes out dx, eax ;write double word by port number an dx - value in eax mov esp, ebp pop ebp ret 

セグメント蚭定


私たちは最も䞍可解でしたが、同時に最も簡単なトピックに到達したした。 前の蚘事で述べたように、メモリ内のペヌゞずセグメントの構成が頭の䞭に混圚しおいたため、ペヌゞテヌブルのアドレスをGDTRにロヌドしお頭を぀かみたした。 十分な資料を読み、それを消化しお実珟するのに数ヶ月かかりたした。 ピヌタヌアベルの教科曞アセンブラヌの犠牲者になったかもしれたせん。 むンテル8086のセグメンテヌションを説明するIBM PCの蚀語ずプログラミング玠晎らしい本です。これらの楜しい時代に、20ビットアドレスの䞊䜍16ビットをセグメントレゞスタにロヌドし、それがメモリ内のアドレスでした。 保護モヌドのi286から始めお、すべおが完党に間違っおいるずいうのは残酷な倱望であるこずが刀明したした。


そのため、叀いプログラムは640 KBを超えおから1 MBのメモリを超えるこずができるため、x86はセグメント化されたメモリモデルをサポヌトしおいるずいう理論がありたす。


プログラマは、実行可胜コヌドを配眮する方法、デヌタを配眮する方法、および安党性を維持する方法に぀いお考える必芁がありたした。 ペヌゞ線成の出珟により、セグメント化された線成は䞍芁になりたしたが、互換性ず保護カヌネル空間ずナヌザヌ空間の特暩の分離の目的のために残ったため、それなしではどこにもありたせん。 特暩レベルが0未満の堎合、䞀郚のプロセッサ呜什は犁止されおおり、プログラムずカヌネルセグメント間のアクセスによりセグメンテヌション゚ラヌが発生したす。


アドレス倉換に぀いおもう䞀床できれば最埌にやりたしょう
行アドレス[0x080xFFFFFFFF]->セグメントの蚱可を確認0x08->仮想アドレス[0xFFFFFFFF]->ペヌゞテヌブル+ TLB->物理アドレス[0xAAAAFFFF]


セグメントはプロセッサ内でのみ䜿甚され、特別なセグメントレゞスタCS、SS、DS、ES、FS、GSに栌玍され、コヌドを実行しお制埡を転送する暩限を確認するためにのみ䜿甚されたす。 そのため、ナヌザヌ空間からカヌネル関数を取埗しお呌び出すこずはできたせん。 蚘述子が0x18のセグメント私が持っおいるものは異なりたすにはレベル3の暩利があり、蚘述子が0x08のセグメントにはレベル0の暩利がありたす。 jmp 0x08経由の暩利[EAX]が、トラップ、ゲヌト、割り蟌みなどの他のメカニズムを䜿甚する矩務がありたす。


セグメントずそのタむプコヌド、デヌタ、ラダヌ、ゲヌトは、グロヌバル蚘述子テヌブルGDTに蚘述されおいる必芁があり、その仮想アドレスずサむズはGDTRレゞスタにロヌドされたす。 セグメント間を遷移するずき簡単にするために、盎接遷移が可胜であるず想定しおいたす、jmp 0x08[EAX]呜什を呌び出す必芁がありたす。 オフセットセレクタヌはCSレゞスタヌにロヌドされ、察応する蚘述子はプロセッサヌのシャドヌレゞスタヌにロヌドされたす。 各蚘述子は8バむト構造です。 十分に文曞化されおおり、その説明はOSDevずIntelのドキュメントの䞡方にありたす最初の蚘事を参照。


たずめたす。 GDTを初期化しおjmp 0x08[EAX]トランゞションを実行するず、プロセッサヌのステヌタスは次のようになりたす。



れロ蚘述子は垞に初期化されおいない必芁があり、アクセスは犁止されおいたす。 マルチスレッドに぀いお説明するずきに、TSS蚘述子ずその意味に぀いお詳しく説明したす。 GDTテヌブルは次のようになりたす。


 extern { fn load_gdt(base: *const GdtEntry, limit: u16); } pub unsafe fn setup_gdt() { GDT[5].set_offset((&super::tss::TSS) as *const _ as u32); GDT[5].set_limit(core::mem::size_of::<super::tss::Tss>() as u32); let gdt_ptr: *const GdtEntry = GDT.as_ptr(); let limit = (GDT.len() * core::mem::size_of::<GdtEntry>() - 1) as u16; load_gdt(gdt_ptr, limit); } static mut GDT: [GdtEntry; 7] = [ //null descriptor - cannot access GdtEntry::new(0, 0, 0, 0), //kernel code GdtEntry::new(0, 0xFFFFFFFF, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE), //kernel data GdtEntry::new(0, 0xFFFFFFFF, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE), //user code GdtEntry::new(0, 0xFFFFFFFF, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE), //user data GdtEntry::new(0, 0xFFFFFFFF, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE), //TSS - for interrupt handling in multithreading GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_TSS_AVAIL, 0), GdtEntry::new(0, 0, 0, 0), ]; 

そしお、ここで初期化に぀いお説明したした。 GDTアドレスずサむズのロヌドは、2぀のフィヌルドのみを含む別の構造を介しお行われたす。 この構造䜓のアドレスは、lgdtコマンドに枡されたす。 デヌタセグメントレゞスタで、オフセット0x10で次の蚘述子をロヌドしたす。


 global load_gdt section .text gdtr dw 0 ; For limit storage dd 0 ; For base storage load_gdt: mov eax, [esp + 4] mov [gdtr + 2], eax mov ax, [esp + 8] mov [gdtr], ax lgdt [gdtr] jmp 0x08:.reload_CS .reload_CS: mov ax, 0x10 ; 0x10 points at the new data selector mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov ax, 0x28 ltr ax ret 

そうすれば、すべおが少し簡単になりたすが、それほど面癜くないでしょう。


äž­æ–­


実際、コアず察話する少なくずもキヌボヌドでクリックしたものを確認する機䌚を私たちに䞎えたす。 これを行うには、割り蟌みコントロヌラヌを初期化する必芁がありたす。


コヌドスタむルに関する叙情的な䜙談。


コミュニティ、特にPhilip Oppermanの努力のおかげで、x86割り蟌み呌び出し芏玄がRustに远加されたした。これにより、iretを実行する割り蟌みハンドラヌを䜜成できたす。 しかし、アセンブラヌずRustを別々のファむルに分離するこずを決定したため、このルヌトを䜿甚しないこずを意識しお決定したした。 はい、スタックメモリを䞍圓に䜿甚しおいたす。これは承知しおいたすが、それでもフレヌバヌです。 私の割り蟌みハンドラヌはアセンブラヌで蚘述されおおり、Rustで蚘述されたものずほが同じ割り蟌みハンドラヌを呌び出したす。 この事実を受け入れお、甘やかしおください。


䞀般に、割り蟌みの初期化プロセスはGDTの初期化ず䌌おいたすが、理解しやすいです。 䞀方、倚くの統䞀されたコヌドが必芁です。 Redox OSの開発者は、蚀語のすべおの楜しみを䜿甚しお矎しい決定を䞋したすが、私は「額に」行っお、コヌドの耇補を蚱可するこずにしたした。


x86の芏則によるず、䞭断はありたすが、䟋倖的な状況がありたす。 このコンテキストでは、私たちの蚭定は実質的に同じです。 唯䞀の違いは、䟋倖がスロヌされるず、スタックに远加情報が含たれるこずがあるこずです。 たずえば、私はそれを䜿甚しお、束を操䜜するずきにペヌゞの䞍足を凊理したすただし、すべおに時間がありたす。 割り蟌みず䟋倖の䞡方が同じテヌブルから凊理されるため、ナヌザヌず私が蚘入する必芁がありたす。 PICProgrammable Interrupt Controllerをプログラムするこずも必芁です。 APICもありたすが、ただわかりたせん。


PICでの䜜業に関しおは、ネットワヌク䞊に倚くの䟋がありたすので、PICでの䜜業に぀いおはあたりコメントしたせん。 アセンブラヌのハンドラヌから始めたす。 それらはすべお完党に同じなので、スポむラヌのコヌドを削陀したす。


IRQ
 global irq0 global irq1 ...... global irq14 global irq15 extern kirq0 extern kirq1 ...... extern kirq14 extern kirq15 section .text irq0: pusha call kirq0 popa iret irq1: pusha call kirq1 popa iret ...... irq14: pusha call kirq14 popa iret irq15: pusha call kirq15 popa iret 

ご芧のずおり、Rust関数の呌び出しはすべお区別ず利䟿性のために「k」プレフィックスで始たりたす。 䟋倖凊理はたったく同じです。 アセンブラヌ関数の堎合、接頭蟞「e」が遞択され、Rustの堎合「k」が遞択されたす。 ペヌゞフォヌルトハンドラヌは異なりたすが、それに぀いお-メモリ管理に関する泚意事項に蚘茉されおいたす。


䟋倖
 global e0_zero_divide global e1_debug ...... global eE_page_fault ...... global e14_virtualization global e1E_security extern k0_zero_divide extern k1_debug ...... extern kE_page_fault ...... extern k14_virtualization extern k1E_security section .text e0_zero_divide: pushad call k0_zero_divide popad iret e1_debug: pushad call k1_debug popad iret ...... eE_page_fault: pushad mov eax, [esp + 32] push eax mov eax, cr2 push eax call kE_page_fault pop eax pop eax popad add esp, 4 iret ...... e14_virtualization: pushad call k14_virtualization popad iret e1E_security: pushad call k1E_security popad iret 

アセンブラヌハンドラヌを宣蚀したす。


 extern { fn load_idt(base: *const IdtEntry, limit: u16); fn e0_zero_divide(); fn e1_debug(); ...... fn e14_virtualization(); fn e1E_security(); fn irq0(); fn irq1(); ...... fn irq14(); fn irq15(); } 

䞊蚘で呌び出すRustハンドラヌを定矩したす。 キヌボヌドを䞭断するには、受信したコヌドを衚瀺するだけで、ポヌト0x60から取埗するこずに泚意しおください-これは、キヌボヌドが最も単玔なモヌドで動䜜する方法です。 将来的には、これが本栌的なドラむバヌに倉わるず思いたす。 各割り蟌みの埌、0x20の凊理終了の信号をコントロヌラヌに出力する必芁がありたす。これは重芁です そうしないず、割り蟌みが発生しなくなりたす。


 #[no_mangle] pub unsafe extern fn kirq0() { // println!("IRQ 0"); outb(0x20, 0x20); } #[no_mangle] pub unsafe extern fn kirq1() { let ch: char = inb(0x60) as char; crate::arch::vga::VGA_WRITER.force_unlock(); println!("IRQ 1 {}", ch); outb(0x20, 0x20); } #[no_mangle] pub unsafe extern fn kirq2() { println!("IRQ 2"); outb(0x20, 0x20); } ... 

IDTおよびPICの初期化。 PICずその再マッピングに぀いお、OSDevで始たりアマチュアサむトで終わる詳现床の異なるチュヌトリアルが倚数芋぀かりたした。 プログラミング手順は䞀定の操䜜シヌケンスず䞀定のコマンドで動䜜するため、これ以䞊説明するこずなくこのコヌドを提䟛したす。 , 0x20-0x2F , 0x20 0x28, 16 IDT.


 unsafe fn setup_pic(pic1: u8, pic2: u8) { // Start initialization outb(PIC1, 0x11); outb(PIC2, 0x11); // Set offsets outb(PIC1 + 1, pic1); /* remap */ outb(PIC2 + 1, pic2); /* pics */ // Set up cascade outb(PIC1 + 1, 4); /* IRQ2 -> connection to slave */ outb(PIC2 + 1, 2); // Set up interrupt mode (1 is 8086/88 mode, 2 is auto EOI) outb(PIC1 + 1, 1); outb(PIC2 + 1, 1); // Unmask interrupts outb(PIC1 + 1, 0); outb(PIC2 + 1, 0); // Ack waiting outb(PIC1, 0x20); outb(PIC2, 0x20); } pub unsafe fn init_idt() { IDT[0x0].set_func(e0_zero_divide); IDT[0x1].set_func(e1_debug); ...... IDT[0x14].set_func(e14_virtualization); IDT[0x1E].set_func(e1E_security); IDT[0x20].set_func(irq0); IDT[0x21].set_func(irq1); ...... IDT[0x2E].set_func(irq14); IDT[0x2F].set_func(irq15); setup_pic(0x20, 0x28); let idt_ptr: *const IdtEntry = IDT.as_ptr(); let limit = (IDT.len() * core::mem::size_of::<IdtEntry>() - 1) as u16; load_idt(idt_ptr, limit); } 

IDTR GDTR — . STI — — , , ASCII- -.


 global load_idt section .text idtr dw 0 ; For limit storage dd 0 ; For base storage load_idt: mov eax, [esp + 4] mov [idtr + 2], eax mov ax, [esp + 8] mov [idtr], ax lidt [idtr] sti ret 

あずがき


さお、この蚘事は非垞に膚倧であるこずが刀明したため、次回はメモリの初期化ず管理に぀いお説明したす。setup_pd関数をコヌドの端にフックしたしたが、次の呌び出しのためにその目的ずデバむスに関する話を残したす。コンテンツの改善点をコヌドに曞くこずをためらわないでください。


゜ヌスコヌドはただGitLabで入手できたす。


ご枅聎ありがずうございたした


UPDパヌト3



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


All Articles