25 Ekim 2020 Pazar

Disk Sistemi Nedir, Ne Değildir? #3: Boot Sektörü


Merhaba. Serinin önceki iki yazısında disklerin fiziksel yapısını ve nasıl bölümlere ayrıldığını ele aldım. Bu yazıda konu biraz daha genelden özele ilerleyecek. Linux'ta DOS'tan bildiğimiz boot sektörü bulunmuyor. Windows'ta da NTFS kendi başına bir yazı konusu olacak kadar karışık. Bu nedenle bu yazıda DOS'a yoğunlaşacağım. Bu yazıda anlatacaklarım, Wikipedia'da Volume Boot Record (VBR) maddesinde ele alınan yapı olacak. Neden bu yapıyı boot sektörü olarak adlandırmayı tercih ettiğimi yazının sonundaki bölümde açıkladım. Şimdi, nedir bu boot sektörü?

Evet, Nedir Bu Boot Sektörü?
Boot sektörü, FAT ve NTFS (ve öncülü HPFS) dosya sistemlerinde disk bölümünün ilk sektörüdür. Ext ve XFS gibi dosya sistemlerinde bu yapı bulunmaz. Yapısında bir kod bloğu ve diskle ilgili bazı bilgiler bulunur. İşlev olarak MBR'ye benzer, şöyle ki, MBR işletim sisteminin yüklenmesi için boot sektörünü yükleyip işleyişi ona devrediyorsa; boot sektörü de boot sürecini devam ettirip DOS çekirdeği olan IO.SYS veya NT ön-çekirdeği olan NTLDR ve sonrasında NTOSKRNL.EXE dosyasını yüklemelidir. Bu arada Windows Vista'dan itibaren NTLDR yerine artık /boot/bcd dosyası bulunmaktadır [1].

Disketler ve bazı USB diskler bölümlenemez. Bunun nedeni bir MBR'lerinin olmamasıdır. Buna karşın sabit diskler MBR'leri sayesinde bölümlenebilir. BIOS ve MBR kodunun ne yaptığını (önceki yazıdan) biliyoruz. Ayrıca üzerine basa basa, BIOS bakış açısından MBR ile boot sektörü arasında fark olmadığını yazmıştım (Alıntı: Code in the MBR and VBR is in essence loaded the same way. [2]). Sistem sabit diskten açılırken:
  1. BIOS, ilk sektörden MBR'yi okur ve çalıştırır.
  2. MBR kodu, boot edilebilir disk bölümün ilk sektörünü okur ve çalıştırır.
  3. Boot sektör kodu, IO.SYS'yi bulup yükler ve süreç devam eder.
DOS, disketten açılırken ikinci adım çalışmaz çünkü disketin ilk sektörü boot sektörüdür. Ve boot sektör kodu da MBR kodu gibi 0:7C00h adresine yüklenir. 

Hatırlatma: UEFI'yle işletim sisteminin yüklenmesi için başka bir algoritma sunar [3]. Yukarıdaki algoritma UEFI'nin olmadığı ortamlarda doğrudur.

[1]: Windows NT 6 startup process
[2]: https://en.wikipedia.org/wiki/Volume_boot_record
[3]: https://en.wikipedia.org/wiki/Boot_sector#UEFI


Boot Sektöründe Neler Var?
Boot sektöründe genel olarak bulunduğu diskin yapısı ve dosya sistemine ait veriler bulunur. Bunlarla yükleme işlemi için gerekli dosyaların diskteki yeri hesaplanır. Dosya sisteminin mantıksal birimi olan kümelere (cluster), CHS/LBA'ya ait bilgiler, DOS'ta dir çıktısındaki disk etiketi, dosya yerleşim tablosu (FAT) sürümü bu bilgilerden başlıcalarıdır. Bu yazıda önce elimdeki sistemlerin boot sektörlerini dosyalara kopyalayacağım. Ardından boot sektör formatı üzerinden bu dosyaları inceleyeceğim.

Yazının sonunda (Boot sektörüyle VBR farkı başlığında) da açıkladığım üzere, kavramlar için kullandığım terimler Wikipedia'dakinden biraz farklı. Yukarıda bahsettiğim veriler Wikipedia'da Volume boot record maddesinden ayrı olarak BIOS parameter block (BPB) ve Design of the FAT file system maddelerinde açıklanıyor. Kişisel görüşüm BIOS parameter block ifadesinin BIOS Data Area (BDA) ile karıştırılabilir olduğu yönünde.


Boot Sektörü Okumak
Boot sektörü Windows'ta okumak için yine HxD'yi kullandım. Önceki yazıda yaptığım gibi HxD'yi yönetici olarak çalıştırıp MBR'yi açtım*. Dizüstü bilgisayarımın diskinde üç bölüm vardı, bunların Windows'un kurulu olduğu sonuncusuyla ilgileniyorum (önceki yazıdaki ekran görüntüsünde görülebilir). Bu bölümün hangi sektörde başladığını önceki yazıdaki bilgileri kullanarak bulabilirim. Üçüncü bölümün başlangıç adresi olan 1E6h adresindeki UInt32 değeri üstteki Sector bölümüne yazıp boot sektörünü görüntüledim. Bunu yalnızca NTFS boot sektörünün FAT'a ne kadar benzediğini göstermek için yapıyorum.

*GPT disklerde bölümleri bulmak için GUID girdisini okumak gerekir. GPT formatını ileride ayrı bir yazıda ele alacağım.


Kırmızı bölge kod, mavi bölge veri ve yeşil boot sektör imzası.

DOS622 makinasında iki tane birincil disk bölümü oluşturup formatlamıştım. Bu makinayı Damn Small Linux (DSL) ile başlatıp komut satırını açıyorum. Linux'ta boot sektörü okumak için herhangi bir program kurmak gerekmiyor. sudo fdisk -l ile diskleri kontrol ettiğimde /dev/hda1 ve /dev/hda2 olmak üzere iki disk görünüyor. Diski dd ile okuyup çıktıyı hexdump'a yönlendirebilir veya hexdump ile diski okuyup head ile ilk 32 satırına (512 byte) bakabilirim:

sudo hexdump -C -v /dev/hda1 | head -n 32

veya

sudo dd if=/dev/hda1 count=1 bs=512 | hexdump -C -v

Aynı komutlar /dev/hda2 için de tekrarlanabilir. Bu verileri kendi makinama kopyalamaya ihtiyacım yok ama gerekirse scp ile bir önceki yazıda anlatıldığı üzere kolayca kopyalayabilirim.

FreeDOS makinasındaysa bir tane birincil, bir tane uzatılmış disk bölümü oluşturup uzatılmış bölüm içinde de iki sanal bölüm daha oluşturmuş ama formatlamamıştım. DSL ile açıp sudo fdisk -l ile disk bölümlerine baktığımda, birincil bölüm /dev/hda1 ve uzatılmış bölüm /dev/hda2'yi görüyorum.

Disk /dev/hda: 1073 MB, 1073741824 bytes
64 heads, 63 sectors/track, 520 cylinders
Units = cylinders of 4032 * 512 = 2064384 bytes

   Device Boot    Start       End    Blocks   Id  System
/dev/hda1   *         1        173     348736+   6  FAT16
/dev/hda2           174        520     699552    5  Extended
/dev/hda5           174        346     348736+   6  FAT16
/dev/hda6           347        520     350752+   6  FAT16


Başlangıç ve bitiş sektörleri gözönünde bulundurulduğunda /dev/hda5 ve /dev/hda6 uzatılmış bölümdeki sanal bölümler. Yukarıdaki komutları /dev/hda2 için çalıştırdığımda önceki yazıda diskeditor'le de gördüğüm, kodu olmayan ama bölümleme tablosu bulunan sektörü görüyorum. /dev/hda5 ve /dev/hda6'da 0F6h byte'ları olan sektörler görünüyor çünkü bu diskleri formatlamamıştım. Bu da demek oluyor ki, disk bölümü formatlandığında boot sektörü formatlandığı dosya sistemine göre yazılıyor.

Peki neden 0F6h? Bu değer, Atari disketleri formatlanırken boş alanlara yazılması için düşünülmüş. Sonra IBM bu şekilde PC standardına eklemiş ve o günden bu yana değiştirilmemiş. Bildiğim kadarıyla özel bir nedeni yok.

Son olarak bir de bir disketin boot sektörüne bakmak istiyorum. Bunun için kendi linux makinamda (host) dd ile 1.44 MB'lik disket büyüklüğünde bir dosya oluşturacağım:

dd if=/dev/zero of=floppy.ima bs=512 count=2880

2880, 80 silindir, 18 sektör ve 2 kafanın çarpımından, bir disketteki toplam sektör sayısı. Windows'ta bu büyüklükte bir txt dosya oluşturulup sonradan adı değiştirilebilir. Önemli olan dosyanın 1 474 560 byte olması.

Dosyayı oluşturduktan sonra DOS6.22'yi açıp Settings -> Storage'dan, Floppy Controller'a oluşturduğum sanal disketi takıyorum. A: sürücüsüne bu haliyle geçersem General failure reading drive A hatasını alırım. (Not: FreeDOS'ta formatlarken format komutunun bir bug'ına denk geldim. Bu yüzden FreeDOS'ta disketin iki kere formatlanması gerekiyor. İlk deneme hata alıyor.)

format A: /S

/S parametresiyle disketi, sistem disketi olarak formatladım yani formatlandıktan sonra içine IO.SYS ve COMMAND.COM gibi dosyalar aktarıldı. Disketin içine bakmak için floppy.ima'yı hexdump'la açmak yeterli. Windows'da yine HxD'yi kullanmak gerekiyor.


Boot Sektörü Veri Yapıları
BIOS parameter block maddesinde ele alınan yapılardan, günümüzde neredeyse yalnız NTFS için olan kullanımda. Oluşturduğumuz sanal makina disklerinde ve diskette DOS 4.0 EBPB var. Önce DOS 4.0 EBPB'ye bakalım:

Sektör OffsetiBüyüklükAçıklama
0x003 byteAsıl koda JMP içerir
0x038 byteOEM AdıNot1
0x0BwordSektörün içerdiği byte
0x0DbyteKümenin içerdiği sektör sayısı
0x0EwordAyrılmış (reserved) sektör sayısı
0x10byteFAT (dosya yerleşim tablosu) sayısı
0x11wordKök dizindeki en fazla girdi sayısıNot2
0x13wordToplam sektör sayısıNot3
0x15byteMedya tanımlayıcıNot4
0x16wordFAT başına sektör sayısı
0x18wordSilindirin içerdiği sektör sayısı
0x1AwordKafa sayısı
0x1CdwordGizli sektörlerin sayısı
0x20dwordToplam sektör sayısıNot5
0x24byteFiziksel sürücü numarasıNot6
0x25byteAyrılmışNot7
0x26byteGenişletilmiş imza (0x28 veya 0x29)
0x27dwordBölüm seri numarası
0x2B11 byteBölüm etiketi
0x368 byteDosya sisteminin türü
Wikipedia'dan derlenmiştir. (madde1, madde2)

Not1: OEM adı alanı diski formatlayan sisteme ait bir imzadır. Normalde özel bir değeri yoktur. Ancak keyfi değerler bazı DOS sürümlerinin disketi okumamasına neden olabilir.
Not2: (FAT32 öncesi) FAT dosya sisteminin yapısı gereği kök dizin "\" altında belli bir sayıdan fazla girdi (dosya+dizin) oluşturulamaz. Tasarım gereği bu dizin özeldir, her zaman ikinci kümede olmalıdır ve büyüklüğü sabittir. Elbette bu istisna alt dizinleri kapsamaz.
Not3: Bu alan 16-bit olduğundan, bölüm sektör sayısı 65 535'den büyük olması durumunda yerini 0x20'deki alana bırakır. Çoğunlukla değeri sıfırdır.
Not4: (Media descriptor) Bu byte diskin türünü belirlemek için kullanılır. Sabit diskler için değeri 0xF0 ve 1.44M disketler için 0xF8'dir. Diğer değerlerle uygulamada karşılaşılacağını düşünmüyorum.
Not5: 0x13'teki alan kullanımdan kaldırılınca (Not3'e bakınız) yerini bu alan almıştır. Eğer 0x13'ün yanısıra bu alan da sıfırsa işletim sistemi toplam sektör sayısını MBR'den okur.
Not6: Disket sürücüler için sırayla 0x00, 0x01... Sabit diskler için 0x80, 0x81 v.b.
Not7: Her ne kadar bu alan ayrılmış (reserved) olarak belirtilse de, WinNT Chkdsk komutu burayı diskteki hatalar için bir flag biti olarak kullanır.

Bu bilgilerle disketin boot sektörünü inceleyelim:

00000000  eb 3c 90 4d 53 44 4f 53  35 2e 30 00 02 01 01 00  |.<.MSDOS5.0.....|
00000010  02 e0 00 40 0b f0 09 00  12 00 02 00 00 00 00 00  |...@............|
00000020  00 00 00 00 00 00 29 05  14 76 1b 4e 4f 20 4e 41  |......)..v.NO NA|
00000030  4d 45 20 20 20 20 46 41  54 31 32 20 20 20 fa 33  |ME    FAT12   .3|


İlk üç byte JMP +3Ch; NOP komutunu içeriyor. JMP'ın kendisi iki byte olduğuna göre kodun başlangıcı 2+3Ch=3Eh offsetinde. 0FAh, 033h, 0C0h byte'ları CLI, XOR AX,AX komutlarını içeriyor (0C0h yukarıda görünmüyor). Diğer değerler sırasıyla;

OEM Adı: MSDOS5.0
Byte/Sektör: 0200h = 512 byte
Sektör/Küme: 1
Ayrılmış Sektörler: 1 (boot sektörün kendisi)
FAT Sayısı: 2
Kök Dizindeki Max. Girdi Sayısı: 0E0h = 224
Toplam Sektör Sayısı1: 0B40h = 2880
Medya Tanımlayıcı: 0F0h (1.44M Disket)
Sektör/FAT: 9
Sektör/Silindir: 12h = 18
Kafa: 2
Gizli Sektörler: 0
Toplam Sektör Sayısı2: 0
Fiziksel Sürücü Num.: 0
Ayrılmış: 0
Genişletilmiş imza: 29h
Bölüm seri numarası: 05h, 14h, 76h, 1Bh = 1B76-1405 (Yukarıdaki ekran görüntüsündeki değerle aynı)
Bölüm etiketi: NO NAME
Dosya Sistemi: FAT12

Aynı şekilde DOS6.22'nin sabit diskine bakalım:

00000000  eb 3c 90 4d 53 44 4f 53  35 2e 30 00 02 04 01 00  |.<.MSDOS5.0.....|
00000010  02 00 02 00 00 f8 fa 00  3f 00 10 00 3f 00 00 00  |........?...?...|
00000020  e1 e7 03 00 80 00 29 12  b6 0b 4f 4d 53 2d 44 4f  |......)...OMS-DO|
00000030  53 5f 36 20 20 20 46 41  54 31 36 20 20 20 fa 33  |S_6   FAT16   .3|


OEM Adı: MSDOS5.0
Byte/Sektör: 0200h = 512 byte
Sektör/Küme: 4 yani her küme 2048 byte.
Ayrılmış Sektörler: 1 (boot sektörün kendisi)
FAT Sayısı: 2
Kök Dizindeki Max. Girdi Sayısı: 0200h =512
Toplam Sektör Sayısı1: 0
Medya Tanımlayıcı: 0F8h (Sabit disk)
Sektör/FAT: 250
Sektör/Silindir: 3Fh = 63
Kafa:10h = 16
Gizli Sektörler: 3Fh = 63
Toplam Sektör Sayısı2: 03 E7E1h = 255 969
Fiziksel Sürücü Num.:80h
Ayrılmış: 0
Genişletilmiş imza: 29h
Bölüm seri numarası: 12h, 0B6h, 0Bh, 4Fh = 4F0B-B612
Bölüm etiketi: MS-DOS_6
Dosya Sistemi: FAT16


Gelelim FAT32'nin yapısına. Wikipedia'da bu DOS 7.1 EBPB olarak geçiyor ve yapının ilk 36 byte'ı aynı.

Sektör OffsetiBüyüklükAçıklama
0x0036 byte(FAT16 yapısına bakınız)
0x24dwordFAT başına sektör sayısı
0x28wordAynalama flag'larıNot8
0x2AwordSürüm
0x2CdwordKök dizin kümesiNot9
0x30wordFSINFO sektörüNot10
0x32wordYedek boot sektörüNot11
0x3412 byteAyrılmışNot12
0x40byteFiziksel sürücü numarasıNot6
0x41byteAyrılmışNot7
0x42byteGenişletilmiş imza (0x28 veya 0x29)
0x43dwordBölüm seri numarası
0x4711 byteBölüm etiketi
0x528 byteDosya sisteminin türü
Wikipedia'dan derlenmiştir. (madde1, madde2)

Not8: Normalde dosya yerleşim tablosu en az iki kopya tutulur ve her dosya okuma yazma işlemi bu iki kopyaya da işlenir. Bu flag ile istenirse yalnız bir tablonun aktif olması sağlanabilir.
Not9: Not2'de belirtilen tasarım kısıtlarını kaldırmak için yeni tasarıma böyle bir alan eklenmiştir.
Not10: FAT32'yle birlikte küme sayısının teorik üst sınırı 65526'dan 228=268 435 456'ya çıktı. 232 değil 228, çünkü 4 bit ayrılmıştır. Bu durumda bile dosya sistemindeki boş alanı hesaplamak veya boş bir küme bulmak için incelenmesi gereken çok fazla küme vardır ve bu performans sorunlarına neden olur. Bu nedenle bu bilgileri tutmak için FSINFO adında ayrı bir veri yapısı tanımlanmıştır. Bu yapı genelde birinci sektörde yani boot sektörün hemen sonrasında tutulur ve RRaA imzasıyla anlaşılır.
Not11: FAT32'yle birlikte, bölümün ilk sektörünün bozulmasına karşı yedek boot sektörü tutulur. Bu her zaman altıncı sektördedir. MBR kodu eğer sıfırıncı sektörü okuyamazsa hardcoded olarak altıncı sektöre bakar. Bu değer kodda olmasına rağmen boot sektörde de bir değer ayrılmıştır ama altıdan başka bir değer olması tavsiye edilmez.
Not12: Bu alanın adı dökümantasyonda "boot file name" olarak görünüyor. Normalde boot sektörün yükleyeceği dosya adı boot kodunda yer alır. Bu veriyi sabit bir alanda tutmak için ayrılmış olabileceği akla geliyor.

Bu arada yukarıdaki notların bir kısmını Microsoft'un FAT32 File System Specification belgesinden derledim. Maalesef Microsoft'un sitesinde bu belge artık yok ama buradan indirilebilir. 

Son olarak NTFS disk bölümlerinin de buna çok benzer bir yapısı var. FAT32 gibi bu yapının da ilk 36 byte'ı FAT16 ile hemen hemen aynı.

Sektör OffsetiBüyüklükAçıklama
0x0036 byte(FAT16 yapısına bakınız)
0x24byteFiziksel sürücü numarasıNot6
0x25byteAyrılmış / Kullanılmıyor
0x26byteGenişletilmiş imza (0x80)
0x27byteAyrılmış
0x28qwordBölümdeki sektör sayısı
0x30qwordAna dosya tablosunun (MFT)
bulunduğu küme numarası
0x38qwordDosya tablosunun ikinci kopyasının
bulunduğu küme numarası
0x40dwordFile Record Segment (FRS)'de
bulunan küme sayısıNot13
0x44dwordIndex block'taki küme sayısıNot13
0x48qwordBölüm seri numarası
0x50dwordChecksum
Wikipedia ve microsoft.com'dan derlenmiştir. (madde1, madde2, madde3)

Not13: 0x40 ve 0x44 offsetlerindeki iki alan aslında dword değil 1 byte + 3 byte ayrılmış olarak tanımlıdır. 3 byte'lık ayrılmış alanın FAT32'de farklı bir kullanımı olduğundan uyumluluk için kasıtlı bırakılmış olabilir. Daha doğrusu bir FAT32 disk programı yanlışlıkla NTFS disk bölümünü bozamasın diye.

Yazının başında da belirttiğim gibi NTFS boot sektörü öncülü FAT'a benzemekle birlikte daha karışık. Bu nedenle yazıyı uzatmamak için NTFS'i ve boot sektöründeki kodu ayrı bir yazıda ele alacağım.

MBR yazısında diskin büyüklüğü hakkında iki farklı terimden bahsetmiştim. Bunlardan biri ham (raw) alan diğeri biçimlendirilebilir (formatlanabilir) alandı. Tabloda ayrılmış sektörler ve gizli sektörler olmak üzere iki alan var, bunların pratikte bazı hesaplamalarda kullanılıyor ve teoride kök dizinle boot sektör arasına yer açmak için de kullanılabilir. Bunlardan başka, bölümdeki dosyaların yerlerini tutan iki tane FAT var. Bu yapılar diskte olmadığında dosya sisteminden söz edilemez, başka bir deyişle dosyalar yazılamaz veya yazılsa bile başı nerede sonu nerede bilinemez. Diskin ham alanı MBR'deki sektör sayısı 512 byte'la (sektördeki byte sayısı) çarpıldığında çıkan alanken, formatlanabilir alan yukarıda sayılan yapıların format sonrasında ham alandan çıkarılmasıyla geri kalan alandır. 

Örn. bir disket için 1 474 560 byte'lık (2880 sektör) bir dosya oluşturmuştum. Bu onun ham alanıdır. Başka bir deyişle bir diskete herhangi bir dosya yapısı olmadan bu kadar byte yazabilirim. Aşağıdaki ekran görüntüsünde DOS formatlanmış disketle ilgili ne diyor?

2880 sektörden 2847 tanesi kullanılabilir. 2x16 sektör FAT ve 1 sektör de boot sektör olmak üzere toplamda 33 sektör dosya sisteminin kullanımına. Dolayısıyla bu diskette 1 457 664 byte'dan daha büyük bir dosya oluşturamam. 

Bu iddia, DSL ile boot edip aşağıdaki komutlarla denenebilir:

sudo mount /dev/fd0 /mnt
sudo rm /mnt/*
sudo dd if=/dev/zero of=/mnt/test
sudo ls -la /mnt

Hatırlayalım; önceki yazıda oluşturduğum FreeDOS makinasında, 3 disk bölümü var. İkisi genişletilmiş (extended) bir tanesi birincil. DOS622 makinasında 2 birincil disk bölümü var ve diskin yarısında bölüm yok. Şimdi yukarıda yazdıklarım için bir kolaylık göstereceğim ancak maalesef sadece Norton Disk Editörü olanlara.

DOS622 makinasında diskedit.exe'yi (v2000) çalıştırdım ve Alt+A ile diskin bölümleme tablosunu görüntüledim (sağ üst görsel). Hemen ardından bir daha Enter'a bastım (kursör birinci bölüm üzerindeyken) ve boot sektör tüm bilgileri okunmuş şekilde açıldı (sağ alt). Üstelik sağdaki sütunda DOS'un raporladığı değerler de gösteriliyor. Bu ne işe yarar? Bozuk bir diski veya disketi (elbette fiziksel olarak bozuk olmayan) DOS çalışan sağlam bilgisayarıma takıp açtığımı varsayalım. Disk editörle kontrol ettiğimde boot sektöründe "Sectors per cluster" değeri işletim sisteminin raporladığından farklı olsun. Bu değeri olması gereken değerle değiştirerek -küçük bir ihtimal de olsa- diski okunabilir duruma getirebilirim. Bu ekrandaki bölüm etiketi ve bölüm seri numarasını not edip, disk editörden çıktıktan sonra dir komutuyla doğrulayabilirim.

Disk editörde tekrar Alt+A tuş kombinasyonuyla MBR'yi görüntüleyip (sağ alttaki "Absolute Sector 0" ifadesine dikkat) diğer bölümü de (veya FreeDOS'ta) aynı şekilde kontrol etmek mümkün. Aralarında büyük bir fark olmadığından ayrıca incelemeyeceğim.


Boot Kodu Nasıl Çalışır?
 
a. FreeDOS Boot Kodu
Bu bölümde hem FreeDOS'un hem de DOS'un boot kodlarını inceleyeceğim. Öncelikle belirtmeliyim ki, FreeDOS boot kodu disketlerde (FAT12) ve disklerde (çoğunlukla FAT16) biraz farklı. Aradaki farkla yeri geldiğinde değineceğim.

FreeDOS'ta bir disket, /S parametresi olmadan formatlandığında boot kodu, basit bir hata mesajıyla boot işlemini sonlandırıyor. Başka bir deyişle boot edebilen bir kod yazılmıyor. Bunun nedeni, FreeDOS boot kodu DOS'a göre daha karmaşık olduğundan "Missing IO.SYS" vb. hata mesajlarına yer kalmaması olabilir. Bu kod çok basit olduğu için üzerinde durmayacağım.
FreeDOS'u incelemenin kolay yanı açık kaynak kodlu olması. FreeDOS'un boot kodunu github'dan indirdim. Bazı yerlere kendi açıklamalarımı da ekledim. Bunlar "; --" şeklinde başlıyor. Dosyanın orjinali buradan, benim açıklamalarımı içeren dosya buradan indirilebilir. Yaptığı iş çok karışık değil. Önce bir jmp komutu (satır** 69) asıl kod parçasına dallanıyor. Bundan hemen sonra sektörün veri yapılarına ait tanımlar var (s73-90). Kodun 0:7C00h adresinde çalıştığını söylemiştim. Kod, 0060h:0 adresine yükleceği kernelin, kendi üzerine yazmasını engellemek için kendini 1FE0h:0 adresine kopyalıyor (s162-168) ve bir far jmp ile kopyalanan kodda kaldığı yerden çalışmasını sürdürüyor. Kernel yüklendiğinde, bu kodun çalışması tamamlanmış olsa bile, kernel.sys'nin boot sektöründeki bazı verilere erişmesi gerekebilir.

**: Satır numaralarını açıklamalarımı eklediğim dosyaya göre verdim.

Boot kodu açısından üç tane önemli veri var. 
1. FAT başlangıcı (fat_start): Dosya yerleşim tablosunun başladığı sektör, gizli sektörlerle ayrılmış sektörlerin toplamı.

fat_start = hidden_sectors + reserved_sectors

Ayrılmış sektörlerin boot sektörüyle FAT arasında yer açmak için kullanılabileceğini belirtmiştim. Bu alan, FAT'in boot sektöründen kaç sektör sonra bulunduğunu bildirir. FAT12 ve FAT16 için bu alan hemen hemen her zaman 1'dir, yani FAT boot sektöründen hemen sonra başlar. Gizli sektörler terimi biraz yanıltıcı olup aslında o boot sektörün kaçıncı sektör olduğunu tutar. Başka bir deyişle o boot sektörün LBA adresidir. Bu alan yalnız boot ederken hesaplamaya katılır. Boot edilmeyen bir diskte sıfır olması hiç bir sorun oluşturmaz.
 
2. Kök dizin tablosunun başlangıcı (root_dir_start): FAT başlangıcına, FAT sayısıyla FAT başına sektör sayısının çarpımının eklenmesiyle bulunan sektör.

root_dir_start = fat_start + number_of_FATs * sectors_per_FAT

3. Diskte dosya verisinin bulunduğu ilk sektör (data_start): Bu da kök dizindeki girdi sayısının, bir sektördeki byte sayısının 32'de birine bölünüp, kök dizin tablosunun başlangıcı (yukarıdaki) değerine eklenmesiyle bulunur. Çünkü bir dosya girdisi 32 bytedır. Sektördeki byte sayısının 32'ye bölünmesiyle bir sektörün içerebileceği girdi sayısı bulunur. Toplam girdi sayısı buna bölündüğünde kök dizin tablosunun sektör cinsinden uzunluğu bulunur.

data_start = root_dir_start + root_dir_entries / (bytes_per_sector >> 5)

Not: Dosya yerleşim tablosu ve dizin tablolarının girdileri, ayrıntılı olarak bir sonraki yazının konusu olacak.

192 ile 228. satırlar arasında bu değerler hesaplanıp ilgili değişkenlere atanıyor. Bellekteki adresleri sırayla 7BD2h, 7BD6h ve 7BDAh. Sonrasında kök dizin tablosunun tamamı belleğe okunuyor (s238-243). 240. satırda pop edilen DI'nin değeri 221. satırda push edilen AX'in değerinden geliyor. Bu da data_start değeri hesaplanırken bulunan, kök dizinin sektör cinsinden uzunluğu. 249 ile 263. satırlar arasında kök dizin tablosunda kernel.sys dosyası aranıyor. Dosya bulunursa bu dosyanın bulunduğu küme (cluster) stack'e atılıp (s263) dosya yerleşim tablosunun tamamı (sectors per FAT tane sektör) diskten okunuyor (s277-290). 

FreeDOS'ta disk (FAT16) ve disket (FAT12) boot kodunun farklı olduğu yer bu nokta. Muhtemelen bu kod format.com içinde hem FAT12 hem FAT16 için derlenmiş halde bulunuyor. Tablonun yapısını ve nasıl çözümlendiğini gelecek yazıda açıklayacağım. Basitçe tablo linked list gibi, o kümeden sonraki kümenin hangisi olduğunu veya dosyanın orada bitip bitmediğini tutuyor. Bu kümelerin listesi 0060h:2000h'da bir listeye word word (satır 301 ve 327) yazılıyor. Bu liste bir döngüyle (s. 349-355) okunup ilgili kümelere karşılık gelen sektörler (yani kernel.sys) 0060h:0 adresine yazılıyor ve 353. satırdaki far jmp ile kernel çalıştırılıyor. 

Dikkat edilirse, kod kompleks (ve dolayısıyla uzun) olduğundan hata mesajı olarak yalnıca bir "Error!." (s. 379) var. readDisk fonksiyonunda her okunan sektör için ekrana bir nokta karakteri basılıyor. Bu, muhtemelen bir sektör okunamadığında hangisinde sorun olduğunu saptamak için gerekli. Fonksiyonda LBA desteği test ediliyor ama hem disket için LBA desteklenmiyor hem de zaten disketten boot ediliyorsa bu test çalıştırılmıyor (s. 416). LBA destekleniyorsa disk okuması daha önceden oluşturulan DAP paketiyle yapılıyor aksi halde read_normal_BIOS (s. 442) bölümü çalıştırılıyor. Bu bölümde (s. 445-486) LBA_SECTOR_0 ve LBA_SECTOR_16 adreslerinde tutulan dword değer alınıp CHS adrese çevriliyor. Bu fonksiyon her sektörü ES:63A0h'ya okuyup sonra fonksiyona girişte ES:BX'te tutulan hedefe kopyalıyor (s. 493-500).

FreeDOS aynı zamanda FAT32 diskleri de destekliyor. Ben önceki yazıda sanal makinayı kurarken FAT32'yi bilerek devre dışı bırakmıştım. Bu nedenle mevcut disk boot sektörü yukarıda bahsettiğim FAT16 kod bloğu dışında tamamen disket boot sektörüyle aynı. Ancak FAT32 diskler için oluşturulmuş boot32.asm ve boot32lb.asm adında iki ayrı boot sektör kodu daha var. Bunları incelersem bile ayrı bir yazıda inceleyeceğim.


b. DOS Boot Kodu
DOS boot kodu kaynak kodu olmamasına rağmen görece basitliği nedeniyle incelemek daha kolay. Yukarıda oluşturduğum floppy.ima dosyasını kullandım. Bu disketi önceden formatlamıştım. Dolayısıyla boot sektörü var. Bunun ilk 512 byte'ını linux'ta dd komutuyla ayırdım. 

dd if=floppy.ima of=floppy.bin count=1 bs=512 

Sonra MBR için yaptığım gibi objdump ile disassemble ettim:

objdump -M intel -D -b binary -m i8086 --adjust-vma=0x7C00 floppy.bin > floppy.asm

floppy.asm dosyasındaki veri yapılarını ve metinleri elle ayıklayıp değişkenlere dönüştürdüm ve elbette yorum ekledim. Dosya buradan indirilebilir.

Burada da benzer şekilde en başta 7C00h adresinde jmp komutu var. Kodun başında stack ayarlanıyor ve hemen sonra disket parametre tablosu değiştiriliyor (7C3Eh - 7C70h arası). Bu tablo disket sürücü denetleyicisiyle ilgili parametreleri tutan ve int 1Eh vektörünün gösterdiği bir tablo (dolayısıyla çağırılabilir değil). Bu tabloya sürücünün desteklemediği anlamsız değerler girmek makinanın kilitlenmesine yol açıyor. Muhtemelen burada kafa konumlandırma süreleriyle oynayarak biraz daha hızlı boot edilmesi amaçlanmış. Fakat işin garibi bu kod parçası disklerde de bulunuyor.

Bu tablonun bir kopyası oluşturulup (7C59h), bazı değerler değiştirildikten sonra bu kopyanın göstericisi int 1Eh vektörüne yazılıyor (7C68h) ve int 13h bir kere daha çağırılarak disket sürücü denetleyicisi yeni parametrelerle resetleniyor. Ardından FreeDOS'taki benzer hesaplamalar burada da yapılıyor. Önce FAT sektörlerinin sayısı bulunuyor (7C87h) sonra bu değere gizli ve ayrılmış sektörlerin sayısı ekleniyor. Bütün bu değerler 32-bit olarak DX:AX'te işlem görüyor. Dikkat edilirse burada değişkenler daha önceden çalışmış kodun üzerine yazılıyor. data_start bellekte 7C49h ve root_dir_start 7C50h (7C9Ah) adreslerinde tutuluyor. Kök dizin listesinin sektör sayısı farklı bir yolla hesaplansa da (7CA8h-7CB6h) aynı sonucu veriyor. 7C50h'deki root_dir_start sektörü 7CCBh'da CHS adresine dönüştürülüp 7CD2h'de diskten okunuyor. 

Yukarıda bütün kök dizin listesi değil yalnızca ilk sektörü okunuyor. Ardından listenin başında IO.SYS ve sonraki kayıtta MSDOS.SYS olup olmadığına bakılıyor ve içlerinden herhangi biri olması gerektiği yerde yoksa kod hata verip bilgisayarı yeniden başlatıyor1.

7D05h adresinde başlayan loadkernel fonksiyonu IO.SYS'nin başlangıç kümesini alıyor, FreeDOS gibi 2 çıkarıyor ve bu dosyanın ilk sektörünü bulup readloop içerisinde yalnızca 3 (7D1Dh) ardışık sektör okuyor2 3. Döngü bittiğinde kod IO.SYS'ye 3 tane değer geçiriyor (7D3Eh): Dosyaların bulunduğu ilk sektör AX:BX'te, CH'de media descriptor byte ve DL'de boot edilen fiziksel sürücü numarası. Ve 7D4Dh'da 0070h:0 adresine yüklenen kernele dallanıyor. 

DOS boot sektörü aşağıdaki üç nedenden (veya kısıttan) dolayı daha basit ancak diğer yandan da birşeylerin ters gitmesine çok açık:

1: DOS kernel dosyalarının kaydının birinci ve ikinci sırada olmasını şart koşuyor. Dosyalar diskte olsalar bile kayıtları farklı bir yerdeyse boot edemiyor. FreeDOS'ta bir disketin boot kodu boot edilmesine olanak veriyorsa, kernel.sys istenirse sonradan elle kopyalansın, yine de boot edilebilir. DOS'ta edilemez.
2: Eğer IO.SYS'de bir defragmantasyon söz konusuysa belleğe okunan dosyanın IO.SYS olduğunun bir garantisi yok. 
3: Dosyanın boyutundan bağımsız üç sektör okunuyor. IO.SYS kendisinin geri kalanını belleğe okumaktan kendi sorumlu.


Linux'ta Bu Yapılar Nasıl?
Kısaca söylemem gerekirse bu yapıların hiçbiri yok. Yani ne disk bölümünün ilk sektöründe sektör sayısı, kafa numarası, ayrılmış sektörler var, ne de herhangi çalıştırılabilir bir kod. BIOS ile kernel arasındaki bağlantıyı sağlamak için boot loader'lar var. Örn. GRUB'u ele alırsam, (1) GRUB Stage1 kodu MBR'de yazılı olabilir. Bu durumda GRUB MBR kodu core.img'ı çalıştırır, grub.conf dosyasını bulup istenen işletim sistemini yükler. (2) Veya Stage1 kodu tercihen /boot disk bölümünün ilk sektöründedir. Bölümleme tablosunda bu bölüm boot edilebilirdir ve standart MBR kodu bu bölümün ilk sektöründeki Stage1 kodunu yükler, ardından core.img yüklenerek birinci adımdaki işlemler devam eder. 
 
LILO ve syslinux'ta da benzeri bir süreç söz konusu. Bu arada UEFI de aslında bir boot loader olduğundan, UEFI destekli makinalarda linux çekirdeği herhangi bir boot loader'a gerek kalmadan BIOS'tan UEFI aracılığıyla çağırılabilir.


Ek: Boot Sektörü ile VBR Farkı

Wikipedia'da "Boot sector" maddesi daha önce MBR olarak ele aldığım yapıyı kapsıyor (daha genel). MBR maddesindeki açıklama şöyle diyor: "Bu madde, bölümlenebilen ortamlarda PC'lere özgü boot sektörü hakkındadır. Bölümlenemeyen ortamlardaki ilk sektör için sürücü boot kaydı maddesine bakınız (volume boot record)." Boot sektörü maddesindeki açıklama da şunu diyor: "Bu madde, genel olarak boot sektörü konsepti hakkındadır. PC'lerdeki sürücü boot kaydı VBR ve ana boot kaydı için MBR maddelerine bakın."

Neden böyle bir adlandırma karmaşası oldu? Bu konuları öğrenirken VBR adıyla hiç karşılaşmadım. İki yapıyı birbirinden ayırt etmek için hep MBR ve boot sektörü ifadelerini kullandım çünkü öğrendiğim kaynaklarda da böyleydi. Wikipedia'da VBR maddesindeki referanslarda da VBR ifadesine rastlamadım. Özetle VBR ifadesi görece yeni bir terim olmalı. Michael Tischer'in PC Intern 3.0: Sistem Programlama (1. basım, 1992) kitabından da kontrol ettim: s.557, MBR için Partition Sector deniyor. Boot sektörüyle anlatılan (s.919) benim bu yazıda değindiğim konu.