Ini bukan sistem operasi yang nyata . Ini hanya sistem operasi sederhana yang dibuat dalam tujuan pendidikan.
Tujuan utama yang saya ikuti adalah mempelajari bagaimana OS bekerja dari bawah ke atas. Mulai dari sektor boot sendiri, interupsi perangkat keras dan perangkat lunak, driver sendiri.
Repositori sufiks dengan GCC karena saya berencana untuk menulis satu OS sederhana dengan karat. Jadi, saya harap, akan ada Ghnaiklor-OS-GCC dan Ghnaiklor-OS-Rustc .
| Halo, dunia |
|---|
GHAIKLOR-OS-GCC terdiri dari dua file dalam format biner mentah: boot.bin dan kernel.bin . Mereka terletak di boot/boot.bin dan kernel/kernel.bin sesuai setelah dikompilasi.
Boot.bin disusun melalui NASM . Makefile mengambil boot/boot.asm dan memanggil nasm boot/boot.asm -f bin -o boot/boot.bin . NASM Handles termasuk dari sub-folder itu sendiri, sehingga semua file perakitan akan dikompilasi ke biner. Tidak ada yang lain, sederhana.
Kernel.bin disusun melalui GCC dan LD lintas kompiler yang harus Anda instal. Perhatikan bagian lingkungan pengembangan. Setelah kompiler silang dipasang, kami dapat mengambil sumber dari CPU , pengemudi , termasuk , folder kernel dan libc secara rekursif. Semua file .c dikompilasi melalui gcc . File objek yang dikompilasi digunakan untuk mengkompilasi kernel.bin kemudian, melalui ld ld -o kernel/kernel.bin -Ttext 0x1000 <OBJ_FILES> --oformat binary .
os-image.bin adalah disusun Concatenate of Boot.Bin dan Kernel.bin . Mudah dicapai dengan cat boot/boot.bin kernel/kernel.bin > os-image.bin .
Saya telah menulis skrip bootstrap.sh , yang dapat Anda jalankan. Ini akan menginstal semua dependensi yang dibutuhkan untuk mesin host Anda.
bash bootstrap.shKetika komputer dinyalakan atau diatur ulang, ia berjalan melalui serangkaian diagnostik yang disebut tes post-on-self-self. Urutan ini memuncak dalam menemukan perangkat yang dapat di -boot, seperti floppy, cdrom atau hard disk.
Perangkat dapat di -boot jika membawa sektor boot dengan urutan byte 0x55 , 0xAA masing -masing dalam byte 511 dan 512. Ketika BIOS menemukan sektor boot seperti itu, dimuat ke dalam memori pada 0x0000:0x7C00 .
Implementasi sederhana perangkat yang dapat di -boot:
jmp $
times 510 - ($ - $$) db 0
dw 0xAA55 $ - $$ hasil di CURRENT_POINTER - START_POINTER . Dengan begitu kami menghitung berapa lama catatan boot kami. Setelah itu, kami dikurangi 510 dari itu dan mengisi dengan nol, mendapatkan catatan boot 512 byte dengan tanda tangan sektor boot.
Misalnya, kami memiliki $ - $$ sama dengan 100. Jadi, kami memiliki 510 - 100 = 410 byte gratis. Kami mengisi 410 byte ini dengan nol. Dan dua byte terakhir 511 dan 512 adalah tanda tangan yang dapat kami isi dengan dw 0xAA55 .
Selesai! Kami memiliki perangkat yang dapat di -boot dan dapat menggantikan jmp $ kami dengan kode apa pun yang Anda sukai.
Implementasi sektor boot
Pada awalnya kode kami berjalan dalam mode nyata.
Mode nyata adalah mode 16-bit sederhana yang ada pada semua prosesor x86. Mode nyata adalah desain mode x86 pertama dan digunakan oleh banyak sistem operasi awal. Untuk tujuan kompatibilitas, semua prosesor x86 mulai eksekusi dalam mode nyata.
Apa yang buruk dan baik dalam mode nyata?
Kontra
Pro
Karena banyak keterbatasan dan masalah yang dimiliki mode nyata, kita perlu beralih ke mode yang dilindungi.
Mode yang dilindungi adalah mode operasi utama prosesor intel modern sejak 80286. Ini memungkinkan bekerja dengan beberapa ruang alamat virtual, yang masing -masing memiliki memori maksimum 4 GB yang dapat dialamatkan.
Karena CPU diinisialisasi oleh BIOS dimulai dalam mode nyata, beralih ke mode yang dilindungi mencegah Anda menggunakan sebagian besar interupsi BIOS. Sebelum beralih ke mode yang dilindungi, Anda harus menonaktifkan interupsi, termasuk NMI, mengaktifkan garis A20 dan memuat tabel deskriptor global.
Algoritma untuk beralih ke mode yang dilindungi:
cli
lgdt [ gdt_descriptor ]
mov eax , cr0
or eax , 0x1
mov cr0 , eax
jmp CODE_SEG:init_pmImplementasi untuk beralih ke PM
Tabel Deskriptor Global
Tapi, kita bisa melangkah lebih jauh ...
Apa itu mode panjang dan mengapa mengaturnya?
Sejak diperkenalkannya prosesor X86-64, mode baru telah diperkenalkan juga, yang disebut Mode Panjang. Mode panjang pada dasarnya terdiri dari dua sub mode yang merupakan mode 64-bit aktual dan mode kompatibilitas (32-bit).
Yang kami minati hanyalah mode 64-bit karena mode ini menyediakan banyak fitur baru seperti:
Sebelum beralih ke mode panjang, kita harus memeriksa apakah CPU mendukung mode ini. Dalam kasus, jika CPU tidak mendukung mode panjang, kita perlu mundur ke mode yang dilindungi.
Mendeteksi apakah mode panjang mendukung
Jika demikian, beralih ke mode panjang
Semua mode ini bagus, tetapi kami tidak dapat menulis sistem operasi dalam 512 byte. Jadi, sektor boot kami harus tahu cara memuat kernel yang dikompilasi dari hard disk.
Ketika kita berada dalam mode nyata, kita dapat menggunakan interupsi BIOS untuk membaca dari disk. Dalam kasus kami, adalah INT 13,2 - Read Disk Sectors .
Bagaimana cara menggunakannya?
;; al = number of sectors to read (1 - 128)
;; ch = track/cylinder number
;; cl = sector number
;; dh = head number
;; dl = drive number
;; bx = pointer to buffer
mov ah , 0x02
mov al , 15
mov ch , 0x00
mov cl , 0x02
mov dh , 0x00
mov dl , 0
mov bx , KERNEL_OFFSET_IN_MEMORY
int 0x13 Kode ini menghasilkan membaca dari hard disk ke alamat KERNEL_OFFSET_IN_MEMORY . Membaca 15 sektor mulai dari yang kedua dan menyimpannya dengan alamat KERNEL_OFFSET_IN_MEMORY .
Karena gambar OS kami yang dikompilasi adalah penggabungan sektor boot dan kernel, dan kami tahu bahwa sektor boot kami adalah 512 byte, kami dapat yakin, bahwa kernel kami dimulai di sektor kedua.
Saat membaca berhasil selesai, kami dapat memanggil instruksi di KERNEL_OFFSET_IN_MEMORY kami dan memberikan eksekusi ke kernel.
call KERNEL_OFFSET_IN_MEMORY
jmp $Implementasi untuk Disk Read
Kami dapat menarik garis di sini tentang sektor boot kami. Alirannya sederhana:
call sederhana;Pada langkah ini, sektor boot kami menyelesaikan pekerjaannya dan mulai bekerja dengan kernel.
Anda dapat menavigasi melalui sumber boot dan mencoba untuk mendapatkan cara kerjanya.
Ketika kami menelepon instruksi berdasarkan alamat, kami bisa mendapatkan beberapa masalah. Kami tidak yakin, bahwa instruksi dengan alamat adalah kernel_main() . Solusi sederhana.
Kita dapat menulis sub-rutin yang melekat pada awal kode kernel. Fungsi eksternal panggilan sub -rutin ini dari kernel kami - kernel_main() . Ketika file objek akan ditautkan bersama, panggilan ini akan diterjemahkan ke dalam panggilan kernel_main() .
global _start
[bits 32]
[extern kernel_main]
_start:
call kernel_main
jmp $Implementasi entri kernel
Pada langkah ini, kami memiliki titik masuk ke metode kernel_main() kami. Dan itu adalah titik masuk kami untuk seluruh kernel.
Saya pikir, membosankan untuk menjelaskan bagaimana #include bekerja dan apa yang terjadi di kernel_main() . Anda dengan mudah dapat mengikuti metode yang saya panggil darinya.
Entri kernel di c
Itu adalah bagian paling sederhana.
Kita perlu membangun boot/boot.bin gambar dalam format biner mentah. Untuk melakukannya, kami memanggil nasm Assembler dengan bendera khusus.
nasm boot/boot.asm -f bin -o boot/boot.binIni menghasilkan format biner mentah yang dapat Anda jalankan melalui QEMU.
Pada langkah ini, kami memiliki sektor boot yang dikompilasi.
Kita perlu membangun semua sumber dari semua folder secara rekursif, kecuali folder boot .
Semua file C dikompilasi ke file objek melalui file gcc dan perakitan melalui nasm :
gcc -g -ffreestanding -Wall -Wextra -fno-exceptions -m32 -std=c11 -c < SOURCE > -o < OBJ_FILE >
nasm < SOURCE > -f elf -o < OBJ_FILE > Ini menghasilkan semua file objek yang diperlukan untuk menautkan ke format biner mentah. Yang tersisa untuk dilakukan hanyalah menautkannya bersama melalui ld :
ld -o kernel/kernel.bin -Ttext 0x1000 kernel/kernel_entry.o < OBJ_FILES > --oformat binary Perhatikan bahwa kernel/kernel_entry.o pada awalnya karena kami memiliki masalah dengan memanggil kernel_main() . Dengan cara ini, kami menjamin bahwa instruksi pertama akan dipanggil dari boot/kernel_entry.asm kami.
Bagaimanapun, kami telah menyusun gambar kernel dalam format biner mentah.
Karena, sektor boot dan kernel kami adalah format biner mentah, kami dapat menggabungkannya.
cat boot/boot.bin kernel/kernel.bin > os-image.bin Sekarang, kita dapat menjalankan os-image.bin melalui qemu-system-i386 . BIOS mencoba menemukan sektor bootable, cari tahu boot/boot.bin dan melihat tanda tangan. Mulai mengeksekusi kode perakitan kami di boot/boot.bin yang memuat kernel/kernel.bin kami. Bin melalui int 13,2 ke dalam memori dan mengeksekusinya.
Begitulah cara kerjanya bersama. Jangan ragu untuk menavigasi melalui proyek, terima kasih?
Lisensi MIT (MIT)
Hak Cipta (C) 2016 Eugene Obrezkov
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Pemberitahuan hak cipta di atas dan pemberitahuan izin ini harus dimasukkan dalam semua salinan atau bagian substansial dari perangkat lunak.
Perangkat lunak ini disediakan "sebagaimana adanya", tanpa jaminan apa pun, tersurat maupun tersirat, termasuk tetapi tidak terbatas pada jaminan dapat diperjualbelikan, kebugaran untuk tujuan tertentu dan nonpringement. Dalam hal apa pun penulis atau pemegang hak cipta tidak akan bertanggung jawab atas klaim, kerusakan atau tanggung jawab lainnya, baik dalam tindakan kontrak, gugatan atau sebaliknya, timbul dari, di luar atau sehubungan dengan perangkat lunak atau penggunaan atau transaksi lain dalam perangkat lunak.