Membuat bootstrap loader untuk UFD
25 November 2011 at 11:43 PM Tinggalkan 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 0×8000, 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).
Entry filed under: Assembly. Tags: bootstrap loader, FAT32, NASM, real mode.
Trackback this post | Subscribe to the comments via RSS Feed