x86 ASSEMBLY İLE ÖZEL (CUSTOM) BOOTLOADER GELİŞTİRME REHBERİ

================================================================================
x86 ASSEMBLY İLE ÖZEL (CUSTOM) BOOTLOADER GELİŞTİRME REHBERİ
================================================================================

GİRİŞ VE TEORİK ALTYAPI
Bir bilgisayarın güç tuşuna basıldığında, anakart üzerindeki BIOS (Basic Input/Output System)
veya UEFI devreye girer. Geleneksel BIOS (Legacy Boot) mimarisinde, sistem donanımı test
edildikten (POST) sonra BIOS, yapılandırılmış önyükleme cihazlarını (Hard disk, USB, Floppy)
sırasıyla kontrol eder.

BIOS, bir diskin önyüklenebilir (bootable) olup olmadığını anlamak için diskin ilk sektörüne
(Master Boot Record - MBR) bakar. Bir disk sektörü standart olarak 512 bayt uzunluğundadır.
Eğer bu 512 baytlık verinin son iki baytı (511. ve 512. baytlar) "0x55" ve "0xAA"
(Boot Signature / Magic Number) değerlerini taşıyorsa, BIOS bu sektördeki makine kodlarını
bellekte özel bir adres olan "0x7C00" adresine yükler ve işlemcinin kontrolünü bu adrese devreder.

Bu 512 baytlık alana "Stage 1 Bootloader" denir. 512 bayt (imza çıkarıldığında sadece 510 bayt),
modern bir işletim sisteminin tamamını yüklemek için kesinlikle yeterli değildir. Bu nedenle
Stage 1'in temel amacı donanımı minimal düzeyde hazırlamak ve diskin diğer sektörlerindeki
gerçek çekirdeği (Kernel) veya "Stage 2" bootloader'ı belleğe yükleyip çalıştırmaktır.

x86 MİMARİSİ: 16-BİT REAL MODE
İşlemci ilk başlatıldığında, 1970'lerin teknolojisine geri dönük uyumluluk (backward compatibility)
sağlamak amacıyla Intel 8086 modunda, yani "16-bit Real Mode"da başlar. Bu modda:
- Bellek adreslemesi maksimum 1 Megabayt (20-bit adres yolu) ile sınırlıdır.
- Segment:Offset bellek modeli kullanılır. Fiziksel adres = (Segment * 16) + Offset şeklinde hesaplanır.
- Hiçbir bellek veya donanım koruması yoktur; belleğin herhangi bir yerine doğrudan yazabilirsiniz.
- BIOS kesmeleri (Interrupts) kullanılabilir. Örneğin ekrana yazı yazdırmak, diskten veri okumak
gibi işlemler için doğrudan donanım sürücüsü yazmak yerine BIOS'un sunduğu INT 10h, INT 13h
gibi servisler çağrılır.

--------------------------------------------------------------------------------
GEREKLİ ARAÇLAR
--------------------------------------------------------------------------------
1. NASM (Netwide Assembler): Yazacağımız Assembly kodunu saf makine diline çevirecek derleyici.
2. QEMU veya Bochs: Yazdığımız bootloader'ı test etmek için kullanacağımız x86 donanım emülatörü.

--------------------------------------------------------------------------------
ADIM ADIM BOOTLOADER KODU (boot.asm)
--------------------------------------------------------------------------------
Aşağıdaki kod, BIOS tarafından 0x7C00 adresine yüklendiğini varsayarak bellekteki segment
register'larını ayarlar, stack'i (yığın) güvenli bir bölgeye konumlandırır, ekrana BIOS
kesmesi kullanarak bir mesaj yazdırır ve ardından CPU'yu durdurarak sonsuz bir döngüye girer.

[bits 16] ; İşlemciye 16-bit Real Mode komutları üretmesini söylüyoruz.
[org 0x7c00] ; BIOS'un bu kodu bellekte 0x7C00 adresine yükleyeceğini belirtiyoruz.
; Bu bildirim, assembler'ın label (etiket) adreslerini doğru hesaplamasını sağlar.

start:
; 1. SEGMENT REGISTER'LARININ SIFIRLANMASI
; BIOS kontrolü bize devrettiğinde segment register'larının (DS, ES, SS) hangi durumda
; olduğunu kesin olarak bilemeyiz. Çökmeleri önlemek için hepsini bilinen bir duruma getirmeliyiz.
cli ; İşlemler sırasında BIOS kesintilerini (interrupts) geçici olarak kapat.
xor ax, ax ; AX register'ını 0 yapmanın en hızlı (ve optimize) yolu.
mov ds, ax ; Data Segment 0'landı.
mov es, ax ; Extra Segment 0'landı.
mov ss, ax ; Stack Segment 0'landı.
mov sp, 0x7C00 ; Stack Pointer, bootloader'ın başladığı yerin hemen altına ayarlandı.
; Stack bellekte aşağı doğru (azalan adreslere doğru) büyüdüğü için
; 0x7C00'den geriye doğru gidecek ve kendi bootloader kodumuzu ezmeyecektir.
sti ; Segment yapılandırması bitti, kesintileri tekrar aktif et.

; 2. EKRANA MESAJ YAZDIRMA İŞLEMİ
mov si, msg_boot ; SI (Source Index) register'ına string'in başlangıç bellek adresini yükle.
call print_string ; Alt programı (subroutine) çağırarak ekrana yazdır.

; 3. SONSUZ DÖNGÜ (İŞLETİM SİSTEMİ OLMADIĞI İÇİN DURMAMIZ GEREK)
hang:
hlt ; İşlemciyi uyku moduna al, donanımsal bir kesinti gelene kadar bekle.
; Bu komut işlemcinin %100 yükte çalışıp ısınmasını engeller.
jmp hang ; hlt'den uyanırsa tekrar hang etiketine dön ve sonsuz döngüyü koru.

; ------------------------------------------------------------------------------
; FONKSİYON: print_string
; Görevi: SI register'ının gösterdiği adresteki null (0) ile biten karakter dizisini
; BIOS'un teletype (INT 10h) servisini kullanarak ekrana yazdırır.
; ------------------------------------------------------------------------------
print_string:
mov ah, 0x0E ; AH=0x0E: BIOS Video Kesmesi için "Teletype Output" fonksiyon kodu.
.loop:
lodsb ; SI'nın gösterdiği adresteki 1 baytı AL register'ına al ve SI'yı 1 artır.
cmp al, 0 ; Okunan karakter 0 (null terminator) mı? (String bitti mi?)
je .done ; Eğer 0 ise, string bitmiş demektir, döngüden çık (.done etiketine atla).
int 0x10 ; BIOS'u çağır, AL register'ında bulunan karakteri ekrana bas.
jmp .loop ; Sonraki karakteri okumak için döngünün başına dön.
.done:
ret ; Fonksiyonu çağıran noktaya (call komutunun hemen altına) geri dön.

; ------------------------------------------------------------------------------
; VERİ ALANI (DATA SECTION)
; ------------------------------------------------------------------------------
msg_boot db 'Sistem Baslatiliyor... Custom Bootloader Aktif.', 0x0D, 0x0A, 0
; 0x0D (Carriage Return) imleci satır başına alır.
; 0x0A (Line Feed) imleci bir alt satıra indirir.
; Sonundaki 0 ise string'in bittiğini belirten C-tarzı null byte'tır.

; ------------------------------------------------------------------------------
; BOOT SEKTÖRÜ DOLDURMA VE SİHİRLİ İMZA (MAGIC NUMBER)
; ------------------------------------------------------------------------------
times 510-($-$$) db 0 ; 512 baytlık sektörün kullanmadığımız boş kalan kısmını 0 ile doldur.
; $ = Assembler'ın o an derlemekte olduğu satırın bellek adresi.
; $$ = Geçerli bölümün (section) başlangıç adresi (0x7c00).
; ($-$$) = Şu ana kadar Assembly ile yazdığımız makine kodunun bayt boyutu.
; Bu formül, tam olarak 510. bayta kadar arayı sıfırlar.

dw 0xAA55 ; 511. ve 512. baytlara Boot Sektör İmzası (Magic Number) yazılır.
; BIOS bu imza olmadan sektördeki kodu kesinlikle çalıştırmaz.
; x86 mimarisi Little-Endian olduğu için bu değer diske 55 AA olarak yazılır.

--------------------------------------------------------------------------------
DERLEME VE TEST ETME SÜRECİ
--------------------------------------------------------------------------------
Yukarıdaki kod bloğunu 'boot.asm' adıyla bir dosyaya kaydettikten sonra, sisteminizde
(Linux, macOS veya WSL) komut satırını açıp şu işlemleri gerçekleştirmelisiniz:

1. Makine Koduna Derleme (Assembling):
$ nasm -f bin boot.asm -o boot.bin
Açıklama: NASM derleyicisine, çıktı formatı olarak hiçbir ek veri içermeyen saf "binary"
(-f bin) kullanmasını söylüyoruz. İşletim sisteminden bağımsız (bare-metal) çalışacağımız için
ELF veya PE (exe) gibi metadata barındıran dosya formatlarına ihtiyacımız yoktur; yalnızca
işlemcinin doğrudan okuyup işleyeceği ham makine koduna (raw machine code) ihtiyacımız var.

2. QEMU Emülatörü ile Çalıştırma:
$ qemu-system-x86_64 -drive format=raw,file=boot.bin
Açıklama: Sanal bilgisayarımızı, oluşturduğumuz 512 baytlık boot.bin dosyasını raw formatta
bir hard disk olarak görecek şekilde başlatıyoruz. QEMU ekranı açılacak, BIOS POST işlemini
tamamlayacak ve ekranda "Sistem Baslatiliyor... Custom Bootloader Aktif." yazısını göreceksiniz.

--------------------------------------------------------------------------------
BİR SONRAKİ AŞAMALAR (İLERİ SEVİYE SİSTEM PROGRAMLAMA)
--------------------------------------------------------------------------------
Bu aşama yalnızca sistemin kapısını aralamaktır. Tam teşekküllü bir işletim sistemi
geliştirme sürecinde bu 512 baytlık kodunuzun devamında sırasıyla şunları başarması gerekir:

1. Disk Okuma İşlemleri (INT 13h): BIOS'un INT 13h, AH=02h (Disk Okuma Servisi) kullanılarak
diskin hemen ardındaki sektörlerde yer alan "Stage 2" bootloader veya Kernel (C/C++ ile
derlenmiş çekirdek kod) RAM'e aktarılmalıdır.
2. A20 Line Aktivasyonu: x86 mimarisindeki tarihi bir kısıtlama olan 1 MB bellek sınırını
kaldırmak ve adres yolunu açmak için klavye denetleyicisi veya BIOS üzerinden A20 pini
aktif edilmelidir.
3. GDT (Global Descriptor Table) Kurulumu: Bellek segmentlerinin (kod segmenti, veri segmenti)
başlangıç adresleri ve erişim hakları (limit ve ayrıcalık seviyeleri) tanımlanmalıdır.
4. Korumalı Moda (Protected Mode) Geçiş: İşlemcinin CR0 kontrol register'ındaki en sağdaki
bit (PE - Protection Enable) '1' yapılarak 16-bit Real Mode terk edilip, tam 32-bit (veya
daha sonra 64-bit Long Mode) mimariye geçiş yapılmalıdır.
5. Kernel'a Atlama: Tüm ortam hazırlandığında, bir 'jmp' (jump) komutu ile kontrol,
yüksek seviyeli dillerle (genellikle C veya Rust) yazılmış işletim sistemi çekirdeğine
teslim edilir.
================================================================================

🔒 Bu içeriği görmek için giriş yapın

 
Yanıt yazmak için giriş yapmalısınız
Forum özelliklerini kullanmak ve Level 2 üyelik satın almak için hesabınıza giriş yapın.
133,775Konular
3,290,556Mesajlar
324,544Kullanıcılar
infaxSon Üye
Üst Alt