Ayyıldız2 | 2008 TR Yapısı • 1-99 Orta Emek Destan • Oto Avsız • 10 Temmuz 21:00 HEMEN TIKLA!
setup.S sistem verilerinin BIOS'dan alınmasından ve onların uygun bir bellek bölgesine yerleştirilmesinden sorumludur.
GNU GRUB ve LILO gibi diğer önyükleyiciler de bzImage'ı yükleyebilirler. Bu tür önyükleyiciler bzImage'ı belleğe yüklemeli ve "gerçek-biçim çekirdek başlıkları"nı kur***ıdırlar, özellikle type_of_loader; sonra denetimi bsetup'a devreder. setup.S şunları varsayar:
bsetup veya setup SETUPSEG:0'a yüklenmemiş olabilir, örn. denetim setup.S'ye geçtiğinde CS SETUPSEG'e eşit olmayabilir;
setup'ın ilk 4 sektörü bootsect'den hemen sonra yüklenir. Sıfırlama vmlinux'den önce gelecek şekilde SYSSEG:0'da yüklü olabilir, bu vars***m bsetup'ta etkili olmaz.
Başlık
/* LILO'nun doğru yüklendiğinden emin olmak için imza kelimeleri */#define SIG1 0xAA55#define SIG2 0x5A5AINITSEG = DEF_INITSEG # 0x9000, önyüklemeyi buraya taşır, yolun dışınaSYSSEG = DEF_SYSSEG # 0x1000, sistem 0x10000'a yüklenir (65536).SETUPSEG = DEF_SETUPSEG # 0x9020, bu geçerli bölüttür # ... ve CS'nin önceki içeriğiDELTA_INITSEG = SETUPSEG - INITSEG # 0x0020.code16.text///////////////////////////////////////////////////////////////////////////////start:{ goto trampoline(); // sıradaki başlığı atla}# Bu setup başlığıdır ve %cs:2 (old 0x9020:2) adresinden başla***ıdır .ascii "HdrS" # başlık imzası .word 0x0203 # başlık sürüm numarası (>= 0x0105) # yoksa eski loadlin-1.5 başarısız olurrealmode_swtch: .word 0, 0 # default_switch, SETUPSEGstart_sys_seg: .word SYSSEG .word kernel_version # çekirdek sürüm dizgesini göstererek # yukarıdaki başlık bölümü # loadlin-1.5 (başlık v1.5) ile uyumlu. # Değiştirmeyin// kernel_version defined belowtype_of_loader: .byte 0 # = 0, eski (LILO, Loadlin, # Bootlin, SYSLX, bootsect...) # atanmış id değerleri için # bakınız Documentation/i386/boot.txt# bayraklar, kullanılmayan bitler 0 ol***ı, (RFU) loadflags arasındaki bitlerloadflags:LOADED_HIGH = 1 # Sıfır değilse, çekirdek yüksek yüklenmiştirCAN_USE_HEAP = 0x80 # Sıfır değilse, yükleyici ayrıca setup.S'nin # gerisinde ne kadar alanın yığın (heap) # olarak kullanılacağını tutmak için # heap_end_ptr'ye sahiptir. # Neyin boş olduğunu sadece yükleyici bilir#ifndef __BIG_KERNEL__ .byte 0#else .byte LOADED_HIGH#endifsetup_move_size: .word 0x8000 # kurulum 0x90000'ye yüklenmediğinde # taşıma boyutu. Çekirdeğe sıçramadan # hemen önce kurulumu 0x90000'a taşıyacağız. # Bununla birlikte geride bıraktığımız # ne kadar yüklenmesi gereken veri # bulunduğunu sadece yükleyici bilir. # burada yükleyiciler 32-bit kodcode32_start: # için farklı başlangıç # adresleri koyarlar.#ifndef __BIG_KERNEL__ .long 0x1000 # 0x1000 = zImage için vars***lan#else .long 0x100000 # 0x100000 = büyük çekirdek için öntanımlı#endiframdisk_image: .long 0 # yüklü ramdisk görüntüsünün adresi # Burada yükleyici görüntüyü yüklediği # 32-bit adresi koyar. Bu sadece # çekirdek tarafından okunacaktır.ramdisk_size: .long 0 # boyutu bayt cinsindendirbootsect_kludge: .word bootsect_helper, SETUPSEGheap_end_ptr: .word modelist+1024 # (Başlık sürümü 0x0201 veya daha büyük) # buradan sonra kurulum sonuna kadar # boşluklar (özel) kurulum tarafından # yerel yığın amaçları için kullanılabilir.// modelist .text bölümünün sonundadırpad1: .word 0cmd_line_ptr: .long 0 # (Başlık sürümü 0x0202 veya daha büyük) # Sıfır değilse, çekirdek komut # satırına 32-bit bir gösterici. # Komut satırı kurulumun başı ile # alçak bellek arasına (0xa0000) # yerleştirilmeli veya okunmadan önce # üzerine yazıl***ı. Eğer bu alan # kullanılırsa, 0x90000 bölütüyle ilgili # sihirli birşey kalmaz; kurulum # alçak bellekte 0x10000 veya # daha yüksek herhangi bir # yere yerleştirilebilir.ramdisk_max: .long __MAXMEM-1 # (Başlık sürümü 0x0203 veya daha büyük) # initrd içeriği için en # yüksek güvenli adres
__MAXMEM tanımlaması linux/asm-i386/page.h içindedir:
/* * 0xC0000000'ın bir __PAGE_OFFSET değeri çekirdeğin bir gigabayt * sanal adres boşluğuna sahip olduğu anlamına gelir ki bu da * kullanabileceğiniz fiziksel bellek miktarını 950MB'a sınırlar */#define __PAGE_OFFSET (0xC0000000)/* * Bu kadar adres boşluğu v***loc() ve iomap() olduğu kadar * "fixmap" eşleştirmeleri (mappings) için de tahsis edilir. */#define __V***LOC_RESERVE (128 << 20)#define __MAXMEM (-__PAGE_OFFSET-__V***LOC_RESERVE)
__MAXMEM = 1G - 128M değerini verir.
Bu başlık bazı plan örneklerini takip etmelidir. linux/Documentation/i386/boot.txt belgesine başvurun:
Konum Proto İsim Anlamı/Boyut0200/2 2.00+ jump Sıçrama komutu0202/4 2.00+ header Sihirli imza "HdrS"0206/2 2.00+ version Desteklene önyükleme protokolü sürümü0208/4 2.00+ realmode_swtch Önyükleme yükleyici çengeli (hook)020C/2 2.00+ start_sys load-low bölütü (0x1000) (modası geçmiş)020E/2 2.00+ kernel_version Çekirdek sürüm dizgesi göstericisi0210/1 2.00+ type_of_loader Önyükleme yükleyici belirteci0211/1 2.00+ loadflags Önyükleme protokolü seçenek bayrakları0212/2 2.00+ setup_move_size Yüksek bellek boyutuna taşı (çengellerle kullanılır)0214/4 2.00+ code32_start Önyükleyici çengeli0218/4 2.00+ ramdisk_image initrd yükleme adresi (önyükleyici tarafından atanır)021C/4 2.00+ ramdisk_size initrd boyutu (önyükleyici tarafından atanır)0220/4 2.00+ bootsect_kludge KULLANMAYIN - sadece bootsect.S kullanımı için0224/2 2.01+ heap_end_ptr kurulum bittikten sonra boş bellek0226/2 N/A pad1 Kullanılmaz0228/4 2.02+ cmd_line_ptr çekirdek komut satırına 32-bit gösterici022C/4 2.03+ initrd_addr_max En yüksek yasal initrd adresi
Kod Bütünlüğünü Kontrol Et
setup kodu sürekli olmayabileceği için önce kod bütünlüğünü kontrol etmeliyiz.
///////////////////////////////////////////////////////////////////////////////trampoline(){ start_of_setup(); // asla dönmez .space 1024;}///////////////////////////////////////////////////////////////////////////////// tüm kodun yüklenip yüklenmediğini görmek için imz*** kontrol etstart_of_setup(){ // Bootlin bunun daha önce yapılmasına bağlıdır, bakınız bootlin:technic.doc int13/AH=15h(AL=0, DL=0x81); // int13/AH=15h: DİSK - DİSK TÜRÜNÜ AL#ifdef SAFE_RESET_DISK_CONTROLLER int13/AH=0(AL=0, DL=0x80); // int13/AH=00h: DİSK - DİSK SİSTEMİNİ RESETLE#endif DS = CS; // kurulum sonunda imz*** kontrol et if (setup_sig1!=SIG1 || setup_sig2!=SIG2) { goto bad_sig; } goto goodsig1;}///////////////////////////////////////////////////////////////////////////////// bazı küçük işlevlerprtstr(); /* DS:SI'teki ascii'leri yaz */prtsp2(); /* çift boşluk yaz */prtspc(); /* tek boşluk yaz */prtchr(); /* AL'deki ascii'leri yaz */beep(); /* CTRL-G yaz, örn. bip */
Kod bütünlüğünü doğrulamak için imza kontrol edilir.
İmza bulunmazsa kalan setup kodu SYSSEG:0'da vmlinux öncesinde yer alır.
no_sig_mess: .string "No setup signature found ..."goodsig1: goto goodsig; // yakın sıçrama yap///////////////////////////////////////////////////////////////////////////////// kalan setup kodunu SYSSEG:0'dan CS:0800'e taşıbad_sig() DELTA_INITSEG = 0x0020 (= SETUPSEG - INITSEG) SYSSEG = 0x1000 word start_sys_seg = SYSSEG; // kurulum başlığında tanımlı{ DS = CS - DELTA_INITSEG; // INITSEG olarak da bilinir BX = (byte)(DS:[497]); // örn. setup_sects // ilk 4 sekctör zaten yüklü CX = (BX - 4) << 8; // kelime cinsinden kalan kod (2-bayt) start_sys_seg = (CX >> 3) + SYSSEG; // gerçek sistem kodu başlangıcı move SYSSEG:0 to CS:0800 (CX*2 bytes); if (setup_sig1!=SIG1 || setup_sig2!=SIG2) {no_sig: prtstr("No setup signature found ...");no_sig_loop: hlt; goto no_sig_loop; }}
hlt komutu komut çalıştırm*** durdurur ve işlemciyi halt durumuna getirir. İşlemci halt kipine girildiğini belirtecek şekilde özel bir taşıt dizisi üretir. Etkin bir kesme (NMI dahil) oluştuğunda, işlemci çalıştırm*** hlt komutundan sonra devam ettirir ve komut göstericisi, hlt'ı takip eden komutu göstererek, kesme yöneticisi çağırılmadan önce yığıta kaydedilecektir. Böylece işlemciyi tekrar halt durumuna koymak için hlt'tan sonra jmp komutuna ihtiyaç duyarız.
setup kodu doğru yere taşınmıştır. start_sys_seg değişkeni gerçek sistem kodunun başladığı yeri gösterir. Eğer bad_sig olmazsa start_sys_seg, SYSSEG olarak kalır.
Yükleyici Türünü Kontrol Et
Yükleyicinin imgeyle uyumluluğunu olduğunu kontrol et.
///////////////////////////////////////////////////////////////////////////////good_sig() char loadflags; // setup başlığı içinde char type_of_loader; // setup başlığı içinde LOADHIGH = 1{ DS = CS - DELTA_INITSEG; // INITSEG olarak da bilinir if ( (loadflags & LOADHIGH) && !type_of_loader ) { // Hata, eski yükleyiciler büyük-çekirdek yüklemeye çalışırlar prtstr("Wrong loader, giving up..."); goto no_sig_loop; // yukarıda bad_sig()'de tanımlı }}loader_panic_mess: .string "Wrong loader, giving up..."
bootsect_helper() tarafından bvmlinux yüklendiğinde type_of_loader'un 0x20 olarak değiştiğine dikkat edin.
Bellek Boyutunu
kB cinsinden genişletilmiş bellek boyutunu (1M üzeri) elde etmek için üç değişik bellek saptama şeması dene.
İlk olarak bir bellek haritası oluşturmamızı sağlayan e820h'yi dene; sonra 32 bitlik bellek boyutu döndüren e801h'yi ve son olarak 0-64M döndüren 88h'yi dene.
///////////////////////////////////////////////////////////////////////////////// bellek boyunu alloader_ok() E820NR = 0x1E8 E820MAP = 0x2D0{ // bu işleve girerken, DS = CS-DELTA_INITSEG; INITSEG olarak da bilinir (long)DS:[0x1E0] = 0;#ifndef STANDARD_MEMORY_BIOS_CALL (byte)DS:[0x1E8] = 0; // E820NR /* method E820H: bakınız ACPI spec * bellek haritası (from hell). e820h belleği farklı türlerden * bir bütün deste olarak sınıflandırılmış şekilde döndürür, ve * bellek deliklerine ve herşeye izin verir. Biz bu bellek * haritasını tararız ve ilk 32 bellek alanının listesini oluştururuz, * [E820MAP]'den döneriz. */meme820: EBX = 0; DI = 0x02D0; // E820MAP do {jmpe820: int15/EAX=E820h(EDX='SMAP', EBX, ECX=20, ESI=DSI); // int15/AX=E820h: GET SYSTEM MEMORY MAP if (failed || 'SMAP'!=EAX) break; // if (1!=DS:[DI+16]) continue; // kullanışsızgood820: if (DS:[1E8]>=32) break; // entry# > E820MAX DS:[0x1E8]++; // entry# ++; DI += 20; // tamponu sonraki için ayarlaagain820: } while (!EBX) // bitmedibail820: /* method E801H: * 1k parça boyutuyla bellek boyutu, loadlin karıştırmamak için. * 0xe801 bellek boyutunu tamamen farklı bir yerde tutarız * çünkü muhtemelen 16 bitten daha uzun olacaktır * (1e0 kullanınız çünkü bu Larry Augustine'in alternatif bellek * tespit şemasını kullanma yöntemidir ve bu yöntem * herşeyi aynı yere yazma konusunda hassastır.) */meme801: stc; // hatalı BIOSlar için uğraş CX = DX = 0; int15/AX=E801h; /* int15/AX=E801h: >64M YAPILANDIR***AR İÇİN BELLEK BOYUTUNU AL * AX = K cinsinden 1M ve 16M arasında bellek boyutu (en çok 3C00 = 15MB) * BX = genişletilmiş bellek, 16M üzeri, 64K bloklar halinde * CX = K cinsinden 1M'dan 16M'a yapılandırılmış bellek * DX = 16M üzeri yapılandırılmış bellek, 64K bloklar halinde */ if (failed) goto mem88; if (!CX && !DX) { CX = AX; DX = BX; }e801usecxdx: (long)DS:[0x1E0] = ((EDX & 0xFFFF) << 6) + (ECX & 0xFFFF); // in K#endifmem88: // eski geleneksel yöntem int15/AH=88h; /* int15/AH=88h: SİSTEM - GENİŞLETİLMİŞ BELLEK BOYUTU * AX = mutlak 100000h adresinden başlayan sürekli kB'ların s***sı */ DS:[2] = AX;}
Donanım Desteği
Klavye, ekran kartı, harddisk, MCA bus'ı ve işaretleme cihazı gibi donanımların desteğini kontrol et.
{ // klavye tekrarlama oranını en çoğa ayarla int16/AX=0305h(BX=0); // int16/AH=03h: KEYBOARD - SET TYPEMATIC RATE AND DELAY /* Ekran kiplerini kullanıcıya göstermek için * ekran kartını ve parametrelerini kontrol et. */ video(); // see video.S // hd0 ve hd1 verisini al hd0 verisini (*int41)'dan CS-DELTA_INITSEG:0080'ya (16 bytes) kopyala; // int41: SYSTEM DATA - HARD DISK 0 PARAMETRE TABLO ADRESİ hd1 verisini (*int46)'dan CS-DELTA_INITSEG:0090'ya (16 bytes) kopyala; // int46: SYSTEM DATA - HARD DISK 1 PARAMETRE TABLE ADRESİ // hd1 var mı kontrol et int13/AH=15h(AL=0, DL=0x81); // int13/AH=15h: DISK - DİSK TÜRÜNÜ AL if (failed || AH!=03h) { // AH==03h eğer harddisk iseno_disk1: temizle CS-DELTA_INITSEG:0090 (16 bytes); }is_disk1: // Mikro Kanal veriyolu (Micro Channel-MCA bus) için kontrol et CS-DELTA_INITSEG:[0xA0] = 0; // tablo uzunluğunu 0'a ayarla int15/AH=C0h; /* int15/AH=C0h: SİSTEM - YAPILANDIRMAYI AL * ES:BX = ROM yapılandırma tablosu */ if (failed) goto no_mca; ROM yapılandırma tablosunu (ES:BX)'den CS-DELTA_INITSEG:00A0 adresine taşı; // CX = (table length<14)? CX:16; sadece ilk 16 baytno_mca: // PS/2 noktalama cihazlarını (pointing device) kontrol et CS-DELTA_INITSEG:[0x1FF] = 0; // vars***lan noktalama cihazı 0 int11h(); // int11h: BIOS - EKİPMAN LİSTESİNİ AL if (AL & 0x04) { // fare kuruldu DS:[0x1FF] = 0xAA; }}
APM Desteği
BIOS APM desteğini kontrol et.
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE){ DS:[0x40] = 0; // sürüm = 0 APM BIOS olmadığı anlamına gelir int15/AX=5300h(BX=0); // int15/AX=5300h: Advanced Power Management v1.0+ - INSTALLATION CHECK if (failed || 'PM'!=BX || !(CX & 0x02)) goto done_apm_bios; // (CX & 0x02) 32 bit desteklendiği anlamına gelir int15/AX=5304h(BX=0); // int15/AX=5304h: Advanced Power Management v1.0+ - DISCONNECT INTERFACE EBX = CX = DX = ESI = DI = 0; int15/AX=5303h(BX=0); /* int15/AX=5303h: Advanced Power Management v1.0+ * - CONNECT 32-BIT PROTMODE INTERFACE */ if (failed) {no_32_apm_bios: // no_32_apm_bios etiketini buraya taşıdım DS:[0x4C] &= ~0x0002; // 32 bit destekleme bitini kaldır goto done_apm_bios; } DS:[0x42] = AX, 32-bit kod bölütü temel adresi; DS:[0x44] = EBX, giriş noktası konumu; DS:[0x48] = CX, 16-bit kod bölütü temel adresi; DS:[0x4A] = DX, 16-bit veri bölütü temel adresi; DS:[0x4E] = ESI, APM BIOS kod bölütü uzunluğu; DS:[0x52] = DI, APM BIOS veri bölütü uzunluğu; int15/AX=5300h(BX=0); // tekrar kontrol et // int15/AX=5300h: Advanced Power Management v1.0+ - INSTALLATION CHECK if (success && 'PM'==BX) { DS:[0x40] = AX, APM version; DS:[0x4C] = CX, APM flags; } else {apm_disconnect: int15/AX=5304h(BX=0); /* int15/AX=5304h: Advanced Power Management v1.0+ * - DISCONNECT INTERFACE */ }done_apm_bios:}#endif
Koru***ı Kip için Hazırlık
// kip seçiciyi çağır{ if (realmode_swtch) { realmode_swtch(); // kip seçme çengeli } else {rmodeswtch_nor***: default_switch() { cli; // hiçbir kesmeye izin yok outb(0x80, 0x70); // NMI etkinleştirmeyi kaldır } }rmodeswtch_end:}// gerekliyse kodu tekrar konumla{ (long)code32 = code32_start; if (!(loadflags & LOADED_HIGH)) { // düşük yüklü zImage // 0x0100 <= start_sys_seg < CS-DELTA_INITSEGdo_move0: AX = 0x100; BP = CS - DELTA_INITSEG; // INITSEG olarak da bilinir BX = start_sys_seg;do_move: sistem imgesini (start_sys_seg:0 .. CS-DELTA_INITSEG:0)'dan 0100:0'a taşı; // her seferinde 0x1000 bayt taşı }end_move:
code32_start'ın zImage için 0x1000 adresi ile bzImage için ise 0x100000 adresi ile ilklendirildiğine dikkat edin. code32 değeri denetimin Koru***ı Kipe Geç içindekilinux/arch/i386/boot/compressed/head.S'e geçirilmesi için kullanılacaktır. zImage önyüklemesi yapılırsa vmlinux'u 0100:0'a taşır; bzImage önyüklemesi yapılırsa bvmlinux start_sys_seg:0'da kalır. Taşıma adresi linux/arch/i386/boot/compressed/Makefile içindeki -Ttext seçeneğiyle uyuş***ıdır. Bakınız linux/arch/i386/boot/compressed/Makefile.
Sonra, eğer gerekliyse, kod CS-DELTA_INITSEG:0'dan (bbootsect ve bsetup) INITSEG:0'a taşınır.
DS = CS; // SETUPSEG olarak da bilinir // sürüm <=201 ile geriye dönük uyumluluğa ihtiyacımız olup olmadığını kontrol et if (!cmd_line_ptr && 0x20!=type_of_loader && SETUPSEG!=CS) { cli; // taşınırken kesme olarak yığıtı kullanabilir // store new SS in DX AX = CS - DELTA_INITSEG; DX = SS; if (DX>=AX) { // yığıt çerçevesi birlikte taşınacak DX = DX + INITSEG - AX; // i.e. SS-CS+SETUPSEG }move_self_1: /* CS-DELTA_INITSEG:0'dan INITSEG:0'ya taşı (setup_move_size bayt) * CS:IP üzerindeki kodun üzerine yazmamak için iki adımda * (src < dest) taşı fakat aşağıya doğru ("std") */ move CS-DELTA_INITSEG:move_self_here+0x200 to INITSEG:move_self_here+0x200, setup_move_size-(move_self_here+0x200) bytes; // INITSEG:move_self_here+0x200 == SETUPSEG:move_self_here goto SETUPSEG:move_self_here; // artık CS=SETUPSEGmove_self_here: move CS-DELTA_INITSEG:0 to INITSEG:0, move_self_here+0x200 bytes; // goto'dan önce eski CS anlamında DS = SETUPSEG; SS = DX; }end_move_self:}
Tekrar dikkat edin, bvmlinux'u yüklediğinde bootsect_helper() tarafından type_of_loader'un değeri 0x20 olarak değiştirilmektedir.
A20'yi Etkinleştir
A20 sorunu ve çözümü için A20 - a pain from the past'e başvurun.
A20_TEST_LOOPS = 32 # Bekleme başına adım s***sı A20_ENABLE_LOOPS = 255 # deneme için toplam döngü{#if defined(CONFIG_MELAN) // Enable A20. AMD Elan bug fix. outb(0x02, 0x92); // outb(val, port)a20_elan_wait: while (!a20_test()); // testi geçemedi goto a20_done;#endifa20_try_loop: // Önce, A20 kapısı olmayan bir sistemde olup olmadığımıza bak.a20_none: if (a20_test()) goto a20_done; // testi geçti // Sonra, BIOS'u (INT 0x15, AX=0x2401) denea20_bios: int15/AX=2401h; // Int15/AX=2401h: SYSTEM - later PS/2s - ENABLE A20 GATE if (a20_test()) goto a20_done; // testi geçti // Klavye denetleyici üzerinden A20'yi etkinleştirmeye çalışa20_kbc: empty_8042(); if (a20_test()) goto a20_done; // BIOS gecikmesi durumunda testi tekrarla outb(0xD1, 0x64); // komut yaz empty_8042(); outb(0xDF, 0x60); // A20 etkin empty_8042(); // a20 gerçekten etkin olana kadar beklea20_kbc_wait: CX = 0;a20_kbc_wait_loop: do { if (a20_test()) goto a20_done; // testi geçti } while (--CX) // Son girişim: "yapılandırma portu A"'yı kullan outb((inb(0x92) | 0x02) & 0xFE, 0x92); // yapılandırma portu A etkilenene kadar beklea20_fast_wait: CX = 0;a20_fast_wait_loop: do { if (a20_test()) goto a20_done; // testi geçti } while (--CX) // A20 hala cevap vermiyor. Tekrar ayarlam*** dene. if (--a20_tries) goto a20_try_loop; prtstr("linux: fatal error: A20 gate not responding!");a20_die: hlt; goto a20_die;}a20_tries: .byte A20_ENABLE_LOOPS // i.e. 255a20_err_msg: .ascii "linux: fatal error: A20 gate not responding!" .byte 13, 10, 0
I/O port işlemleri için, Kaynakça içindeki ilgili başvuru materyallerine bakın.
Koru***ı Kipe Geç
Tüm 32 bitlik IA-32 işlemcileri ile kod uyumluluğunu sağlamak için, aşağıdaki koru***ı kipe geçmeyi sağlayacak adımları yerine getirin:
İlk GDT girdisinde boş tanıml***cılı GDT'yi hazırl***n, bir kod bölütü tanıml***cı ve bir veri bölütü tanıml***cı;
Maskelenebilir donanım kesmeleri ve NMI dahil kesmeleri etkisiz kılın;
lgdt asm komutunu kullanarak GDTR yazmacına taban adresini ve GDT sınırını yükleyin;
mov cr0 (Intel 386 ve üstü) veya lmsw komutu (Intel 286 ile uyumluluk için) kullanarak CR0 yazmacındaki PE bayrağını etkinleştirin;
Ardından bir uzak jmp veya bir uzak call komutu çalıştırın.
Yığıt nor*** oku/yaz veri bölütüne yerleştirilebilir, böylece adanmış tanıml***cıya gerek kalmaz.
a20_done:{ lidt idt_48; // load idt with 0, 0; // DS:gdt'yi doğrusal göstericiye dönüştür *(long*)(gdt_48+2) = DS << 4 + &gdt; lgdt gdt_48; // yardımcı işlemciyi sıfırla outb(0, 0xF0); delay(); outb(0, 0xF1); delay(); // kesmeleri yeniden programla outb(0xFF, 0xA1); // tüm kesmeleri maskele delay(); outb(0xFB, 0x21); // irq2 dışında tüm irq'ları maskele // koru***ı kip! AX = 1; lmsw ax; // makina durumu kelimesi (word), CR0'ın 0'dan 15'e // kadar bitleri sadece PE, MP, EM ve TS bayraklarını // etkiler goto flush_instr;flush_instr: BX = 0; // bir önyükleme gösteren bayrak ESI = (CS - DELTA_INITSEG) << 4; // gerçek kip kod göstericisi /* NOT: Yüksek yüklenen büyük çekirdekler için * jmpi 0x100000,__KERNEL_CS'ye gereksinim duyarız * * fakat henüz CS yazmacını yüklemedik, * bu yüzden hedef konumun vars***lan boyutu hala 16 bit. * Bununla birlikte, bir terim öneki (0x66) kullanarak, * CPU bizim 48 bit uzak göstericimizi uygun bir şekilde alır. * Bakınız (INTeL 80386 Programmer's Reference Manual, * Mixing 16-bit and 32-bit code, page 16-6) */ // __KERNEL_CS:[(uint32*)code32]'e git; .byte 0x66, 0xeacode32: .long 0x1000 // Koru***ı Kip için Hazırlık // içinde üstüne yazılır .word __KERNEL_CS // bölüt 0x10 // bakınız linux/arch/i386/boot/compressed/head.S:startup_32}
Uzak jmp komutu (0xea) CS kaydedicisini günceller. kalan bölüt kaydedicileri (DS, SS, ES, FS ve GS) içeriği daha sonra yeniden yüklenmelidir. terim boyutu öneki (0x66) jmp'ı 32 bitlik terim code32'ye kadar çalıştırılmasına zorlamak için kullanılır. Terim boyutu öneki ayrıntıları için bakınız: IA-32 Manual (Vol.1. Ch.3.6. Operand-size and Address-size Attributes, and Vol.3. Ch.17. Mixing 16-bit and 32-bit Code).
Denetim linux/arch/i386/boot/compressed/head.S:startup_32'ye geçirilir. zImage için 0x1000 adresinde; bzImage için 0x100000 adresinde. Bakınız: linux/arch/i386/boot/compressed/head.S.
ESI toplanan sistem verilerinin bellek alanını gösterir. 16 bitlik gerçek kip çekirdek kodundan 32 bitlik kısma parametre geçirmek için kullanılır. Ayrıntılar için linux/Documentation/i386/zero-page.txt dosyasına bakınız.
Daha fazla anahtarlama ayrıntısı için IA-32 Manual Vol.3. (Ch.9.8. Software Initialization for Protected-Mode Operation, Ch.9.9.1. Switching to Protected Mode, and Ch.17.4. Transferring Control Among Mixed-Size Code Segments) belgesine başvurun.
Muhtelif
Kalanlar destek işlevleri ve değişkenleridir.
/* linux/Makefile hedefleri tarafından oluşturulan makrolar: * include/linux/compile.h ve include/linux/version.h */kernel_version: .ascii UTS_RELEASE .ascii " (" .ascii LINUX_COMPILE_BY .ascii "@" .ascii LINUX_COMPILE_HOST .ascii ") " .ascii UTS_VERSION .byte 0///////////////////////////////////////////////////////////////////////////////default_switch() { cli; outb(0x80, 0x70); } /* Kesmeleri ve NMI'yi iptal et */bootsect_helper(ES:BX); /* bkz. Bootsect Yardımcısı *////////////////////////////////////////////////////////////////////////////////a20_test(){ FS = 0; GS = 0xFFFF; CX = A20_TEST_LOOPS; // i.e. 32 AX = FS:[0x200]; do {a20_test_wait: FS:[0x200] = ++AX; delay(); } while (AX==GS:[0x210] && --CX); return (AX!=GS[0x210]); // ZF==0 (i.e. NZ/NE, a20_test!=0) means test passed}///////////////////////////////////////////////////////////////////////////////// klavye komut kuyruğu boş mu, bakempty_8042(){ int timeout = 100000; for (; {empty_8042_loop: if (!--timeout) return; delay(); inb(0x64, &AL); // 8042 durum portu if (AL & 1) { // çıktı delay(); inb(0x60, &AL); // okuno_output:} else if (!(AL & 2)) return; // girdi yok }}///////////////////////////////////////////////////////////////////////////////// CMOS saatini oku, AL'den saniyeyi döndür, video.S'de kullanılırgettime(){ int1A/AH=02h(); /* int1A/AH=02h: SAAT - GERÇEK SAAT ZAMANINI AL * DH = BCD gösterimli saniye*/ AL = DH & 0x0F; AH = DH >> 4; aad;}///////////////////////////////////////////////////////////////////////////////delay() { outb(AL, 0x80); } // I/O yaptıktan sonra gerekli// Tanıml***cı tablogdt: .word 0, 0, 0, 0 # dummy .word 0, 0, 0, 0 # kullanılmadı // bölüt 0x10, __KERNEL_CS .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) .word 0 # taban adres = 0 .word 0x9A00 # kodu oku/çalıştır .word 0x00CF # tanelilik (granularity) = 4096, 386 # (sınırın 5. yarım baytı) // bölüt 0x18, __KERNEL_DS .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) .word 0 # taban adres = 0 .word 0x9200 # veri oku/yaz .word 0x00CF # tanelilik = 4096, 386 # (sınırın 5. yarım baytı)idt_48: .word 0 # idt sınırı = 0 .word 0, 0 # idt tabanı = 0L/* [gdt_48] komutla eşleşmesi için 0x0800 (2048) ol***ı, * Linux 2.2.22'nin yaptığı gibi. */gdt_48: .word 0x8000 # gdt sınırı=2048, # 256 GDT girdisi .word 0, 0 # gdt tabanı (daha sonra doldurulur)#include "video.S"// setup.S'nin sonundaki imza:{setup_sig1: .word SIG1 // 0xAA55setup_sig2: .word SIG2 // 0x5A5Amodelist:}
video.S içindeki video ayar ve algılama kodu:
ASK_VGA = 0xFFFD // defined in linux/include/asm-i386/boot.h///////////////////////////////////////////////////////////////////////////////video(){ pushw DS; // farklı bölütler kullan FS = DS; DS = ES = CS; GS = 0; cld; basic_detect(); // temel kart türü testi (EGA/VGA/MDA/CGA)#ifdef CONFIG_VIDEO_SELECT if (FS:[0x01FA]!=ASK_VGA) { // kullanıcı seçimli video kipi mode_set(); if (failed) { prtstr("You passed an undefined mode number.\n"); mode_menu(); } } else {vid2: mode_menu(); }vid1:#ifdef CONFIG_VIDEO_RETAIN restore_screen(); // ekran içeriğini geri yükle#endif /* CONFIG_VIDEO_RETAIN */#endif /* CONFIG_VIDEO_SELECT */ mode_params(); // kip parametrelerini sakla popw ds; // orjinal DS'yi geri yükle}
GNU GRUB ve LILO gibi diğer önyükleyiciler de bzImage'ı yükleyebilirler. Bu tür önyükleyiciler bzImage'ı belleğe yüklemeli ve "gerçek-biçim çekirdek başlıkları"nı kur***ıdırlar, özellikle type_of_loader; sonra denetimi bsetup'a devreder. setup.S şunları varsayar:
bsetup veya setup SETUPSEG:0'a yüklenmemiş olabilir, örn. denetim setup.S'ye geçtiğinde CS SETUPSEG'e eşit olmayabilir;
setup'ın ilk 4 sektörü bootsect'den hemen sonra yüklenir. Sıfırlama vmlinux'den önce gelecek şekilde SYSSEG:0'da yüklü olabilir, bu vars***m bsetup'ta etkili olmaz.
Başlık
/* LILO'nun doğru yüklendiğinden emin olmak için imza kelimeleri */#define SIG1 0xAA55#define SIG2 0x5A5AINITSEG = DEF_INITSEG # 0x9000, önyüklemeyi buraya taşır, yolun dışınaSYSSEG = DEF_SYSSEG # 0x1000, sistem 0x10000'a yüklenir (65536).SETUPSEG = DEF_SETUPSEG # 0x9020, bu geçerli bölüttür # ... ve CS'nin önceki içeriğiDELTA_INITSEG = SETUPSEG - INITSEG # 0x0020.code16.text///////////////////////////////////////////////////////////////////////////////start:{ goto trampoline(); // sıradaki başlığı atla}# Bu setup başlığıdır ve %cs:2 (old 0x9020:2) adresinden başla***ıdır .ascii "HdrS" # başlık imzası .word 0x0203 # başlık sürüm numarası (>= 0x0105) # yoksa eski loadlin-1.5 başarısız olurrealmode_swtch: .word 0, 0 # default_switch, SETUPSEGstart_sys_seg: .word SYSSEG .word kernel_version # çekirdek sürüm dizgesini göstererek # yukarıdaki başlık bölümü # loadlin-1.5 (başlık v1.5) ile uyumlu. # Değiştirmeyin// kernel_version defined belowtype_of_loader: .byte 0 # = 0, eski (LILO, Loadlin, # Bootlin, SYSLX, bootsect...) # atanmış id değerleri için # bakınız Documentation/i386/boot.txt# bayraklar, kullanılmayan bitler 0 ol***ı, (RFU) loadflags arasındaki bitlerloadflags:LOADED_HIGH = 1 # Sıfır değilse, çekirdek yüksek yüklenmiştirCAN_USE_HEAP = 0x80 # Sıfır değilse, yükleyici ayrıca setup.S'nin # gerisinde ne kadar alanın yığın (heap) # olarak kullanılacağını tutmak için # heap_end_ptr'ye sahiptir. # Neyin boş olduğunu sadece yükleyici bilir#ifndef __BIG_KERNEL__ .byte 0#else .byte LOADED_HIGH#endifsetup_move_size: .word 0x8000 # kurulum 0x90000'ye yüklenmediğinde # taşıma boyutu. Çekirdeğe sıçramadan # hemen önce kurulumu 0x90000'a taşıyacağız. # Bununla birlikte geride bıraktığımız # ne kadar yüklenmesi gereken veri # bulunduğunu sadece yükleyici bilir. # burada yükleyiciler 32-bit kodcode32_start: # için farklı başlangıç # adresleri koyarlar.#ifndef __BIG_KERNEL__ .long 0x1000 # 0x1000 = zImage için vars***lan#else .long 0x100000 # 0x100000 = büyük çekirdek için öntanımlı#endiframdisk_image: .long 0 # yüklü ramdisk görüntüsünün adresi # Burada yükleyici görüntüyü yüklediği # 32-bit adresi koyar. Bu sadece # çekirdek tarafından okunacaktır.ramdisk_size: .long 0 # boyutu bayt cinsindendirbootsect_kludge: .word bootsect_helper, SETUPSEGheap_end_ptr: .word modelist+1024 # (Başlık sürümü 0x0201 veya daha büyük) # buradan sonra kurulum sonuna kadar # boşluklar (özel) kurulum tarafından # yerel yığın amaçları için kullanılabilir.// modelist .text bölümünün sonundadırpad1: .word 0cmd_line_ptr: .long 0 # (Başlık sürümü 0x0202 veya daha büyük) # Sıfır değilse, çekirdek komut # satırına 32-bit bir gösterici. # Komut satırı kurulumun başı ile # alçak bellek arasına (0xa0000) # yerleştirilmeli veya okunmadan önce # üzerine yazıl***ı. Eğer bu alan # kullanılırsa, 0x90000 bölütüyle ilgili # sihirli birşey kalmaz; kurulum # alçak bellekte 0x10000 veya # daha yüksek herhangi bir # yere yerleştirilebilir.ramdisk_max: .long __MAXMEM-1 # (Başlık sürümü 0x0203 veya daha büyük) # initrd içeriği için en # yüksek güvenli adres
__MAXMEM tanımlaması linux/asm-i386/page.h içindedir:
/* * 0xC0000000'ın bir __PAGE_OFFSET değeri çekirdeğin bir gigabayt * sanal adres boşluğuna sahip olduğu anlamına gelir ki bu da * kullanabileceğiniz fiziksel bellek miktarını 950MB'a sınırlar */#define __PAGE_OFFSET (0xC0000000)/* * Bu kadar adres boşluğu v***loc() ve iomap() olduğu kadar * "fixmap" eşleştirmeleri (mappings) için de tahsis edilir. */#define __V***LOC_RESERVE (128 << 20)#define __MAXMEM (-__PAGE_OFFSET-__V***LOC_RESERVE)
__MAXMEM = 1G - 128M değerini verir.
Bu başlık bazı plan örneklerini takip etmelidir. linux/Documentation/i386/boot.txt belgesine başvurun:
Konum Proto İsim Anlamı/Boyut0200/2 2.00+ jump Sıçrama komutu0202/4 2.00+ header Sihirli imza "HdrS"0206/2 2.00+ version Desteklene önyükleme protokolü sürümü0208/4 2.00+ realmode_swtch Önyükleme yükleyici çengeli (hook)020C/2 2.00+ start_sys load-low bölütü (0x1000) (modası geçmiş)020E/2 2.00+ kernel_version Çekirdek sürüm dizgesi göstericisi0210/1 2.00+ type_of_loader Önyükleme yükleyici belirteci0211/1 2.00+ loadflags Önyükleme protokolü seçenek bayrakları0212/2 2.00+ setup_move_size Yüksek bellek boyutuna taşı (çengellerle kullanılır)0214/4 2.00+ code32_start Önyükleyici çengeli0218/4 2.00+ ramdisk_image initrd yükleme adresi (önyükleyici tarafından atanır)021C/4 2.00+ ramdisk_size initrd boyutu (önyükleyici tarafından atanır)0220/4 2.00+ bootsect_kludge KULLANMAYIN - sadece bootsect.S kullanımı için0224/2 2.01+ heap_end_ptr kurulum bittikten sonra boş bellek0226/2 N/A pad1 Kullanılmaz0228/4 2.02+ cmd_line_ptr çekirdek komut satırına 32-bit gösterici022C/4 2.03+ initrd_addr_max En yüksek yasal initrd adresi
Kod Bütünlüğünü Kontrol Et
setup kodu sürekli olmayabileceği için önce kod bütünlüğünü kontrol etmeliyiz.
///////////////////////////////////////////////////////////////////////////////trampoline(){ start_of_setup(); // asla dönmez .space 1024;}///////////////////////////////////////////////////////////////////////////////// tüm kodun yüklenip yüklenmediğini görmek için imz*** kontrol etstart_of_setup(){ // Bootlin bunun daha önce yapılmasına bağlıdır, bakınız bootlin:technic.doc int13/AH=15h(AL=0, DL=0x81); // int13/AH=15h: DİSK - DİSK TÜRÜNÜ AL#ifdef SAFE_RESET_DISK_CONTROLLER int13/AH=0(AL=0, DL=0x80); // int13/AH=00h: DİSK - DİSK SİSTEMİNİ RESETLE#endif DS = CS; // kurulum sonunda imz*** kontrol et if (setup_sig1!=SIG1 || setup_sig2!=SIG2) { goto bad_sig; } goto goodsig1;}///////////////////////////////////////////////////////////////////////////////// bazı küçük işlevlerprtstr(); /* DS:SI'teki ascii'leri yaz */prtsp2(); /* çift boşluk yaz */prtspc(); /* tek boşluk yaz */prtchr(); /* AL'deki ascii'leri yaz */beep(); /* CTRL-G yaz, örn. bip */
Kod bütünlüğünü doğrulamak için imza kontrol edilir.
İmza bulunmazsa kalan setup kodu SYSSEG:0'da vmlinux öncesinde yer alır.
no_sig_mess: .string "No setup signature found ..."goodsig1: goto goodsig; // yakın sıçrama yap///////////////////////////////////////////////////////////////////////////////// kalan setup kodunu SYSSEG:0'dan CS:0800'e taşıbad_sig() DELTA_INITSEG = 0x0020 (= SETUPSEG - INITSEG) SYSSEG = 0x1000 word start_sys_seg = SYSSEG; // kurulum başlığında tanımlı{ DS = CS - DELTA_INITSEG; // INITSEG olarak da bilinir BX = (byte)(DS:[497]); // örn. setup_sects // ilk 4 sekctör zaten yüklü CX = (BX - 4) << 8; // kelime cinsinden kalan kod (2-bayt) start_sys_seg = (CX >> 3) + SYSSEG; // gerçek sistem kodu başlangıcı move SYSSEG:0 to CS:0800 (CX*2 bytes); if (setup_sig1!=SIG1 || setup_sig2!=SIG2) {no_sig: prtstr("No setup signature found ...");no_sig_loop: hlt; goto no_sig_loop; }}
hlt komutu komut çalıştırm*** durdurur ve işlemciyi halt durumuna getirir. İşlemci halt kipine girildiğini belirtecek şekilde özel bir taşıt dizisi üretir. Etkin bir kesme (NMI dahil) oluştuğunda, işlemci çalıştırm*** hlt komutundan sonra devam ettirir ve komut göstericisi, hlt'ı takip eden komutu göstererek, kesme yöneticisi çağırılmadan önce yığıta kaydedilecektir. Böylece işlemciyi tekrar halt durumuna koymak için hlt'tan sonra jmp komutuna ihtiyaç duyarız.
setup kodu doğru yere taşınmıştır. start_sys_seg değişkeni gerçek sistem kodunun başladığı yeri gösterir. Eğer bad_sig olmazsa start_sys_seg, SYSSEG olarak kalır.
Yükleyici Türünü Kontrol Et
Yükleyicinin imgeyle uyumluluğunu olduğunu kontrol et.
///////////////////////////////////////////////////////////////////////////////good_sig() char loadflags; // setup başlığı içinde char type_of_loader; // setup başlığı içinde LOADHIGH = 1{ DS = CS - DELTA_INITSEG; // INITSEG olarak da bilinir if ( (loadflags & LOADHIGH) && !type_of_loader ) { // Hata, eski yükleyiciler büyük-çekirdek yüklemeye çalışırlar prtstr("Wrong loader, giving up..."); goto no_sig_loop; // yukarıda bad_sig()'de tanımlı }}loader_panic_mess: .string "Wrong loader, giving up..."
bootsect_helper() tarafından bvmlinux yüklendiğinde type_of_loader'un 0x20 olarak değiştiğine dikkat edin.
Bellek Boyutunu
kB cinsinden genişletilmiş bellek boyutunu (1M üzeri) elde etmek için üç değişik bellek saptama şeması dene.
İlk olarak bir bellek haritası oluşturmamızı sağlayan e820h'yi dene; sonra 32 bitlik bellek boyutu döndüren e801h'yi ve son olarak 0-64M döndüren 88h'yi dene.
///////////////////////////////////////////////////////////////////////////////// bellek boyunu alloader_ok() E820NR = 0x1E8 E820MAP = 0x2D0{ // bu işleve girerken, DS = CS-DELTA_INITSEG; INITSEG olarak da bilinir (long)DS:[0x1E0] = 0;#ifndef STANDARD_MEMORY_BIOS_CALL (byte)DS:[0x1E8] = 0; // E820NR /* method E820H: bakınız ACPI spec * bellek haritası (from hell). e820h belleği farklı türlerden * bir bütün deste olarak sınıflandırılmış şekilde döndürür, ve * bellek deliklerine ve herşeye izin verir. Biz bu bellek * haritasını tararız ve ilk 32 bellek alanının listesini oluştururuz, * [E820MAP]'den döneriz. */meme820: EBX = 0; DI = 0x02D0; // E820MAP do {jmpe820: int15/EAX=E820h(EDX='SMAP', EBX, ECX=20, ESI=DSI); // int15/AX=E820h: GET SYSTEM MEMORY MAP if (failed || 'SMAP'!=EAX) break; // if (1!=DS:[DI+16]) continue; // kullanışsızgood820: if (DS:[1E8]>=32) break; // entry# > E820MAX DS:[0x1E8]++; // entry# ++; DI += 20; // tamponu sonraki için ayarlaagain820: } while (!EBX) // bitmedibail820: /* method E801H: * 1k parça boyutuyla bellek boyutu, loadlin karıştırmamak için. * 0xe801 bellek boyutunu tamamen farklı bir yerde tutarız * çünkü muhtemelen 16 bitten daha uzun olacaktır * (1e0 kullanınız çünkü bu Larry Augustine'in alternatif bellek * tespit şemasını kullanma yöntemidir ve bu yöntem * herşeyi aynı yere yazma konusunda hassastır.) */meme801: stc; // hatalı BIOSlar için uğraş CX = DX = 0; int15/AX=E801h; /* int15/AX=E801h: >64M YAPILANDIR***AR İÇİN BELLEK BOYUTUNU AL * AX = K cinsinden 1M ve 16M arasında bellek boyutu (en çok 3C00 = 15MB) * BX = genişletilmiş bellek, 16M üzeri, 64K bloklar halinde * CX = K cinsinden 1M'dan 16M'a yapılandırılmış bellek * DX = 16M üzeri yapılandırılmış bellek, 64K bloklar halinde */ if (failed) goto mem88; if (!CX && !DX) { CX = AX; DX = BX; }e801usecxdx: (long)DS:[0x1E0] = ((EDX & 0xFFFF) << 6) + (ECX & 0xFFFF); // in K#endifmem88: // eski geleneksel yöntem int15/AH=88h; /* int15/AH=88h: SİSTEM - GENİŞLETİLMİŞ BELLEK BOYUTU * AX = mutlak 100000h adresinden başlayan sürekli kB'ların s***sı */ DS:[2] = AX;}
Donanım Desteği
Klavye, ekran kartı, harddisk, MCA bus'ı ve işaretleme cihazı gibi donanımların desteğini kontrol et.
{ // klavye tekrarlama oranını en çoğa ayarla int16/AX=0305h(BX=0); // int16/AH=03h: KEYBOARD - SET TYPEMATIC RATE AND DELAY /* Ekran kiplerini kullanıcıya göstermek için * ekran kartını ve parametrelerini kontrol et. */ video(); // see video.S // hd0 ve hd1 verisini al hd0 verisini (*int41)'dan CS-DELTA_INITSEG:0080'ya (16 bytes) kopyala; // int41: SYSTEM DATA - HARD DISK 0 PARAMETRE TABLO ADRESİ hd1 verisini (*int46)'dan CS-DELTA_INITSEG:0090'ya (16 bytes) kopyala; // int46: SYSTEM DATA - HARD DISK 1 PARAMETRE TABLE ADRESİ // hd1 var mı kontrol et int13/AH=15h(AL=0, DL=0x81); // int13/AH=15h: DISK - DİSK TÜRÜNÜ AL if (failed || AH!=03h) { // AH==03h eğer harddisk iseno_disk1: temizle CS-DELTA_INITSEG:0090 (16 bytes); }is_disk1: // Mikro Kanal veriyolu (Micro Channel-MCA bus) için kontrol et CS-DELTA_INITSEG:[0xA0] = 0; // tablo uzunluğunu 0'a ayarla int15/AH=C0h; /* int15/AH=C0h: SİSTEM - YAPILANDIRMAYI AL * ES:BX = ROM yapılandırma tablosu */ if (failed) goto no_mca; ROM yapılandırma tablosunu (ES:BX)'den CS-DELTA_INITSEG:00A0 adresine taşı; // CX = (table length<14)? CX:16; sadece ilk 16 baytno_mca: // PS/2 noktalama cihazlarını (pointing device) kontrol et CS-DELTA_INITSEG:[0x1FF] = 0; // vars***lan noktalama cihazı 0 int11h(); // int11h: BIOS - EKİPMAN LİSTESİNİ AL if (AL & 0x04) { // fare kuruldu DS:[0x1FF] = 0xAA; }}
APM Desteği
BIOS APM desteğini kontrol et.
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE){ DS:[0x40] = 0; // sürüm = 0 APM BIOS olmadığı anlamına gelir int15/AX=5300h(BX=0); // int15/AX=5300h: Advanced Power Management v1.0+ - INSTALLATION CHECK if (failed || 'PM'!=BX || !(CX & 0x02)) goto done_apm_bios; // (CX & 0x02) 32 bit desteklendiği anlamına gelir int15/AX=5304h(BX=0); // int15/AX=5304h: Advanced Power Management v1.0+ - DISCONNECT INTERFACE EBX = CX = DX = ESI = DI = 0; int15/AX=5303h(BX=0); /* int15/AX=5303h: Advanced Power Management v1.0+ * - CONNECT 32-BIT PROTMODE INTERFACE */ if (failed) {no_32_apm_bios: // no_32_apm_bios etiketini buraya taşıdım DS:[0x4C] &= ~0x0002; // 32 bit destekleme bitini kaldır goto done_apm_bios; } DS:[0x42] = AX, 32-bit kod bölütü temel adresi; DS:[0x44] = EBX, giriş noktası konumu; DS:[0x48] = CX, 16-bit kod bölütü temel adresi; DS:[0x4A] = DX, 16-bit veri bölütü temel adresi; DS:[0x4E] = ESI, APM BIOS kod bölütü uzunluğu; DS:[0x52] = DI, APM BIOS veri bölütü uzunluğu; int15/AX=5300h(BX=0); // tekrar kontrol et // int15/AX=5300h: Advanced Power Management v1.0+ - INSTALLATION CHECK if (success && 'PM'==BX) { DS:[0x40] = AX, APM version; DS:[0x4C] = CX, APM flags; } else {apm_disconnect: int15/AX=5304h(BX=0); /* int15/AX=5304h: Advanced Power Management v1.0+ * - DISCONNECT INTERFACE */ }done_apm_bios:}#endif
Koru***ı Kip için Hazırlık
// kip seçiciyi çağır{ if (realmode_swtch) { realmode_swtch(); // kip seçme çengeli } else {rmodeswtch_nor***: default_switch() { cli; // hiçbir kesmeye izin yok outb(0x80, 0x70); // NMI etkinleştirmeyi kaldır } }rmodeswtch_end:}// gerekliyse kodu tekrar konumla{ (long)code32 = code32_start; if (!(loadflags & LOADED_HIGH)) { // düşük yüklü zImage // 0x0100 <= start_sys_seg < CS-DELTA_INITSEGdo_move0: AX = 0x100; BP = CS - DELTA_INITSEG; // INITSEG olarak da bilinir BX = start_sys_seg;do_move: sistem imgesini (start_sys_seg:0 .. CS-DELTA_INITSEG:0)'dan 0100:0'a taşı; // her seferinde 0x1000 bayt taşı }end_move:
code32_start'ın zImage için 0x1000 adresi ile bzImage için ise 0x100000 adresi ile ilklendirildiğine dikkat edin. code32 değeri denetimin Koru***ı Kipe Geç içindekilinux/arch/i386/boot/compressed/head.S'e geçirilmesi için kullanılacaktır. zImage önyüklemesi yapılırsa vmlinux'u 0100:0'a taşır; bzImage önyüklemesi yapılırsa bvmlinux start_sys_seg:0'da kalır. Taşıma adresi linux/arch/i386/boot/compressed/Makefile içindeki -Ttext seçeneğiyle uyuş***ıdır. Bakınız linux/arch/i386/boot/compressed/Makefile.
Sonra, eğer gerekliyse, kod CS-DELTA_INITSEG:0'dan (bbootsect ve bsetup) INITSEG:0'a taşınır.
DS = CS; // SETUPSEG olarak da bilinir // sürüm <=201 ile geriye dönük uyumluluğa ihtiyacımız olup olmadığını kontrol et if (!cmd_line_ptr && 0x20!=type_of_loader && SETUPSEG!=CS) { cli; // taşınırken kesme olarak yığıtı kullanabilir // store new SS in DX AX = CS - DELTA_INITSEG; DX = SS; if (DX>=AX) { // yığıt çerçevesi birlikte taşınacak DX = DX + INITSEG - AX; // i.e. SS-CS+SETUPSEG }move_self_1: /* CS-DELTA_INITSEG:0'dan INITSEG:0'ya taşı (setup_move_size bayt) * CS:IP üzerindeki kodun üzerine yazmamak için iki adımda * (src < dest) taşı fakat aşağıya doğru ("std") */ move CS-DELTA_INITSEG:move_self_here+0x200 to INITSEG:move_self_here+0x200, setup_move_size-(move_self_here+0x200) bytes; // INITSEG:move_self_here+0x200 == SETUPSEG:move_self_here goto SETUPSEG:move_self_here; // artık CS=SETUPSEGmove_self_here: move CS-DELTA_INITSEG:0 to INITSEG:0, move_self_here+0x200 bytes; // goto'dan önce eski CS anlamında DS = SETUPSEG; SS = DX; }end_move_self:}
Tekrar dikkat edin, bvmlinux'u yüklediğinde bootsect_helper() tarafından type_of_loader'un değeri 0x20 olarak değiştirilmektedir.
A20'yi Etkinleştir
A20 sorunu ve çözümü için A20 - a pain from the past'e başvurun.
A20_TEST_LOOPS = 32 # Bekleme başına adım s***sı A20_ENABLE_LOOPS = 255 # deneme için toplam döngü{#if defined(CONFIG_MELAN) // Enable A20. AMD Elan bug fix. outb(0x02, 0x92); // outb(val, port)a20_elan_wait: while (!a20_test()); // testi geçemedi goto a20_done;#endifa20_try_loop: // Önce, A20 kapısı olmayan bir sistemde olup olmadığımıza bak.a20_none: if (a20_test()) goto a20_done; // testi geçti // Sonra, BIOS'u (INT 0x15, AX=0x2401) denea20_bios: int15/AX=2401h; // Int15/AX=2401h: SYSTEM - later PS/2s - ENABLE A20 GATE if (a20_test()) goto a20_done; // testi geçti // Klavye denetleyici üzerinden A20'yi etkinleştirmeye çalışa20_kbc: empty_8042(); if (a20_test()) goto a20_done; // BIOS gecikmesi durumunda testi tekrarla outb(0xD1, 0x64); // komut yaz empty_8042(); outb(0xDF, 0x60); // A20 etkin empty_8042(); // a20 gerçekten etkin olana kadar beklea20_kbc_wait: CX = 0;a20_kbc_wait_loop: do { if (a20_test()) goto a20_done; // testi geçti } while (--CX) // Son girişim: "yapılandırma portu A"'yı kullan outb((inb(0x92) | 0x02) & 0xFE, 0x92); // yapılandırma portu A etkilenene kadar beklea20_fast_wait: CX = 0;a20_fast_wait_loop: do { if (a20_test()) goto a20_done; // testi geçti } while (--CX) // A20 hala cevap vermiyor. Tekrar ayarlam*** dene. if (--a20_tries) goto a20_try_loop; prtstr("linux: fatal error: A20 gate not responding!");a20_die: hlt; goto a20_die;}a20_tries: .byte A20_ENABLE_LOOPS // i.e. 255a20_err_msg: .ascii "linux: fatal error: A20 gate not responding!" .byte 13, 10, 0
I/O port işlemleri için, Kaynakça içindeki ilgili başvuru materyallerine bakın.
Koru***ı Kipe Geç
Tüm 32 bitlik IA-32 işlemcileri ile kod uyumluluğunu sağlamak için, aşağıdaki koru***ı kipe geçmeyi sağlayacak adımları yerine getirin:
İlk GDT girdisinde boş tanıml***cılı GDT'yi hazırl***n, bir kod bölütü tanıml***cı ve bir veri bölütü tanıml***cı;
Maskelenebilir donanım kesmeleri ve NMI dahil kesmeleri etkisiz kılın;
lgdt asm komutunu kullanarak GDTR yazmacına taban adresini ve GDT sınırını yükleyin;
mov cr0 (Intel 386 ve üstü) veya lmsw komutu (Intel 286 ile uyumluluk için) kullanarak CR0 yazmacındaki PE bayrağını etkinleştirin;
Ardından bir uzak jmp veya bir uzak call komutu çalıştırın.
Yığıt nor*** oku/yaz veri bölütüne yerleştirilebilir, böylece adanmış tanıml***cıya gerek kalmaz.
a20_done:{ lidt idt_48; // load idt with 0, 0; // DS:gdt'yi doğrusal göstericiye dönüştür *(long*)(gdt_48+2) = DS << 4 + &gdt; lgdt gdt_48; // yardımcı işlemciyi sıfırla outb(0, 0xF0); delay(); outb(0, 0xF1); delay(); // kesmeleri yeniden programla outb(0xFF, 0xA1); // tüm kesmeleri maskele delay(); outb(0xFB, 0x21); // irq2 dışında tüm irq'ları maskele // koru***ı kip! AX = 1; lmsw ax; // makina durumu kelimesi (word), CR0'ın 0'dan 15'e // kadar bitleri sadece PE, MP, EM ve TS bayraklarını // etkiler goto flush_instr;flush_instr: BX = 0; // bir önyükleme gösteren bayrak ESI = (CS - DELTA_INITSEG) << 4; // gerçek kip kod göstericisi /* NOT: Yüksek yüklenen büyük çekirdekler için * jmpi 0x100000,__KERNEL_CS'ye gereksinim duyarız * * fakat henüz CS yazmacını yüklemedik, * bu yüzden hedef konumun vars***lan boyutu hala 16 bit. * Bununla birlikte, bir terim öneki (0x66) kullanarak, * CPU bizim 48 bit uzak göstericimizi uygun bir şekilde alır. * Bakınız (INTeL 80386 Programmer's Reference Manual, * Mixing 16-bit and 32-bit code, page 16-6) */ // __KERNEL_CS:[(uint32*)code32]'e git; .byte 0x66, 0xeacode32: .long 0x1000 // Koru***ı Kip için Hazırlık // içinde üstüne yazılır .word __KERNEL_CS // bölüt 0x10 // bakınız linux/arch/i386/boot/compressed/head.S:startup_32}
Uzak jmp komutu (0xea) CS kaydedicisini günceller. kalan bölüt kaydedicileri (DS, SS, ES, FS ve GS) içeriği daha sonra yeniden yüklenmelidir. terim boyutu öneki (0x66) jmp'ı 32 bitlik terim code32'ye kadar çalıştırılmasına zorlamak için kullanılır. Terim boyutu öneki ayrıntıları için bakınız: IA-32 Manual (Vol.1. Ch.3.6. Operand-size and Address-size Attributes, and Vol.3. Ch.17. Mixing 16-bit and 32-bit Code).
Denetim linux/arch/i386/boot/compressed/head.S:startup_32'ye geçirilir. zImage için 0x1000 adresinde; bzImage için 0x100000 adresinde. Bakınız: linux/arch/i386/boot/compressed/head.S.
ESI toplanan sistem verilerinin bellek alanını gösterir. 16 bitlik gerçek kip çekirdek kodundan 32 bitlik kısma parametre geçirmek için kullanılır. Ayrıntılar için linux/Documentation/i386/zero-page.txt dosyasına bakınız.
Daha fazla anahtarlama ayrıntısı için IA-32 Manual Vol.3. (Ch.9.8. Software Initialization for Protected-Mode Operation, Ch.9.9.1. Switching to Protected Mode, and Ch.17.4. Transferring Control Among Mixed-Size Code Segments) belgesine başvurun.
Muhtelif
Kalanlar destek işlevleri ve değişkenleridir.
/* linux/Makefile hedefleri tarafından oluşturulan makrolar: * include/linux/compile.h ve include/linux/version.h */kernel_version: .ascii UTS_RELEASE .ascii " (" .ascii LINUX_COMPILE_BY .ascii "@" .ascii LINUX_COMPILE_HOST .ascii ") " .ascii UTS_VERSION .byte 0///////////////////////////////////////////////////////////////////////////////default_switch() { cli; outb(0x80, 0x70); } /* Kesmeleri ve NMI'yi iptal et */bootsect_helper(ES:BX); /* bkz. Bootsect Yardımcısı *////////////////////////////////////////////////////////////////////////////////a20_test(){ FS = 0; GS = 0xFFFF; CX = A20_TEST_LOOPS; // i.e. 32 AX = FS:[0x200]; do {a20_test_wait: FS:[0x200] = ++AX; delay(); } while (AX==GS:[0x210] && --CX); return (AX!=GS[0x210]); // ZF==0 (i.e. NZ/NE, a20_test!=0) means test passed}///////////////////////////////////////////////////////////////////////////////// klavye komut kuyruğu boş mu, bakempty_8042(){ int timeout = 100000; for (; {empty_8042_loop: if (!--timeout) return; delay(); inb(0x64, &AL); // 8042 durum portu if (AL & 1) { // çıktı delay(); inb(0x60, &AL); // okuno_output:} else if (!(AL & 2)) return; // girdi yok }}///////////////////////////////////////////////////////////////////////////////// CMOS saatini oku, AL'den saniyeyi döndür, video.S'de kullanılırgettime(){ int1A/AH=02h(); /* int1A/AH=02h: SAAT - GERÇEK SAAT ZAMANINI AL * DH = BCD gösterimli saniye*/ AL = DH & 0x0F; AH = DH >> 4; aad;}///////////////////////////////////////////////////////////////////////////////delay() { outb(AL, 0x80); } // I/O yaptıktan sonra gerekli// Tanıml***cı tablogdt: .word 0, 0, 0, 0 # dummy .word 0, 0, 0, 0 # kullanılmadı // bölüt 0x10, __KERNEL_CS .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) .word 0 # taban adres = 0 .word 0x9A00 # kodu oku/çalıştır .word 0x00CF # tanelilik (granularity) = 4096, 386 # (sınırın 5. yarım baytı) // bölüt 0x18, __KERNEL_DS .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) .word 0 # taban adres = 0 .word 0x9200 # veri oku/yaz .word 0x00CF # tanelilik = 4096, 386 # (sınırın 5. yarım baytı)idt_48: .word 0 # idt sınırı = 0 .word 0, 0 # idt tabanı = 0L/* [gdt_48] komutla eşleşmesi için 0x0800 (2048) ol***ı, * Linux 2.2.22'nin yaptığı gibi. */gdt_48: .word 0x8000 # gdt sınırı=2048, # 256 GDT girdisi .word 0, 0 # gdt tabanı (daha sonra doldurulur)#include "video.S"// setup.S'nin sonundaki imza:{setup_sig1: .word SIG1 // 0xAA55setup_sig2: .word SIG2 // 0x5A5Amodelist:}
video.S içindeki video ayar ve algılama kodu:
ASK_VGA = 0xFFFD // defined in linux/include/asm-i386/boot.h///////////////////////////////////////////////////////////////////////////////video(){ pushw DS; // farklı bölütler kullan FS = DS; DS = ES = CS; GS = 0; cld; basic_detect(); // temel kart türü testi (EGA/VGA/MDA/CGA)#ifdef CONFIG_VIDEO_SELECT if (FS:[0x01FA]!=ASK_VGA) { // kullanıcı seçimli video kipi mode_set(); if (failed) { prtstr("You passed an undefined mode number.\n"); mode_menu(); } } else {vid2: mode_menu(); }vid1:#ifdef CONFIG_VIDEO_RETAIN restore_screen(); // ekran içeriğini geri yükle#endif /* CONFIG_VIDEO_RETAIN */#endif /* CONFIG_VIDEO_SELECT */ mode_params(); // kip parametrelerini sakla popw ds; // orjinal DS'yi geri yükle}