Bagaimana Anda menjalankan suatu program dengan sendirinya tanpa menjalankan sistem operasi? Dapatkah Anda membuat program perakitan yang dapat dimuat dan dijalankan oleh komputer saat startup, misalnya mem-boot komputer dari flash drive dan menjalankan program yang menggunakan CPU?
assembly
x86
operating-system
bootloader
osdev
pengguna2320609
sumber
sumber
Jawaban:
Anda menempatkan kode biner ke tempat prosesor mencari setelah reboot (mis. Alamat 0 pada ARM).
Jawaban umum untuk pertanyaan: itu bisa dilakukan. Ini sering disebut sebagai "pemrograman bare metal". Untuk membaca dari flash drive, Anda ingin tahu apa itu USB, dan Anda ingin ada driver untuk bekerja dengan USB ini. Program pada drive ini juga harus dalam beberapa format tertentu, pada beberapa sistem file tertentu ... Ini adalah sesuatu yang biasanya dilakukan oleh boot loader, tetapi program Anda dapat menyertakan bootloadernya sendiri sehingga dapat diisi sendiri, jika firmware hanya akan memuat satu blok kecil kode.
Banyak papan ARM memungkinkan Anda melakukan beberapa hal. Beberapa memiliki boot loader untuk membantu Anda dengan pengaturan dasar.
Di sini Anda dapat menemukan tutorial yang bagus tentang cara melakukan sistem operasi dasar pada Raspberry Pi.
Sunting: Artikel ini, dan seluruh wiki.osdev.org akan menjawab sebagian besar pertanyaan Anda http://wiki.osdev.org/Introduction
Juga, jika Anda tidak ingin bereksperimen langsung pada perangkat keras, Anda dapat menjalankannya sebagai mesin virtual menggunakan hypervisors seperti qemu. Lihat cara menjalankan "hello world" secara langsung pada perangkat keras ARM tervirtualisasi di sini .
sumber
Contoh yang bisa dijalankan
Mari kita buat dan jalankan beberapa program dunia bare metal yang sangat kecil yang berjalan tanpa OS di:
Kami juga akan mencobanya pada emulator QEMU sebanyak mungkin, karena lebih aman dan nyaman untuk dikembangkan. Tes QEMU telah berada di host Ubuntu 18.04 dengan QEMU 2.11.1 yang telah dikemas sebelumnya.
Kode semua contoh x86 di bawah ini dan lainnya ada pada repo GitHub ini .
Cara menjalankan contoh di perangkat keras x86 nyata
Ingatlah bahwa menjalankan contoh pada perangkat keras nyata bisa berbahaya, misalnya Anda dapat menghapus disk atau merusak perangkat keras secara tidak sengaja: hanya lakukan ini pada mesin lama yang tidak berisi data penting! Atau bahkan lebih baik, gunakan devboards semi-sekali pakai murah seperti Raspberry Pi, lihat contoh ARM di bawah ini.
Untuk laptop x86 biasa, Anda harus melakukan sesuatu seperti:
Bakar gambar ke stik USB (akan menghancurkan data Anda!):
pasang USB di komputer
Hidupkan
katakan itu untuk boot dari USB.
Ini berarti membuat firmware memilih USB sebelum hard disk.
Jika itu bukan perilaku default mesin Anda, terus tekan Enter, F12, ESC atau kunci aneh lainnya setelah dinyalakan hingga Anda mendapatkan menu boot di mana Anda dapat memilih untuk boot dari USB.
Seringkali dimungkinkan untuk mengonfigurasi urutan pencarian di menu-menu itu.
Misalnya, pada T430 saya, saya melihat yang berikut ini.
Setelah dihidupkan, inilah saatnya saya harus menekan Enter untuk masuk ke menu boot:
Kemudian, di sini saya harus menekan F12 untuk memilih USB sebagai perangkat boot:
Dari sana, saya dapat memilih USB sebagai perangkat boot seperti ini:
Atau, untuk mengubah urutan boot dan memilih USB untuk memiliki prioritas lebih tinggi sehingga saya tidak harus memilihnya secara manual setiap kali, saya akan menekan F1 pada layar "Startup Interrupt Menu", dan kemudian arahkan ke:
Sektor boot
Pada x86, level paling sederhana dan terendah yang dapat Anda lakukan adalah membuat Master Boot Sector (MBR) , yang merupakan jenis sektor boot , dan kemudian instal ke disk.
Di sini kita membuat satu dengan satu
printf
panggilan:Hasil:
Perhatikan bahwa bahkan tanpa melakukan apa pun, beberapa karakter sudah dicetak di layar. Itu dicetak oleh firmware, dan berfungsi untuk mengidentifikasi sistem.
Dan pada T430 kita hanya mendapatkan layar kosong dengan kursor yang berkedip:
main.img
berisi yang berikut ini:\364
dalam oktal ==0xf4
dalam hex: pengkodean untukhlt
instruksi, yang memberitahu CPU untuk berhenti bekerja.Karenanya program kami tidak akan melakukan apa-apa: hanya memulai dan berhenti.
Kami menggunakan oktal karena
\x
nomor hex tidak ditentukan oleh POSIX.Kami dapat memperoleh enkode ini dengan mudah dengan:
yang keluaran:
tetapi ini juga didokumentasikan dalam manual Intel.
%509s
menghasilkan 509 ruang. Diperlukan untuk mengisi file hingga byte 510.\125\252
dalam oktal ==0x55
diikuti oleh0xaa
.Ini adalah 2 byte sihir yang diperlukan yang harus berupa byte 511 dan 512.
BIOS memeriksa semua disk kami untuk mencari yang dapat di-boot, dan BIOS hanya mempertimbangkan yang dapat di-boot yang memiliki dua byte ajaib itu.
Jika tidak ada, perangkat keras tidak akan memperlakukan ini sebagai disk yang dapat di-boot.
Jika Anda bukan seorang
printf
master, Anda dapat mengkonfirmasi kontenmain.img
dengan:yang menunjukkan yang diharapkan:
di mana
20
ruang di ASCII.Firmware BIOS membaca 512 byte tersebut dari disk, memasukkannya ke dalam memori, dan menetapkan PC ke byte pertama untuk mulai menjalankannya.
Halo sektor boot dunia
Sekarang kita telah membuat program minimal, mari kita pindah ke dunia halo.
Pertanyaan yang jelas adalah: bagaimana cara melakukan IO? Beberapa pilihan:
minta firmware, misalnya BIOS atau UEFI, untuk melakukannya untuk kita
VGA: wilayah memori khusus yang akan dicetak ke layar jika ditulis. Dapat digunakan dalam Mode Terlindungi.
menulis driver dan berbicara langsung ke perangkat keras layar. Ini adalah cara yang "tepat" untuk melakukannya: lebih kuat, tetapi lebih kompleks.
port serial . Ini adalah protokol standar yang sangat sederhana yang mengirim dan menerima karakter dari terminal host.
Di desktop, tampilannya seperti ini:
Sumber .
Sayangnya ini tidak terpapar pada kebanyakan laptop modern, tetapi merupakan cara umum untuk menggunakan papan pengembangan, lihat contoh ARM di bawah ini.
Ini benar-benar memalukan, karena antarmuka seperti itu sangat berguna untuk men-debug kernel Linux misalnya .
gunakan fitur debug chip. ARM menyebut semihosting mereka misalnya. Pada perangkat keras nyata, ini memerlukan beberapa perangkat keras dan dukungan perangkat lunak tambahan, tetapi pada emulator itu bisa menjadi pilihan nyaman gratis. Contoh .
Di sini kita akan melakukan contoh BIOS karena lebih sederhana pada x86. Tetapi perhatikan bahwa ini bukan metode yang paling kuat.
utama
GitHub hulu .
link.ld
Kumpulkan dan tautkan dengan:
Hasil:
Dan pada T430:
Diuji pada: Lenovo Thinkpad T430, UEFI BIOS 1.16. Disk dihasilkan pada host Ubuntu 18.04.
Selain instruksi perakitan pengguna darat standar, kami memiliki:
.code16
: memberitahu GAS untuk mengeluarkan kode 16-bitcli
: nonaktifkan interupsi perangkat lunak. Itu bisa membuat prosesor mulai berjalan lagi setelahhlt
int $0x10
: melakukan panggilan BIOS. Inilah yang mencetak karakter satu per satu.Bendera tautan penting adalah:
--oformat binary
: output kode rakitan biner mentah, jangan bungkus di dalam file ELF seperti halnya untuk executable userland biasa.Untuk lebih memahami bagian skrip linker, biasakan diri Anda dengan langkah relokasi menghubungkan: Apa yang dilakukan linker?
Cooler x86 program bare metal
Berikut adalah beberapa pengaturan logam telanjang yang lebih kompleks yang telah saya capai:
Gunakan C sebagai ganti perakitan
Rangkuman: gunakan GRUB multiboot, yang akan menyelesaikan banyak masalah menjengkelkan yang tidak pernah Anda pikirkan. Lihat bagian di bawah ini.
Kesulitan utama pada x86 adalah bahwa BIOS hanya memuat 512 byte dari disk ke memori, dan Anda cenderung meledakkan 512 byte tersebut saat menggunakan C!
Untuk mengatasinya, kita bisa menggunakan bootloader dua tahap . Ini membuat panggilan BIOS lebih lanjut, yang memuat lebih banyak byte dari disk ke dalam memori. Berikut ini adalah contoh perakitan tahap 2 minimal dari awal menggunakan panggilan BIOS int 0x13 :
Kalau tidak:
-kernel
opsi, yang memuat seluruh file ELF ke dalam memori. Ini adalah contoh ARM yang saya buat dengan metode itu .kernel7.img
, seperti halnya QEMU-kernel
.Hanya untuk tujuan pendidikan, berikut adalah contoh minimal C satu tahap :
main.c
entri
linker.ld
Lari
C library standar
Akan lebih menyenangkan jika Anda juga ingin menggunakan pustaka standar C, karena kami tidak memiliki kernel Linux, yang mengimplementasikan banyak fungsi pustaka standar C melalui POSIX .
Beberapa kemungkinan, tanpa masuk ke sistem operasi lengkap seperti Linux, termasuk:
Tulis milikmu sendiri. Itu hanya sekelompok header dan file C pada akhirnya, kan? Baik??
Newlib
Contoh terperinci di: /electronics/223929/c-standard-libraries-on-bare-metal/223931
Mengimplementasikan Newlib semua hal yang membosankan non-OS khusus untuk Anda, misalnya
memcmp
,memcpy
, dllKemudian, ia memberikan beberapa potongan untuk Anda menerapkan syscalls yang Anda butuhkan sendiri.
Sebagai contoh, kita dapat mengimplementasikan
exit()
ARM melalui semihosting dengan:seperti yang ditunjukkan pada contoh ini .
Misalnya, Anda bisa mengarahkan ulang
printf
ke sistem UART atau ARM, atau menerapkannyaexit()
dengan semihosting .sistem operasi tertanam seperti FreeRTOS dan Zephyr .
Sistem operasi seperti itu biasanya memungkinkan Anda untuk mematikan penjadwalan pre-emptive, sehingga memberi Anda kendali penuh atas runtime program.
Mereka dapat dilihat sebagai semacam Newlib pra-implementasi.
GNU GRUB Multiboot
Sektor boot sederhana, tetapi tidak terlalu nyaman:
Karena alasan itulah GNU GRUB membuat format file yang lebih nyaman yang disebut multiboot.
Contoh kerja minimal: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world
Saya juga menggunakannya pada repo contoh GitHub saya untuk dapat dengan mudah menjalankan semua contoh pada perangkat keras nyata tanpa membakar USB satu juta kali.
Hasil QEMU:
T430:
Jika Anda menyiapkan OS Anda sebagai file multiboot, GRUB kemudian dapat menemukannya di dalam sistem file biasa.
Inilah yang dilakukan sebagian besar distro, menempatkan gambar OS di bawah
/boot
.File multiboot pada dasarnya adalah file ELF dengan header khusus. Mereka ditentukan oleh GRUB di: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
Anda dapat mengubah file multiboot menjadi disk yang dapat di-boot dengan
grub-mkrescue
.Firmware
Sebenarnya, sektor boot Anda bukan perangkat lunak pertama yang berjalan pada CPU sistem.
Apa yang sebenarnya berjalan pertama adalah apa yang disebut firmware , yang merupakan perangkat lunak:
Firmwares yang terkenal meliputi:
Firmware melakukan hal-hal seperti:
lewati setiap hard disk, USB, jaringan, dll. hingga Anda menemukan sesuatu yang dapat di-boot.
Ketika kami menjalankan QEMU,
-hda
mengatakan itumain.img
adalah hard disk yang terhubung ke perangkat keras, danhda
itu yang pertama kali dicoba, dan itu yang digunakan.muat 512 byte pertama ke alamat memori RAM
0x7c00
, letakkan RIP CPU di sana, dan biarkan berjalanmenunjukkan hal-hal seperti menu boot atau panggilan cetak BIOS pada tampilan
Firmware menawarkan fungsionalitas mirip OS di mana sebagian besar OS bergantung. Misalnya subset Python telah porting untuk dijalankan di BIOS / UEFI: https://www.youtube.com/watch?v=bYQ_lq5dcvM
Dapat dikatakan bahwa firmware tidak dapat dibedakan dari OS, dan bahwa firmware adalah satu-satunya pemrograman bare metal yang "benar" yang dapat dilakukan.
Seperti yang dikatakan CoreOS dev ini :
Poskan keadaan awal BIOS
Seperti banyak hal dalam perangkat keras, standardisasi lemah, dan salah satu hal yang tidak boleh Anda andalkan adalah keadaan awal register ketika kode Anda mulai berjalan setelah BIOS.
Jadi bantulah diri Anda sendiri dan gunakan beberapa kode inisialisasi seperti berikut: https://stackoverflow.com/a/32509555/895245
Mendaftar suka
%ds
dan%es
memiliki efek samping yang penting, jadi Anda harus membidiknya walaupun Anda tidak menggunakannya secara eksplisit.Perhatikan bahwa beberapa emulator lebih bagus daripada perangkat keras asli dan memberikan Anda kondisi awal yang bagus. Kemudian ketika Anda menjalankannya pada perangkat keras nyata, semuanya rusak.
El Torito
Format yang dapat dibakar ke CD: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29
Dimungkinkan juga untuk menghasilkan gambar hibrida yang bekerja pada ISO atau USB. Ini dapat dilakukan dengan
grub-mkrescue
( contoh ), dan juga dilakukan oleh kernel Linux saatmake isoimage
menggunakanisohybrid
.LENGAN
Dalam ARM, ide-ide umumnya sama.
Tidak ada firmware pra-instal semi-standar yang tersedia secara luas seperti BIOS untuk kami gunakan untuk IO, jadi dua jenis IO paling sederhana yang dapat kami lakukan adalah:
Saya telah mengunggah:
beberapa QEMU C + Newlib dan contoh perakitan sederhana di sini di GitHub .
Contoh prompt.c misalnya mengambil input dari terminal host Anda dan memberikan output semua melalui simulasi UART:
Lihat juga: Bagaimana membuat program ARM bare metal dan menjalankannya di QEMU?
pengaturan blinker Raspberry Pi yang sepenuhnya otomatis di: https://github.com/cirosantilli/raspberry-pi-bare-metal-blinker
Lihat juga: Bagaimana menjalankan program C tanpa OS di Raspberry Pi?
Untuk "melihat" LED pada QEMU Anda harus mengkompilasi QEMU dari sumber dengan flag debug: /raspberrypi/56373/is-it-possible-to-get-get-the-state-of- the-leds-and-gpios-in-a-qemu-emulation-like-t
Selanjutnya, Anda harus mencoba dunia hello UART. Anda dapat mulai dari contoh tanda bahayanya, dan ganti kernel dengan yang ini: https://github.com/dwelch67/raspberrypi/tree/bce377230c2cdd8ff1e40919fdedbc2533ef5a00/uart01
Pertama, buat UART bekerja dengan Raspbian seperti yang saya jelaskan di: /raspberrypi/38/prepare-for-ssh-without-a-screen/54394#54394 Akan terlihat seperti ini:
Pastikan untuk menggunakan pin yang tepat, atau Anda dapat membakar konverter UART ke USB, saya sudah melakukannya dua kali dengan hubungan arus pendek dan 5V ...
Akhirnya terhubung ke serial dari host dengan:
Untuk Raspberry Pi, kami menggunakan kartu Micro SD alih-alih stik USB untuk memuat file executable kami, yang biasanya Anda perlukan adaptor untuk terhubung ke komputer Anda:
Jangan lupa untuk membuka kunci adaptor SD seperti yang ditunjukkan di: /ubuntu/213889/microsd-card-is-set-to-read-only-state-how-can-i-write-data -pada-itu / 814585 # 814585
https://github.com/dwelch67/raspberrypi sepertinya tutorial Raspberry Pi bare metal paling populer yang tersedia saat ini.
Beberapa perbedaan dari x86 termasuk:
IO dilakukan dengan menulis ke alamat sihir secara langsung, tidak ada
in
danout
instruksi.Ini disebut memori yang dipetakan IO .
untuk beberapa perangkat keras nyata, seperti Raspberry Pi, Anda dapat menambahkan sendiri firmware (BIOS) ke gambar disk.
Itu adalah hal yang baik, karena itu membuat memperbarui firmware itu lebih transparan.
Sumber daya
sumber
Sistem Operasi sebagai inspirasi
Sistem operasi juga merupakan program , jadi kami juga dapat membuat program kami sendiri dengan membuat dari awal atau mengubah (membatasi atau menambahkan) fitur-fitur dari salah satu sistem operasi kecil , dan kemudian menjalankannya selama proses boot (menggunakan gambar ISO ) .
Misalnya, halaman ini dapat digunakan sebagai titik awal:
Cara menulis sistem operasi yang sederhana
Di sini, seluruh Sistem Operasi cocok sepenuhnya dalam sektor boot 512-byte ( MBR )!
OS sederhana semacam itu atau yang serupa dapat digunakan untuk membuat kerangka kerja sederhana yang akan memungkinkan kita:
Namun, ada banyak kemungkinan. Misalnya, untuk melihat OS bahasa rakitan x86 yang lebih besar, kita dapat menjelajahi MykeOS , sistem operasi x86 yang merupakan alat pembelajaran untuk menunjukkan kerja OS mode 16-bit sederhana, mode nyata, dengan kode yang dikomentari dengan baik dan dokumentasi yang luas .
Boot Loader sebagai inspirasi
Jenis umum lain dari program yang berjalan tanpa sistem operasi adalah juga Boot Loaders . Kami dapat membuat program yang terinspirasi oleh konsep seperti misalnya menggunakan situs ini:
Cara mengembangkan Boot Loader Anda sendiri
Artikel di atas menyajikan juga arsitektur dasar dari program semacam itu :
Seperti yang dapat kita lihat, arsitektur ini sangat fleksibel dan memungkinkan kita untuk mengimplementasikan program apa pun , tidak harus boot loader.
Secara khusus, ini menunjukkan bagaimana menggunakan teknik "kode campur" berkat yang memungkinkan untuk menggabungkan konstruksi tingkat tinggi (dari C atau C ++ ) dengan perintah tingkat rendah (dari Assembler ). Ini adalah metode yang sangat berguna, tetapi kita harus ingat bahwa:
Artikel ini juga menunjukkan cara melihat program yang dibuat beraksi dan cara melakukan pengujian dan debug.
Aplikasi UEFI sebagai inspirasinya
Contoh di atas menggunakan fakta memuat MBR sektor pada media data. Namun, kita bisa masuk lebih dalam ke kedalaman dengan melakukan plaing misalnya dengan aplikasi UEFI :
Jika kami ingin mulai membuat program seperti itu , kami dapat, misalnya, mulai dengan situs web ini:
Pemrograman untuk EFI: Membuat Program "Hello, World" / Pemrograman UEFI - Langkah Pertama
Menjelajahi masalah keamanan sebagai inspirasi
Diketahui bahwa ada seluruh kelompok perangkat lunak berbahaya (yang merupakan program) yang berjalan sebelum sistem operasi dimulai .
Sekelompok besar dari mereka beroperasi pada sektor MBR atau aplikasi UEFI, sama seperti semua solusi di atas, tetapi ada juga yang menggunakan titik masuk lain seperti Volume Boot Record (VBR) atau BIOS :
atau mungkin yang lain juga.
Serangan sebelum startup sistem
Berbagai cara untuk boot
Saya juga berpikir bahwa dalam konteks ini perlu juga disebutkan bahwa ada berbagai bentuk mem - boot sistem operasi (atau program yang dapat dieksekusi yang dimaksudkan untuk ini) . Ada banyak, tetapi saya ingin memperhatikan memuat kode dari jaringan menggunakan opsi Network Boot ( PXE ), yang memungkinkan kita untuk menjalankan program di komputer terlepas dari sistem operasinya dan bahkan terlepas dari media penyimpanan yang terhubung langsung ke komputer:
Apa itu Booting Jaringan (PXE) dan Bagaimana Cara Menggunakannya?
sumber