Membuat bootstrap loader untuk UFD
25 November 2011 3 Komentar
Pada beberapa mata kuliah seperti sistem operasi dan sistem berkas, dosen mungkin harus mendemokan beberapa komponen low-level yang tidak tersedia pada sistem operasi modern. Seperti yang diketahui, seluruh sistem operasi modern zaman sekarang beroperasi pada CPU protected mode. Hal ini berarti hanya kode program saja yang berada di ring 0, sementara kode program yang dibuat user tidak akan mendapatkan akses ring 0 (kecuali bila diizinkan oleh sistem operasi). Beberapa instruksi assembly seperti IN, OUT, dan beberapa INT umumnya dibatasi oleh sistem operasi. Sebagai contoh, Windows akan menolak instruksi IN/OUT pada program user mode tetapi mengizinkannya pada driver. Sementara di Linux, IN/OUT diperbolehkan bila sebelumnya terdapat pemanggilan ioperm() dan program di-akses dengan hak akses superuser.
Bagaimana bila seseorang ingin tetap menjalankan programnya di CPU real-mode? Ia harus membuat sistem operasi sendiri! Atau untuk sebuah eksperimen sederhana, ia dapat membuat sebuah bootstrap loader yang akan menjalankan sebuah program real mode begitu komputer dinyalakan! Dan untuk eksperimen seperti ini, media UFD (USB Flash Drive) adalah pilihan yang tepat. Kebanyakan BIOS di motherboard zaman sekarang sudah mendukung emulasi disk untuk UFD, sehingga mendukung proses boot dari UFD.
Ini adalah percobaan yang saya lakukan dengan menggunakan sebuah UFD berukuran 4 GB yang menggunakan file system FAT32. UFD tersebut hanya mengandung sebuah partisi. Saya menyalin boot sector dari partisi pertama. Struktur boot sector untuk FAT32 dapat dilihat di http://en.wikipedia.org/wiki/FAT32. Berikut ini kode program assembly (dalam NASM) yang membentuk boot sector di UFD saya beserta dengan bootstrap loader buatan sendiri:
[BITS 16] [ORG 0x7C00] jmp mulai nop ; ------------------------------------------------------ ; Informasi Boot Sector ; ------------------------------------------------------ db 'SOS-JCH ' ; OEM name db 00, 0x2 ; Bytes per sector db 0x8 ; Sector per cluster db 0x20, 00 ; Reserved sector count db 0x2 ; Number of FAT db 00, 00, 00,00 db 0xF8 ; Media descriptor (F8 = Fixed disk) db 00, 00 db 0x3E, 00 ; Sectors per track db 0x7C, 00 ; Number of heads db 00, 00, 00, 00 ; Count of hidden sectors db 0xA2, 0xA7, 0x77, 00 ; Total sectors db 0xDB, 0x1D, 00, 00 ; Sectors per FAT db 00, 00 ; Mirroring flags db 00, 00 ; Version db 0x02, 00, 00, 00 ; Cluster number of root directory start db 0x01, 00 ; Sector number of FS Information Sector db 0x06, 00 ; First sector number of a copy of three FAT32 boot sectors db 00, 00, 00, 00 db 00, 00, 00, 00 db 00, 00, 00, 00 db 00, 00 db 0x29 ; Extended boot signature db 0xA5, 0x82, 0x1D, 0x86 ; Volume ID db 'Latihan ' ; Volume Label db 'FAT32 ' ; File system type ; ------------------------------------------------------ ; Starting point ; ------------------------------------------------------ mulai: ; ------------------------------------------------------ ; Inisialisasi ; ------------------------------------------------------ cli xor ax, ax mov ss, ax mov sp, 0x7C00 mov ds, ax mov es, ax sti ; ------------------------------------------------------ ; Menyalin FAT dan menyimpannya ke lokasi offset 0x7E00 ; ------------------------------------------------------ mov word [ds:dap_jumlah_sector], 1 mov word [ds:dap_dest_segment], ds mov word [ds:dap_dest_offset], 0x7E00 mov dword [ds:dap_lba_lo], 94 call bacasector mov eax, [0x7E08] mov [fat_next_cluster], eax ; ------------------------------------------------------ ; Menyalin RootDirectoryEntry ke lokasi offset 0x8000 ; ------------------------------------------------------ mov word [ds:dap_jumlah_sector], 8 mov word [ds:dap_dest_offset], 0x8000 mov dword [ds:dap_lba_lo], 0x3C14 call bacasector ; ------------------------------------------------------ ; Mencari File Dengan Nama KERNEL.BIN ; ------------------------------------------------------ proses_cluster_berikutnya: mov si, 0x8000 pencarian_file_kernel: push si lea di, [nama_file_kernel] cld mov cx, 11 repe cmpsb jz file_kernel_ditemukan ; ; File kernel tidak ditemukan pada entry ini. ; Lanjut ke entry direktori berikutnya. ; pop si add si, 32 cmp si, 0x9000 jbe pencarian_file_kernel ; ; File kernel tidak ditemukan di cluster ini ; Periksa apakah masih ada cluster berikutnya. ; mov eax, [fat_next_cluster] and eax, 0x0ffffff0 cmp eax, 0x0ffffff0 jz hang mov di, 0x8000 call bacacluster call bacafat32 jmp proses_cluster_berikutnya file_kernel_ditemukan: ;------------------------------------------------------------ ; Baca File Kernel dan Salin Di Lokasi Memori 0x8000 ;------------------------------------------------------------ pop si mov ax, word [si+0x14] shl eax, 16 mov ax, [si+0x1A] mov [fat_next_cluster], eax mov di, 0x8000 baca_cluster_kernel_berikutnya: call bacacluster call bacafat32 mov eax, [fat_next_cluster] and eax, 0x0ffffff0 cmp eax, 0x0ffffff0 jz start_kernel add di, 0x1000 mov eax, [fat_next_cluster] jmp baca_cluster_kernel_berikutnya ;------------------------------------------------------------ ; Mulai eksekusi kernel ;------------------------------------------------------------ start_kernel: jmp 0x8000 hang: jmp hang bacafat32: ; ; Baca sebuah sektor FAT32 ke lokasi 0x7E00 ; Perhitungan berdasarkan nilai memori [fat_next_cluster] ; ; Mengisi fat_next_cluster dengan nilai ; entry fat berikutnya ; Rumus: sector = 62 + 32 + fat_next_cluster / 128 ; offset = (fat_next_cluster % 128 ) * 4 xor eax, eax xor edx, edx mov ax, [fat_next_cluster] mov dx, [fat_next_cluster+2] mov cx, 128 div cx push dx ; remainder, untuk offset nanti add eax, 94; mov word [ds:dap_jumlah_sector], 1 mov word [ds:dap_dest_offset], 0x7E00 mov dword [ds:dap_lba_hi], 0 mov dword [ds:dap_lba_lo], eax call bacasector pop dx shl dx, 2 add edx, 0x7E00 mov ecx, [edx] mov [fat_next_cluster], ecx ret bacacluster: ; ; Baca cluster data berikutnya. ; eax = nomor cluster ; di = offset tujuan ; Rumus menghitung sector dari cluster: ; s = 62 + 15318 + ((c-2)*8) ; dec eax dec eax mov ecx, 8 mul ecx xor ecx, ecx add eax, 15380 adc edx, ecx mov dword [ds:dap_lba_hi], edx mov dword [ds:dap_lba_lo], eax mov word [ds:dap_jumlah_sector], 8 mov word [ds:dap_dest_offset], di call bacasector ret bacasector: mov ah, 0x42 mov dl, 0x80 mov si, dap int 0x13 ret dap db 0x10, 0x00 dap_jumlah_sector dw 0x0001 dap_dest_offset dw 0x0000 dap_dest_segment dw 0x007C dap_lba_lo dd 0x00000000 dap_lba_hi dd 0x00000000 pesan_salah_fat32 db 'Kernel tidak ditemukan.',0 nama_file_kernel db 'KERNEL BIN' fat_next_cluster dd 0x00000000 times 510-($-$$) db 0 dw 0xAA55
Directive [BITS 16] memberi tahu NASM bahwa output dari assembly ini adalah kode program real mode 16-bit. Pada saat BIOS selesai menyalin boot sector, ia akan memindahkan IP pada 0x7C00 sehingga instruksi yang akan dikerjakan adalah 0x7C00. Itu sebabnya terdapat directive [ORG 0x7C00].
Assembly di atas jauh dari sederhana, sehingga masih banyak perbaikan yang dapat dilakukan. Salah satunya adalah saya menganggap 1 cluster terdiri atas 8 sector dan 1 sector terdiri atas 512 bytes. Hal ini berlaku pada UFD saya, tetapi mungkin berbeda pada UFD yang di-format secara berbeda. Selain itu, saya menganggap sector yang menampung informasi Root Directory dimulai dari lokasi sector ke 15.318. Saya juga menganggap jarak dari MBR ke boot sector adalah 62 sector. Kode assembly yang lebih baik akan menyertakan proses perhitungan (berdasarkan informasi field di boot sector) sehingga bootstrap loader menjadi fleksibel.
Kode assembly yang ada pada dasarnya akan memeriksa linked list di FAT32 dan membaca seluruh cluster yang berisi informasi Root Directory. Bootstrap loader akan mencari file bernama KERNEL.BIN. Bila file ini ketemu, isi dari file tersebut akan disalin ke lokasi memori 0x8000, dan eksekusi akan dilanjutkan pada lokasi memori tersebut.
Kode assembly di atas dapat diproses menjadi sebuah boot sector dengan menggunakan NASM:
nasm -o boot.bin boot.asm
Output dari perintah di atas adalah sebuah file berukuran 512 bytes dan diakhir dengan 0x55AA. Semuanya memenuhi persyaratan sebuah boot sector. Langkah berikutnya adalah men-copy file tersebut ke boot sector partisi pertama di UFD. Hal ini dapat dilakukan dengan perintah dd di Linux:
$ sudo dd if=boot.bin of=/dev/sdb1 bs=512 count=1
Sebelum mulai restart komputer dan mengatur prioritas boot device di BIOS, pastikan terlebih dahulu bahwa boot flag pada partisi /dev/sdb1 telah di-set menjadi true. Untuk mengatur flag tersebut, seseorang bisa menggunakan perintah fdisk di Linux.
Sekarang, pada saat ingin mencoba kode program real mode, seorang programmer assembly hanya perlu menyimpan program tersebut dengan nama KERNEL.BIN. Setelah itu, copy file KERNEL.BIN ke root directory flash disk. Kini, setiap kali komputer di-boot melalui flash disk, file KERNEL.BIN akan dikerjakan.
Perlu diingat bahwa program real-mode memiliki memori yang terbatas. Akses memori hanya dibatasi pada range 1 MB (bila gate A20 aktif, menjadi 1.114.096 bytes). Area memori ini sudah termasuk yang dipakai untuk Interrupt Descriptor Table (IDT), buffer I/O, dsb sehingga yang tersedia bagi program lebih sedikit lagi. Untuk memakai memori lebih dari 1 MB, programmer harus beralih ke protected mode (itu sebabnya semua sistem operasi modern bekerja pada protected mode).
Anda harus log masuk untuk menerbitkan komentar.