6 Mayıs 2019 Pazartesi

Disk Sistemi Nedir, Ne Değildir? #1: Diskler


Merhaba. Bir süredir disk sistemi ve dosya yerleşim tablosuyla (FAT) ilgili yazmayı düşünüyordum ama boot sektörüne değinmeden FAT'ten bahsetmek, MBR'ye ve daha da önemlisi donanıma değinmeden de boot sektöründen konuşmak anlamlı gelmediği için konuyu en baştan ele almaya karar verdim. En baştan demişken BIOS'un nerede bitip MBR'nin nerede başladığından da bahsedeceğim ama BIOS'u derinlemesine ele almayacağım (belki başka yazıda).


Diski Adreslemek: CHS, LBA ve Int 13h
Diskte Silindir Kafalar
ve Sektörler
CHS, Cylinder, Head ve Sector kelimelerinin baş harflerinden oluşan bir kısaltma. Çok eski (1970'lerden) disklerle birlikte standartlaşmış ve yapı farklılaşsa da geriye doğru uyumluluktan ötürü kullanılmaya devam edilmiş. Yanda dönen bir diskin iç yapısı görülüyor. Bu disk 4 plaktan (platter) oluşmuş ve erişim için 8 kafa (head) var. Her plakta iç içe halkalar var. Bunlar silindirler. Silindire "track" de deniyor ama ben silindiri kullanacağım. Son olarak, her silindir belli açıda dilimlere bölündüğünde, dilimlerin silindiri kestiği alanlara sektör deniyor. Bu sektörler 512 byte büyüklükte. Günümüzde 4KB'lik sektörler kullanılıyor ama disk denetleyici uyumluluk yüzünden işletim sistemine 512 byte raporluyor. Şimdilik 1980'lerde olduğumuzu varsayalım (keşke). CHS adreslemede 3 tane sayı gerekiyor. Dikkat edilmesi gereken silindir ve kafa numaraları sıfırdan başlarken sektör numaraları her zaman birden başlamalıdır.

Aşağıda daha açıklayıcı bulduğum bir görsel var:

https://superuser.com/questions/974581/chs-to-lba-mapping-disk-storage
Cluster (küme) birkaç sektörden oluşan bir yapı. Dosya sisteminin en küçük birimine deniyor. Bu yazıda değinmeyeceğim çünkü henüz donanımı tanımlıyorum, dosya sistemine gelemedim. Bazı kaynaklarda sektör yerine kullanıldığını gördüm ancak kesinlikle yanlış. Bir küme bir sektöre eşit olabilir (örn. disketlerde) ama zorunda değildir. 

5 MB'lık IBM disk
forklift'le taşınıyor.
Kaynak: thenextweb.com
CHS'nin basit ama kullanışsız bir mantığı var. Dikkatli okuyucu dış silindirlerdeki sektörlerin içtekilere göre büyük olacağını fark etmiştir. CHS, dış sektörlerin verimsiz kullanılmasına yol açıyor. 1970'lerde kullanılan bir kaç on MB'lık ve bir kaç onbin dolar fiyatlı disklerde yapı böyleymiş ve bunlara CAV (constant angular velocity - sabit açısal hız) diskler deniyor. Açısal hız sabit olduğundan kafa her sektör üzerinden aynı sürede geçiyor. Yeri gelmişken, CD ve DVD'lerin CLA (constant linear velocity - sabit doğrusal hız) olduklarını belirteyim. CD'yi okumaya başladığında yavaş döner sona doğru dış sektörlerde hızlanırdı. 1990'larda (hatta 80'lerin sonunda) disklerde zone bit recording teknolojisi geliştiriliyor. ZBR'de geniş dış silindirler fazla sayıda sektöre bölünmüşken iç silindirlerde daha az sektör var. Geriye doğru uyumluluk için denetleyici işletim sisteminden CHS adreslerini alıyor ve arka planda CHS'yi fiziksel sektöre dönüştüren bir formül uyguluyor.

Bu arada hızlıca bir flashback'le günümüze gelelim. SSD'lerde artık ne CHS ne de CAV/ZBR'den söz edilebilir. Dolayısıyla CHS'nin modasının ne kadar geçtiği anlaşılabilir.

LBA adreslemede yalnız doğrusal sektör numarası veriliyor. Örn. 1.44MB'lik High Density (HD) disketlerde 18 silindir, 2 kafa ve 80 sektör vardı (Double Density'lerde (DD) 9 silindir). Son sektörün CHS adresi (17, 1, 80) iken LBA adresi 2879'dur (18 * 2 * 80 = 2880, LBA'de ilk sektör sıfır).

c, silindir; h, kafa ve s, sektör numarası olsun. Nhead, diskteki kafa sayısı ve NSPC bir silindirdeki sektör sayısı olmak üzere CHS'den LBA'ya dönüşüm aşağıdaki formülle yapılır:

LBA(c, h, s) = (c * Nhead + h) * NSPC + (s - 1)    [ 1 ]

LBA'nın bir avantajı aşağıdaki gibi karmaşık yapıda sektörleri olan disklere izin vermesi ama tek avantajı bu değil.

Kaynak: https://venam.nixers.net/blog/unix/2017/11/05/unix-filesystem.html

Int 13h, BIOS'un disk erişimi için sunduğu bir kesme servis rutini (ISR). Bu ISR, 80'lerde CHS kullanıyordu ve büyüyen diskler karşısında zamanla yetersiz kalmaya başlamıştı. Int 13h'ün bir çok fonksiyonu var ama bu yazıda ben 02 ve 03'e yoğunlaşacağım. 02, diskten belleğe okumaya, 03 bellekten diske yazmaya yarıyor. Kesme çağırılmadan önce giriş değerleri aşağıdaki gibi olmalı:

AH = 02h/03h (okuma/yazma)
AL = Okunacak/yazılacak sektör sayısı ( >0 )
CH = Silindir numarasının düşük 8-biti
CL = Sektör numarası 1-63 (bit 0 .. 5) +
Silindir numarasının yüksek 2-biti (bit 6 .. 7)
DH = Kafa numarası
DL = Sürücü numarası: Sabit diskler için 7. bit 1 olmalı.
Örn. Birinci disket sürücü: 00h, ikinci disket sürücü: 01h
Birinci sabit disk: 80h, ikinci sabit disk: 81h
ES:BX: Okunacak yazılacak veri için gösterici


Int 13h için bir örnek hazırladım. debug.exe, Windows XP'de var ama o bile bu kadar düşük seviye bir işleme izin vermiyor. Bunun için bir sanal makinaya DOS6.22 kurdum (sanırım zamanında İTÜ yazılım sunucusundan indirmiştim). Kurulumu sonraki yazıda ele alacağım. Şimdilik yalnız ekran görüntülerini ekledim:

debug.exe ile disk okumak
Yukarıda diskin ilk sektörünü okudum. Okunan byte'ların anlamına sonraki yazıda değineceğim. Bu arada yukarıda, sektörün başı ve sonu olmak üzere iki ekran görüntüsü var.

Int 13h'ü kullanmak istemeseydim, erişimi IDE denetleyicisinin portlarına erişerek yapmam gerekecekti. Yazının devamında bununla ilgili bir örnek vereceğim.

Şimdi bir hesap yapalım. CHS adresleme için toplam 3 byte ayrılmış: CH/CL/DH. 8+6+10=24 bit. 512 byte * 224 = 8 GB. Üstelik sektör numarası 0 olamadığından aslında bundan daha az: 218 * 63 * 512 byte = 7.875 GB. Yani CHS, 8 GB'dan büyük diskleri adreslemede yetersiz! LBA'nın adresleme konusunda da avantajı var ama bunlara girmeden önce ATA standartlarına bakmamız gerek.


(Paralel) ATA Standardı
Yukarıda disk erişimine BIOS tarafından baktık. O tarafta işlerin karışık olması yetmezmiş gibi donanım tarafı da karışık. 1986'da Western Digital ilk IDE/ATA standardını duyurduğunda adresleme için 22-bit ayrılmıştı: yanlış bilmiyorsam 10-bit silindir, 4-bit kafa ve 8-bit sektör. 1994'te EIDE/ATA-2 standardında silindir 16-bit oldu ve adresleme 28-bit'e çıktı. Üstelik CHS'nin yanısıra 28-bit LBA adresler de destekleniyordu. Ancak IBM, BIOS kesmeleri ve diğer (MBR) standartları oluşturmuştu ve buna göre (örn. int 13h) silindir 10-bit, kafa 8-bit ve sektör 6-bit'di. Ortak bölenler alındığında CHS, 10+4+6 = 20-bit ve sektör başına 512 byte'la 512 MB'ye kadar olan diskleri destekleyebiliyordu. Aslında sektör numaraları da birden başladığından bundan daha da az.*

Standartları değiştirmek geriye doğru uyumluluk nedeniyle imkansızdı. Neyse ki o yıllarda LBA'nın temelleri atılıyordu ve IBM'in standardındaki bir saçmalık bu soruna geçici bir çözüm sağladı: IBM disk kafalarına 8-bit ayırmıştı ama 3.5 inç form faktörlü 1 inç yüksekliğindeki sabit diske 128 plak veya 256 kafanın nasıl sığdırılabileceğini düşünmemişti. Bunun pratikte imkansız olması sayesinde 512 MB'den büyük disklerin silindir sayısını 10-bit'le sınırlamak için işletim sistemine diskte varolandan fazla sayıda kafa raporlanıyordu. Int 13h kodunda bu sayılar [1] formülünde yerine koyulup LBA'ya dönüştürülerek erişiliyordu, elbette BIOS'ta da LBA'nın aktifleştirilmesi koşuluyla.**

*: Bu paragraf için ayrıntılı bilgi:
https://en.wikipedia.org/wiki/Logical_block_addressing#Enhanced_BIOS
https://en.wikipedia.org/wiki/Parallel_ATA

**: Bu paragraf için ayrıntılı bilgi:
https://en.wikipedia.org/wiki/Logical_block_addressing#LBA-assisted_translation


Örnek:
2 GB'lik bir diskin fiziksel yapısında 8 kafa, 8320 silindir ve her silindirde 63 sektör olduğunu varsayalım. İşletim sistemi, int 13h'le disk parametrelerini sorguladığında dönecek değer 128 kafa, 520 silindir ve 63 sektör olacak. Çarpım değişmedi. Aynı işletim sistemi, diske GÇ portları üzerinden doğrudan erişmek isteseydi 16'dan büyük kafa numaralarını porta nasıl göndereceğini bilemeyecekti.

Bu diskte CHS 100,17,17 adresine erişilmek istendiğini düşünelim. [1]'deki formülde değerler yerine konulduğunda:

LBA = (100 * 128 + 17) * 63 + (17 - 1) = 807 487 sektörüne erişilecek.

1990'larda ATA-2 standardı 28-bit'le 128GB'a kadar diskleri destekleyebiliyor ama IBM'in mevcut arabirimi LBA dönüşümüyle 8 GB'a kadar diskleri destekleyebiliyordu (IBM, MBR'de LBA için 32-bit ayırmıştı. Gelecek yazıda değineceğim). 1990'ların ortasında IBM ve Microsoft, 42h ve 43h gibi fonksiyonlarla int 13h'ün yeteneklerini genişlettiler. Diğer yandan MBR'deki LBA kaydı 32-bit olduğundan ATA standardının da ilerlemek için alanı vardı. 2003'te ATA-6 standardıyla disklerde fiziksel adresleme 48-bit'e çıktı ve aynı zamanda CHS adresleme tarihe karıştı.


BIOS Diske Nasıl Erişir? Disk Denetleyicisi
IDE/ATA disk denetleyicisinin portlarıyla ilgili dökümantasyon Ralf Brown Interrupt List'in D bölümünde ports.b dosyasında bulunabilir. Eskiden anakartlarda iki EIDE disk denetleyicisi oluyordu. Bunlara da master/slave olmak üzere ikişer disk bağlanabiliyordu. Denetleyicilerin taban (base) adresi sırayla 01F0h ve 0170h'ydi. Portlar şu şekilde:

Port | GÇ | Aciklama
-----+----+----------------------------------------------------
01F0 | RW | Veri yazmacı
01F1 | R- | Hata yazmacı++
01F1 | -W | (Write Precompensation Cylinder divided by 4)+
01F2 | RW | Sektör Sayısı
01F3 | RW | Sektör Numarası (CHS)
     |    | 0-7. adres bitleri (LBA)
01F4 | RW | Silindir numarası düşük anlamlı byte (CHS)
     |    | 15-8. adres bitleri (LBA)
01F5 | RW | Silindir numarası yüksek anlamlı byte (CHS)
     |    | 23-16. adres bitleri (LBA)
01F6 | RW | Sürücü ve kafa numarası
     |    | 27-24. adres bitleri (LBA)
01F7 | R- | Durum yazmacı++
01F7 | -W | Komut yazmacı++


+: Bu değerin yeni disklerde anlamı yok. Sıfır olabilir. Ne olduğunu açıklayacağım.
++: Bu yazmaçlardaki bitlerin anlamları için Ralf Brown Interrupt Listesi'ndeki tabloya bakınız.

01F6h göründüğünden biraz daha karışık. Geriye doğru uyumluluk nedeniyle 5 ve 7. bitler her zaman 1 olmak zorunda (bunlar sektörü 512 byte olmayan MFM disklerde kullanılıyordu). 6. bit sıfırsa adres CHS, birse LBA olarak işlem görür. 4. bit sıfırsa işlem master, birse slave diske aittir. 3-0. bitler adres bitleridir. Buraya kadarki bilgi aslında kodu yazmak için yeterli ama henüz elimizde kodu yazabileceğimiz bir yer yok. İsteyen okur bir DOS veya FreeDOS sanal makina kurup kendi deneyebilir.

Okumak kolay olsun diye satırın başına offset adreslerini ve solda açıklamaları yazdım. Kodu debug.exe'ye a (Assemble) komutuyla girmek gerekiyor.

0100    mov ax,0001
0103    mov dx,01F2
0106    out dx,al    ; Sektor sayisi = 1
0107    inc dx       ; dx = 01F3
0108    out dx,al    ; Sektor numarasi = 1
0109    inc dx       ; dx = 01F4
010A    dec ax       ; ax = 0000
010B    out dx,al    ; Silindir D = 0
010C    inc dx       ; dx = 01F5
010D    out dx,al    ; Silindir Y = 0
010E    inc dx       ; dx = 01F6
010F    mov al,A0    ; CHS modu, master disk
0111    out dx,al
0112    inc dx       ; dx = 1F7
0113    mov al,20
0115    out dx,al    ; 20h ATA Sektor okuma komutu
0116    in al,dx     ; Durum yazmacini oku
0117    test al,58   ; 0101 1000: Drive ready | Seek complete | Buffer ready
0119    jz 0116      ; Dataya erisene kadar bekle
011B    mov dx,01F0
011E    mov bx,0200
0121    in ax,dx     ; Datayi oku (word)
0122    mov [bx],ax  ; Bellege kopyala
0124    inc bx
0125    inc bx
0126    cmp bx,0400  ; 0200h byte
012A    jnz 0121
012C    int 3        ; Breakpoint


Kodu çalıştırmak için g (Go) komutunu verip, d (Dump) ile belleği kontrol ettiğimde sorunsuz (int 13h çıktısıyla aynı) okunduğunu görüyorum:


Bu kod debugger'da çalıştırmak için uygun. Sondaki int 3, int 20 ile değiştirildikten sonra aşağıdaki komutlarla kaydedilebilir:

rbx
0000
rcx
002E
nreadmbr.com
w

Kodun ekrana çıktı vermediği unutulmamalıdır. Yüklemek için dosya adı, ya debug.exe'ye parametre olarak verilir veya n komutuyla girilir ve l (Load) komutuyla yüklenir. LBA kodunu bir sonraki yazıya bırakıyorum.


Biraz Daha Teori
Write Precompensation Cylinder (WPC): Yazının başlarında ZBR olmayan CHS disklerde dış sektörlerin daha büyük alanı olduğunu yazdım. Bu disklerde üretici bir silindir belirler ve bundan dıştaki silindirlere yazarken yazma kodlamasını biraz değiştirmesi gerekir. Bu kodlama değişimine "write precompensation" adı verilir. Bu silindiri BIOS mu raporlar yoksa kullanıcı mı girer bilmiyorum. Böyle bir diskim olmadı.

Çok eski disklerde kafalar bir step motora bağlıydı. Step motorların sıcaklık hassasiyeti düşük olduğundan "voice coil motor" adında bir teknoloji kullanılmaya başlandı. Aşağıdaki video nasıl çalıştığına dair bir fikir verecektir:

Bu motorlar, uygulanan akım oranında kafayı sürüklüyor, akım kesildiğinde de bir yayla, kafa otomatik olarak boş bir alana taşınıyordu.

Landing Zone (LZ): Step motorlu disklerde bilgisayar kapatılmadan önce kafaların LZ adı verilen, içinde data olmayan bir silindirin üstüne taşınması yani "park" edilmesi gerekirdi. Kafaları park edilmemiş diskler hareket ettirilirse, kafa veri silindirleri üzerinde hareket ederek diski çizebilirdi. Int 13h'ün 19h altrutini bu park etme işlemini yapar. Günümüzde park etme gereksiz, dolayısıyla LZ de anlamsızdır.

Voice coil motorlar, step motorlardan daha az hassas oldukları için üreticiler şöyle bir çözüm buldular. Örn. 3 plakalı bir diskin bir yüzüne kafaların daha hassas yer belirlemesi için belli işaretler koydular. En üstteki kafa bu işaretlere göre yerini belirleyip gerekiyorsa geri beslemeyle konumunu düzeltiyordu. Böylelikle ilginç bir şekilde tek sayıda kafası olan diskler ortaya çıktı.

Kaynak: https://www.brainbell.com/tutors/A+/Hardware/_Actuator_Arms.htm

Donanımla şimdilik işim bitti. Sonraki yazıda artık MBR'den söz edeceğim.