linux/arch/i386/boot/bootsect.S

Verildiği gibi bbootsect, bsetup ve bvmlinux'dan oluşan (head.o, misc.o, piggy.o),bzImage'ı yüklüyoruz. İlk floppy sektörü, bbootsect (512 bytes) linux/arch/i386/boot/bootsect.S'dan derlenmiştir. BIOS tarafından 07C0:0'a yüklenir. bzImage'ın sıfırlaması (bsetup ve bvmlinux) henüz yüklenmiş değildir.

Bootsect'i Taşı

SETUPSECTS = 4                     /*varsayılan kurulum sektörü sayısı*/
BOOTSEG    = 0x07C0                /*önyükleme sektörünün orjinal adresi*/
INITSEG    = DEF_INITSEG  (0x9000) /*önyüklemeyi buraya taşıyoruz;yolun dışına*/
SETUPSEG   = DEF_SETUPSEG (0x9020) /*kurulum buradan başlar*/
SYSSEG     = DEF_SYSSEG   (0x1000) /*sistem 0x10000 (65536) adresine yüklendi*/
SYSSIZE    = DEF_SYSSIZE  (0x7F00) /*sistem boyutu: 16-bayt sayısı*/
                                   /*yüklenecek*/
ROOT_DEV   = 0                     /*ROOT_DEV şimdi "build" tarafından yazıldı*/
SWAP_DEV   = 0                     /*SWAP_DEV şimdi "build" tarafından yazıldı*/

.code16
.text

///////////////////////////////////////////////////////////////////////////////
_start:
{
        // kendimizi 0x7C00'den 0x90000'e taşıdık ve oraya sıçradık.
        move BOOTSEG:0 to INITSEG:0 (512 bytes);
        goto INITSEG:go;
}

bbootsect INITSEG:0 (0x9000:0)'a taşınmıştır. Artık BOOTSEG'i unutabiliriz.

Disk parametrelerini Al

///////////////////////////////////////////////////////////////////////////////
// yığıtı ve disk parametre tablosunu hazırla
go:
{
  SS:SP = INITSEG:3FF4;   // yığıtı INITSEG:0x4000-12'ye koy
  /* 0x4000 keyfi bir değerdir >=
    *   bootsect boyutu + setup boyutu + yığıt için oda;
    * 12 disk parametre boyutudur. */
  disk parametresini (0:0078'deki gösterici) \
  INITSEG:3FF4 adresine kopyala (12 bytes);
  // int1E: SYSTEM DATA - DISKETTE PARAMETERS
  yama sektör sayısı 36'ya (parametre tablosunda 4. konum, 1 byte);
  disk parametre tablosu göstericisine (0:0078, int1E) INITSEG:3FF4 ata;
}

SS kaydedicisinden hemen sonra SP'nin başlatıldığından emin olun. SS'nin tavsiye edilen değişiklik yöntemi lss komutunu kullanmaktır (IA-32 Intel Architecture Software Developer's Manual, (Vol.3. Ch.5.8.3. Masking Exceptions and Interrupts When Switching Stacks).

İtme ve çekme gibi yığıt işlemleri artık kabul edilebilir. Önce disk parametrelerinin 12 byte'ı INITSEG:3FF4'e kopyalanmıştı.

///////////////////////////////////////////////////////////////////////////////
// disk sürücü parametrelerini al, özellikle sektör/iz sayısı.
  char disksizes[] = {36, 18, 15, 9};
  int sectors;
{
  SI = disksizes;                         // i = 0;
  do {
probe_loop:
    sectors = DS:[SI++];            // sectors = disksizes[i++];
    if (SI>=disksizes+4) break;     // if (i>=4) break;
    int13/AH=02h(AL=1, ES:BX=INITSEG:0200, CX=sectors, DX=0);
    // int13/AH=02h: DISK - READ SECTOR(S) INTO MEMORY
  } while (sektör okuma hatası);
}

"lodsb" DS:[SI]'dan AL'ye baytları yükler ve SI'yı otomatik olarak arttırır.

İz başına düşen sektör sayısı sectors değişkenine kaydedilir.

Kurulum Kodunu Yükle

bsetup (setup_sects sektörü) bbotsect'den hemen sonra yüklenecektir, örn. SETUPSEG:0. Dikkat ederseniz INITSEG:0200==SETUPSEG:0 ve setup_sects tools/build tarafından bsetup boyutunun linux/arch/i386/tools/build.c'dekiyle eşleşmesi için değiştirilmiştir.

///////////////////////////////////////////////////////////////////////////////
got_sectors:
  word sread;             // geçerli iz için sektörlerin okunması
  char setup_sects;       // tools/build tarafından üzerine yazılmış
{
  print out "Loading";
  /* int10/AH=03h(BH=0): VIDEO - İMLEÇ KONUMUNU VE BOYUTUNU AL
    * int10/AH=13h(AL=1, BH=0, BL=7, CX=9, DH=DL=0, ES:BP=INITSEG:$msg1):
    *   VIDEO - DİZGEYİ YAZ */

  // kurulum-sektörlerini taşınan (0x90200 adresine) önyükleme bloğundan
  // (bootblock) sonra doğrudan yükle.
  SI = &sread;        // sread, head ve track indekslemek için SI kullanımı
  sread = 1;              // önyükleme sektörü okundu

  int13/AH=00h(DL=0);     // reset FDC

  BX = 0x0200;            // bsetup'ı bbootsect'den (512 bytes) hemen sonra oku
  do {
next_step:
    /* silindir çapraz okumayı (cylinder crossing reading) engellemek için,
      * bu sefer kaç tane sektörün oknacağını hesapla */
    uint16 pushw_ax = AX = MIN(sectors-sread, setup_sects);
no_cyl_crossing:
    read_track(AL, ES:BX);          // AX değiştirilmez
    // ES:BX, sread, head and track'e read_track()için değer ata
    set_next(AX);
    setup_sects -= pushw_ax;        // kalanlar - sonraki adım
  } while (setup_sects);
}

SI sread, head ve track değişkenlerini indekslemek için sread adresine atanmıştır. Onlar bellekte art arda bulunur. read_track() ve set_next() yöntemlerinin ayrıntıları için bakınız Diski Oku

Sıkıştırılmış İmgeyi Yükle

bvmlinux (head.o, misc.o, piggy.o) 0x100000 adresine yüklenecektir, syssize*16 bayt.

///////////////////////////////////////////////////////////////////////////////
// vmlinux/bvmlinux'u yükle (head.o, misc.o, piggy.o)
{
  read_it(ES=SYSSEG);
  kill_motor();                           // disket sürücü motorunu kapat
  print_nl();                             // CR LF yazar
}

read_it() ayrıntıları için bakınız Diski Oku. Eğer zImage'ı yüklüyorsak, vmlinux 0x10000 (SYSSEG:0) adresine yüklenir.

bzImage (bbootsect, bsetup, bvmlinux) artık tümüyle bellekte bulunuyor.

Ayarlara Git

///////////////////////////////////////////////////////////////////////////////
// hangi kök aygıtın kullanılacağına bak ve setup.S'e sıçra
  int root_dev;                     // tools/build tarafından üzerine yazılır
{
  if (!root_dev) {
    switch (sectors) {
    case 15: root_dev = 0x0208;     // /dev/ps0 - 1.2Mb
        break;
    case 18: root_dev = 0x021C;     // /dev/PS0 - 1.44Mb
        break;
    case 36: root_dev = 0x0220;     // /dev/fd0H2880 - 2.88Mb
        break;
    default: root_dev = 0x0200;     // /dev/fd0 - auto detect
        break;
    }
  }

  // önyükleme bloğundan sonra doğrudan yüklenen ayar yordamına sıçra
  goto SETUPSEG:0;
}

Denetimi bsetup'a geçirir. linux/arch/i386/boot/setup.S bölümündeki linux/arch/i386/boot/setup.S:start'a bakınız.

Diski Oku

Aşağıdaki işlev bsetup ve bvmlinux'u diskten yüklemek için kullanılır. Dikkat ederseniz syssize linux/arch/i386/tools/build.c içindeki tools/build tarafından değiştirilmiştir.

sread:  .word 0                         # geçerli iz'in (track) sektör okuması
head:   .word 0                         # geçerli kafa
track:  .word 0                         # geçerli iz
///////////////////////////////////////////////////////////////////////////////
// load the system image at address SYSSEG:0
read_it(ES=SYSSEG)
  int syssize;                    /* 16-bayt türünden sistem boyutu
                                   * tools/build tarafından üzerine yazıldı */
{
  if (ES & 0x0fff) die;           // hizalama 64KB değil

  BX = 0;
  for (;;) {
rp_read:
#ifdef __BIG_KERNEL__
    bootsect_helper(ES:BX);
    /* INITSEG:0220==SETUPSEG:0020 - bootsect_kludge,
      *   SETUPSEG:bootsect_helper() gösterici içerir.
      * Bu işlev bazı veriyapılarını başlangıç durumuna getirir
      *   ilk sefer çağırımda,
      *   ve SYSSEG:0'dan 0x100000'a taşır, her seferinde 64KB,
      *   aşağıdaki çağırımda.
      * Bakınız  Bootsect Yardımcısı. */
#else
    AX = ES - SYSSEG + ( BX >> 4);  // kaç tane 16-bayt okuma
#endif
    if (AX > syssize) return;       // herşey yüklendi
ok1_read:
    /* bu sefer uygun AL (okunacak sektörler) al
     * çapraz silindir okumasını ve BX taşmasını önlemek için. */
    AX = sectors - sread;
    CX = BX + (AX << 9);            // 1 sector = 2^9 bytes
    if (CX overflow && CX!=0) {     // > 64KB
      AX = (-BX) >> 9;
    }
ok2_read:
    read_track(AL, ES:BX);
    set_next(AX);
  }
}

///////////////////////////////////////////////////////////////////////////////
// diski parametrelerle oku (sread, track, head)
read_track(AL sektörler, ES:BX hedef)
{
  for (;;) {
    printf(".");
    // int10/AH=0Eh: VIDEO - TELETYPE ÇIKTI

    // sread, track, head) değerlerine göre CX, DX değerlerini ata
    DX = track;
    CX = sread + 1;
    CH = DL;

    DX = head;
    DH = DL;
    DX &= 0x0100;

    int13/AH=02h(AL, ES:BX, CX, DX);
    // int13/AH=02h: DISK - SEKTÖRLERİ BELLEĞE OKU
    if (disk okuma başarılı) return;
    // "addw $8, %sp" önceki 4 "pushw" işlemini iptal etmek için.
bad_rt:
    print_all();            // yazma hata kodu, AX, BX, CX ve DX
    int13/AH=00h(DL=0);     // reset FDC
  }
}

///////////////////////////////////////////////////////////////////////////////
// set ES:BX, sread, head and track for next read_track()
set_next(AX sectors_read)
{
  CX = AX;                        // sektörleri oku
  AX += sread;
  if (AX==sectors) {
    head = 1 ^ head;        // head'i 0 ve 1 arasında değiştir
    if (head==0) track++;
ok4_set:
    AX = 0;
  }
ok3_set:
  sread = AX;
  BX += CX && 9;
  if (BX overflow) {              // > 64KB
    ES += 0x1000;
    BX = 0;
  }
set_next_fn:
}

Bootsect Yardımcısı

setup.S:bootsect_helper() sadece bootsect.S:read_it() tarafından kullanılır.

bbootsect ve bsetup yarı ayrı bağlandıkları için kendi kod/veri bölütlerine bağlı konumlar kullanırlar. Değişik bölütlerde bootsect_helper() için "call far" (lcall) çağırmak zorundayız ve "return far" dönmeli. Bu çağırma, CS!=DS durumunu yaratan CS değişikliği ile sonuçlanır ve setup.S içindeki değişkenleri belirtmek için bölütü niteleyici kullanmak zorundayız.

///////////////////////////////////////////////////////////////////////////////
// bzImage yüklendiğinde bootsect yükleyici tarafından çağırılır
bootsect_helper(ES:BX)
  bootsect_es = 0;                // setup.S içinde tanımlı
  type_of_loader = 0;             // setup.S içinde tanımlı
{
  if (!bootsect_es) {             // ilk sefer için çağırılır
    type_of_loader = 0x20;  // bootsect-yükleyici, version 0
    AX = ES >> 4;
    *(byte*)(&bootsect_src_base+2) = AH;
    bootsect_es = ES;
    AX = ES - SYSSEG;
    return;
  }
bootsect_second:
  if (!BX) {                      // 64KB full
    // SYSSEG:0'dan hedefe taşı, her seferinde 64KB
    int15/AH=87h(CX=0x8000, ES:SI=CS:bootsect_gdt);
    // int15/AH=87h: SİSTEM - GENİŞLETİLMİŞ BELLEĞE KOPYALA
    if (kopyalama hatası) {
      bootsect_panic() {
        prtstr("INT15 refuses to access high mem, giving up.");
bootsect_panic_loop:            goto bootsect_panic_loop;   // never return
      }
    }
    ES = bootsect_es;       // ES'i daima 0x10000 noktasına ata
    *(byte*)(&bootsect_dst_base+2)++;
  }
bootsect_ex:
  // AX içindeki taşınmış çerçeveler (16-bayt)
  AH = *(byte*)(&bootsect_dst_base+2) << 4;
  AL = 0;
}

///////////////////////////////////////////////////////////////////////////////
// data used by bootsect_helper()
bootsect_gdt:
  .word   0, 0, 0, 0
  .word   0, 0, 0, 0

bootsect_src:
  .word   0xffff

bootsect_src_base:
  .byte   0x00, 0x00, 0x01                # base = 0x010000
  .byte   0x93                            # typbyte
  .word   0                               # limit16,base24 =0

bootsect_dst:
  .word   0xffff

bootsect_dst_base:
  .byte   0x00, 0x00, 0x10                # base = 0x100000
  .byte   0x93                            # typbyte
  .word   0                               # limit16,base24 =0
  .word   0, 0, 0, 0                      # BIOS CS
  .word   0, 0, 0, 0                      # BIOS DS

bootsect_es:
  .word   0

bootsect_panic_mess:
  .string "INT15 refuses to access high mem, giving up."

type_of_loader değerinin değiştiğine dikkat edin. Bu konu Yükleyici Türünü Kontrol Et bölümünde açıklanmıştır.

Muhtelif

Kalanlar destek işlevleridir; değişkenler ve "gerçek kip çekirdek başlığı" parçaları. Dikkat ederseniz .text bölütündeki veri kod olarak bulunur, böylece yüklendiğinde uygun bir şekilde başlangıç durumuna getirilebilir.

///////////////////////////////////////////////////////////////////////////////
// bazı küçük işlevler
print_all();  /* hata kodu yaz, AX, BX, CX and DX */
print_nl();   /* CR LF yaz*/
print_hex();  /* SS:BP tarafından gösterilen kelimeyi onaltılık olarak yaz*/
kill_motor()  /* disket sürücü motorunu kapat */
{
#if 1
  int13/AH=00h(DL=0);     // reset FDC
#else
  outb(0, 0x3F2);         // outb(val, port)
#endif
}

///////////////////////////////////////////////////////////////////////////////
sectors:        .word 0
disksizes:      .byte 36, 18, 15, 9
msg1:           .byte 13, 10
                .ascii "Loading"

"gerçek kip çekirdek başlığı"nın bir parçası olan bootsect taşıyıcı 497. konumdan başlar.

.org 497
setup_sects:    .byte SETUPSECS       // tools/build tarafından üzerine yazılır
root_flags:     .word ROOT_RDONLY
syssize:        .word SYSSIZE         // tools/build tarafından üzerine yazılır
swap_dev:       .word SWAP_DEV
ram_size:       .word RAMDISK
vid_mode:       .word SVGA_MODE
root_dev:       .word ROOT_DEV        // tools/build tarafından üzerine yazılır
boot_flag:      .word 0xAA55

Bu "başlık" linux/Documentation/i386/boot.txt içindeki yerleşim örneğiyle uyuşmalıdır.

Konum   Proto   İsim            Anlam
/Boyut
01F1/1  ALL     setup_sects     setup'ın sektör cinsinden boyutu
01F2/2  ALL     root_flags      Sıfırdan farklı ise, kök dizin salt okunur
                                olarak bağlanır
01F4/2  ALL     syssize         KULLANMAYIN - sadece bootsect.S kullanımı için
01F6/2  ALL     swap_dev        KULLANMAYIN - atıl oldu
01F8/2  ALL     ram_size        KULLANMAYIN - sadece bootsect.S kullanımı için
01FA/2  ALL     vid_mode        Video kip kontrolü
01FC/2  ALL     root_dev        Varsayılan kök aygıt sayısı
01FE/2  ALL     boot_flag       0xAA55 sihirli numara

Kaynakça

<IA-32 Intel Mimarisi Yazılım Geliştiricisinin Kılavuzu> belgesine bu belgede bir çok kez başvurulduğundan kısaca "IA-32 Kılavuzu" diyeceğim.