| |||||||
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ı kurmalıdırlar, özellikle type_of_loader; sonra denetimi bsetup'a devreder. setup.S şunları varsayar:
/* LILO'nun doğru yüklendiğinden emin olmak için imza kelimeleri */
#define SIG1 0xAA55
#define SIG2 0x5A5A
INITSEG = DEF_INITSEG # 0x9000, önyüklemeyi buraya taşır, yolun dışına
SYSSEG = 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ği
DELTA_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şlamalı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 olur
realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
start_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 below
type_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 olmalı, (RFU) loadflags arasındaki bitler
loadflags:
LOADED_HIGH = 1 # Sıfır değilse, çekirdek yüksek yüklenmiştir
CAN_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
#endif
setup_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 kod
code32_start: # için farklı başlangıç
# adresleri koyarlar.
#ifndef __BIG_KERNEL__
.long 0x1000 # 0x1000 = zImage için varsayılan
#else
.long 0x100000 # 0x100000 = büyük çekirdek için öntanımlı
#endif
ramdisk_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 cinsindendir
bootsect_kludge:
.word bootsect_helper, SETUPSEG
heap_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ır
pad1: .word 0
cmd_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ılmalı. 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 vmalloc() ve iomap() olduğu kadar * "fixmap" eşleştirmeleri (mappings) için de tahsis edilir. */ #define __VMALLOC_RESERVE (128 << 20) #define __MAXMEM (-__PAGE_OFFSET-__VMALLOC_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ı /Boyut 0200/2 2.00+ jump Sıçrama komutu 0202/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östericisi 0210/1 2.00+ type_of_loader Önyükleme yükleyici belirteci 0211/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 çengeli 0218/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çin 0224/2 2.01+ heap_end_ptr kurulum bittikten sonra boş bellek 0226/2 N/A pad1 Kullanılmaz 0228/4 2.02+ cmd_line_ptr çekirdek komut satırına 32-bit gösterici 022C/4 2.03+ initrd_addr_max En yüksek yasal initrd adresi
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 imzayı kontrol et
start_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 imzayı kontrol et
if (setup_sig1!=SIG1 || setup_sig2!=SIG2) {
goto bad_sig;
}
goto goodsig1;
}
///////////////////////////////////////////////////////////////////////////////
// bazı küçük işlevler
prtstr(); /* 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ırmayı 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ırmayı 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ü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.
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 al
loader_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, ES:DI=DS:DI);
// int15/AX=E820h: GET SYSTEM MEMORY MAP
if (failed || 'SMAP'!=EAX) break;
// if (1!=DS:[DI+16]) continue; // kullanışsız
good820:
if (DS:[1E8]>=32) break; // entry# > E820MAX
DS:[0x1E8]++; // entry# ++;
DI += 20; // tamponu sonraki için ayarla
again820:
} while (!EBX) // bitmedi
bail820:
/* 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 YAPILANDIRMALAR İÇİ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
#endif
mem88: // 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 sayısı */
DS:[2] = AX;
} |
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 ise
no_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 bayt
no_mca:
// PS/2 noktalama cihazlarını (pointing device) kontrol et
CS-DELTA_INITSEG:[0x1FF] = 0; // varsayılan noktalama cihazı 0
int11h();
// int11h: BIOS - EKİPMAN LİSTESİNİ AL
if (AL & 0x04) { // fare kuruldu
DS:[0x1FF] = 0xAA;
}
}
|
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
|
// kip seçiciyi çağır
{
if (realmode_swtch) {
realmode_swtch(); // kip seçme çengeli
} else {
rmodeswtch_normal:
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_INITSEG
do_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 Korumalı 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şmalı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=SETUPSEG
move_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 sorunu ve çözümü için A20 - a pain from the past'e başvurun.
A20_TEST_LOOPS = 32 # Bekleme başına adım sayı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;
#endif
a20_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) dene
a20_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 bekle
a20_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 bekle
a20_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 ayarlamayı 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. 255
a20_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.
Tüm 32 bitlik IA-32 işlemcileri ile kod uyumluluğunu sağlamak için, aşağıdaki korumalı kipe geçmeyi sağlayacak adımları yerine getirin:
Yığıt normal oku/yaz veri bölütüne yerleştirilebilir, böylece adanmış tanımlayı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
// korumalı 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 varsayı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, 0xea
code32: .long 0x1000 // Korumalı 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.
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, bak
empty_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); // oku
no_output:} else if (!(AL & 2)) return; // girdi yok
}
}
///////////////////////////////////////////////////////////////////////////////
// CMOS saatini oku, AL'den saniyeyi döndür, video.S'de kullanılır
gettime()
{
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ımlayıcı tablo
gdt:
.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) olmalı,
* 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 // 0xAA55
setup_sig2: .word SIG2 // 0x5A5A
modelist:
}
|
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
}
|
/* YAPILACAKLAR: video() ayrıntıları */
| ||||||||||