| 
 | ||||||
.text
///////////////////////////////////////////////////////////////////////////////
startup_32()
{
  /* bölütleri bilinen değerlere ata */
  cld;
  DS = ES = FS = GS = __KERNEL_DS;
#ifdef CONFIG_SMP
#define cr4_bits mmu_cr4_features-__PAGE_OFFSET
  /* long mmu_cr4_features linux/arch/i386/kernel/setup.c içinde tanımlı
    * __PAGE_OFFSET = 0xC0000000, örn. 3G */
  // CR4 desteği ile ApP (> Intel 486) CR'ü BSP'den kopyalayacak
  if (BX && cr4_bits) {
    // sayfalama seçeneklerini aç (turn on) (PSE, PAE, ...)
    CR4 |= cr4_bits;
  } else
#endif
  {
    /* sayfa tablolarını (pg0..empty_zero_page-1) sadece BSP ilklendirir
      *   .org 0x2000'de pg0
      *   .org 0x4000'de empty_zero_page
      *   toplam (0x4000-0x2000)/4 = 0x0800 girdi */
    pg0 = {
      0x00000007,             // 7 = PRESENT + RW + USER
      0x00001007,             // 0x1000 = 4096 = 4K
      0x00002007,
      ...
    pg1:    0x00400007,
      ...
      0x007FF007              // toplam 8M
    empty_zero_page:
    };
  }
  . = 0xC0000000 + 0x100000;
  _text = .;                    /* Metin ve salt-okunur veri */
  .text : {
        *(.text)
...
[root@localhost boot]# nm --defined /boot/vmlinux-2.4.20-28.9 | \ grep 'startup_32\|mmu_cr4_features\|pg0\|\<empty_zero_page\>' | sort c0100000 t startup_32 c0102000 T pg0 c0104000 T empty_zero_page c0376404 B mmu_cr4_features
// sayfa dizini temel göstericisini ata, fiziksel adres CR3 = swapper_pg_dir - __PAGE_OFFSET; // sayfalama etkin! CR0 |= 0x80000000; // PG bitini ayarla goto 1f; // flush prefetch-queue 1: EAX = &1f; // sonraki komutu takip eden adres goto *(EAX); // EIP'yi yeniden konumla 1: SS:ESP = *stack_start;
#define OLD_CL_MAGIC_ADDR       0x90020
#define OLD_CL_MAGIC            0xA33F
#define OLD_CL_BASE_ADDR        0x90000
#define OLD_CL_OFFSET           0x90022
#define NEW_CL_POINTER          0x228   /* Gerçek kip veriye göreli */
#ifdef CONFIG_SMP
  if (BX) {
    EFLAGS = 0;             // AP EFLAGS'leri temizler
  } else
#endif
  {
    // İlk CPU BSS'yi temizler
    clear BSS;              // örn. __bss_start .. _end
    setup_idt() {
      /* idt_table[256]; arch/i386/kernel/traps.c içinde tanımlı
        *   .data.idt bölümüne yerleştirilmiş
      EAX = __KERNEL_CS << 16 + ignore_int;
      DX = 0x8E00;    // kesme kapısı, dpl = 0, mevcut
      idt_table[0..255] = {EAX, EDX};
    }
    EFLAGS = 0;
    /*
      * Önyükleme parametrelerini yolun dışına kopyala (ayak altından al).
      * _empty_zero_page'in ilk 2kB'lık bölümü önyükleme parametreleri için,
      * ikinci 2kB'lık bölümü komut satırı içindir.
      */
    taşı *ESI (gerçek kip başlık)'dan empty_zero_page'e, 2KB;
    temizle empty_zero_page+2K, 2KB;
    ESI = empty_zero_page[NEW_CL_POINTER];
    if (!ESI) {             // 32 bitlik komut satırı göstericisi
      if (OLD_CL_MAGIC==(uint16)[OLD_CL_MAGIC_ADDR]) {
        ESI = [OLD_CL_BASE_ADDR]
              + (uint16)[OLD_CL_OFFSET];
        taşı *ESI'dan empty_zero_page+2K'ya, 2KB;
      }
    } else {                // 2.02+'da geçerli
      taşı *ESI'dan empty_zero_page'e, 2KB;
    }
  }
}
struct cpuinfo_x86;                  // bakınız: include/asm-i386/processor.h
struct cpuinfo_x86 boot_cpu_data;    // bakınız: arch/i386/kernel/setup.c
#define CPU_PARAMS      SYMBOL_NAME(boot_cpu_data)
#define X86             CPU_PARAMS+0
#define X86_VENDOR      CPU_PARAMS+1
#define X86_MODEL       CPU_PARAMS+2
#define X86_MASK        CPU_PARAMS+3
#define X86_HARD_MATH   CPU_PARAMS+6
#define X86_CPUID       CPU_PARAMS+8
#define X86_CAPABILITY  CPU_PARAMS+12
#define X86_VENDOR_ID   CPU_PARAMS+28
checkCPUtype:
{
  X86_CPUID = -1;                 // CPUID yok
  X86 = 3;                        // en azından 386
  save original EFLAGS to ECX;
  flip AC bit (0x40000) in EFLAGS;
  if (AC bit not changed) goto is386;
  X86 = 4;                        // en azından 486
  flip ID bit (0X200000) in EFLAGS;
  restore original EFLAGS;        //  AC ve ID seçenekleri için
  if (ID bit değişemez) goto is486;
  // işlemci bilgilerini al
  CPUID(EAX=0);
  X86_CPUID = EAX;
  X86_VENDOR_ID = {EBX, EDX, ECX};
  if (!EAX) goto is486;
  CPUID(EAX=1);
  CL = AL;
  X86 = AH & 0x0f;                // aile
  X86_MODEL = (AL & 0xf0) >> 4;   // model
  X86_MASK = CL & 0x0f;           // adımlama kimliği (stepping id)
  X86_CAPABILITY = EDX;               // özellik
is486:
  // PG, PE, ET'yi kaydet AM, WP, NE, MP'yi ise ayarla
  EAX = (CR0 & 0x80000011) | 0x50022;
  goto 2f;                   // "is386:" işlemeyi atla
is386:
  orjinal EFLAGS'ları ECX'den yeniden al;
  // PG, PE, ET'yi kaydet MP'yi ayarla
  EAX = (CR0 & 0x80000011) | 0x02;
  /* ET: Eklenti Türü (Extension Type) (CR0'ın 4 biti).
    * Intel 386 ve Intel 486 işlemcilerde bu seçenek atandığı (set) zaman
    * Intel 387 DX aritmetik işlemcisi komutlarının desteklendiğini gösterir.
    * Pentium 4, Intel Xeon ve P6 ailesi işlemcilerde ise
    * bu seçenek sabit 1 olur.
    *     -- IA-32 Manual Vol.3. Ch.2.5. Control Registers (p.2-14) */
2:
  CR0 = EAX;
  check_x87() {
    /* Doğru olması için ET'ye bağımlıyız.
     * Bu 287/387 için sınar. */
    X86_HARD_MATH = 0;
    clts;                   // CR0.TS = 0;
    fninit;                 // Init FPU;
    fstsw AX;               // AX = ST(0);
    if (AL) {
      CR0 ^= 0x04;    // yardımcı işlemci (coprocessor) yok, EM'i ata
    } else {
      ALIGN
1:    X86_HARD_MATH = 1;
      /* IA-32 Manual Vol.3. Ch.18.14.7.14. FSETPM komutu
        * 287 işlemcisinin korumalı kipte olduğunu söyler
        * 387 tarafından dikkate alınmaz*/
      fsetpm;
    }
  }
}
  ready:  .byte 0;        // global değişken
{
  ready++;                // kaç işlemci hazır
  lgdt gdt_descr;         // yeni betimleme tablosunu güvenli yerde kullan
  lidt idt_descr;
  goto __KERNEL_CS:$1f;   // "lgdt"den sonra bölüt yazmaçlarını yeniden yükle
1:
  DS = ES = FS = GS = __KERNEL_DS;
#ifdef CONFIG_SMP
  SS = __KERNEL_DS;       // sadece bölütü yeniden yükle
#else
  SS:ESP = *stack_start;  /* init_task_union'ın sonu,
                           * linux/arch/i386/kernel/init_task.c'de tanımlı */
#endif
  EAX = 0;
  lldt AX;
  cld;
#ifdef CONFIG_SMP
  if (1!=ready) {         // ilk işlemci değil
    initialize_secondary();
    // bakınız: linux/arch/i386/kernel/smpboot.c
  } else
#endif
  {
    start_kernel(); // bakınız: linux/init/main.c
  }
L6:
  goto L6;
}
ENTRY(stack_start)
  .long init_task_union+8192;
  .long __KERNEL_DS;
#ifndef INIT_TASK_SIZE
# define INIT_TASK_SIZE 2048*sizeof(long)
#endif
union task_union {
  struct task_struct task;
  unsigned long stack[INIT_TASK_SIZE/sizeof(long)];
};
/* INIT_TASK ilk görev tablosunu kurmak için kullanıldı,
 * riski göze alarak kullanın! Base=0, limit=0x1fffff (=2MB) */
union task_union init_task_union
  __attribute__((__section__(".data.init_task"))) =
    { INIT_TASK(init_task_union.task) };
///////////////////////////////////////////////////////////////////////////////
// öntanımlı kesme yöneticisi ("handler")
ignore_int() { printk("Unknown interrupt\n"); iret; }
/*
 * Kesme belirtici tablosu 256 idt için odaya (room) sahiptir,
 * global belirtici tablosu sahip olabileceğimiz
 * görev sayısına bağımlıdır...
 */
#define IDT_ENTRIES     256
#define GDT_ENTRIES     (__TSS(NR_CPUS))
.globl SYMBOL_NAME(idt)
.globl SYMBOL_NAME(gdt)
  ALIGN
  .word 0
idt_descr:
  .word IDT_ENTRIES*8-1           # idt 256 girdi içerir
SYMBOL_NAME(idt):
  .long SYMBOL_NAME(idt_table)
  .word 0
gdt_descr:
  .word GDT_ENTRIES*8-1
SYMBOL_NAME(gdt):
  .long SYMBOL_NAME(gdt_table)
/*
 * Bu, 0-8M'de (önyükleme amaçları için) bir kimlik eşleşmesi ve
 * PAGE_OFFSET sanal adresinde başka bir 0-8M eşleşmesi oluşturmak
 * üzere ilklendirilir.
 */
.org 0x1000
ENTRY(swapper_pg_dir)   // "ENTRY" linux/include/linux/linkage.h'da tanımlı
  .long 0x00102007
  .long 0x00103007
  .fill BOOT_USER_PGD_PTRS-2,4,0
  /* öntanımlı: 766 girdi */
  .long 0x00102007
  .long 0x00103007
  /* öntanımlı: 254 girdi */
  .fill BOOT_KERNEL_PGD_PTRS-2,4,0
/*
 * Sayfa tablolarının burada sadece 8MB'ı ilklendirilir
 * - sonuncu sayfa tabloları bellek boyutuna bağlı
 * olarak daha sonra ayarlanır.
 */
.org 0x2000
ENTRY(pg0)
.org 0x3000
ENTRY(pg1)
/*
 * empty_zero_page hemen sayfa tablosunu takip etmelidir !
 * (İlklendirme döngüsü empty_zero_page'e kadar sayar)
 */
.org 0x4000
ENTRY(empty_zero_page)
/*
 * normal "text" bölütünün gerçek başlangıcı
 */
.org 0x5000
ENTRY(stext)
ENTRY(_stext)
///////////////////////////////////////////////////////////////////////////////
/*
 * Bu veri bölümünü başlatır. Dikkat ederseniz yukarıda tümü
 * text bölümündedir çünkü bu bizim başka bir şekilde
 * gideremeyeceğimiz hizalama gereksinimidir.
 */
.data
ALIGN
/*
 * Tipik olarak 140 "quadwords" içerir; NR_CPUS'a bağlı olarak.
 *
 * DİKKAT! Herhangi bir şeyi değiştirirseniz, bunun head.S'deki
 * gdt belirticisiyle eşleştiğinden emin olun.
 */
ENTRY(gdt_table)
  .quad 0x0000000000000000        /* NULL belirtici */
  .quad 0x0000000000000000        /* kullanılmadı */
  .quad 0x00cf9a000000ffff        /* 0x10 kernel 4GB code at 0x00000000 */
  .quad 0x00cf92000000ffff        /* 0x18 kernel 4GB data at 0x00000000 */
  .quad 0x00cffa000000ffff        /* 0x23 user   4GB code at 0x00000000 */
  .quad 0x00cff2000000ffff        /* 0x2b user   4GB data at 0x00000000 */
  .quad 0x0000000000000000        /* kullanılmadı */
  .quad 0x0000000000000000        /* kullanılmadı */
  /*
    * APM bölütleri bayt taneciklilik özelliğine sahiptir ve
    * tabanları ile sınırları çalışma zamanında atanır.
    */
  .quad 0x0040920000000000        /* 0x40 kötü BIOS'lar için APM ataması */
  .quad 0x00409a0000000000        /* 0x48 APM CS    kod */
  .quad 0x00009a0000000000        /* 0x50 APM CS 16 kod (16 bit) */
  .quad 0x0040920000000000        /* 0x58 APM DS    veri */
  .fill NR_CPUS*4,8,0             /* TSS'ler ve LDT'ler için boşluk */
| 
 | |||||||||