Nachladen des Kernels durch eigenen Bootloader


  • Mod

    Es gibt da einen Punkt bei der OS-Entwicklung mit eigenem Bootloader, der mir gerade Grenzen aufzeigt. Ich kann hier ...

    mov al, 63         ; read n sectors (max. 63!!!)
    

    ... nur max. 63 Sektoren einlesen. Der Kernel wird aber größer und größer.
    Hat jemand einen Hinweis oder Link, wie man dieses Thema mit eigenem Bootloader sauber löst? Der Kernel wird doch ab 0x8000 geladen? Da müsste doch genügend Platz sein.

    Hier der bisher für "kleine" Kernel erfolgreich verwendete Code:

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; read kernel from floppy disk ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
        mov [bootdrive], dl ; boot drive stored by BIOS in DL.
                            ; we save it to [bootdrive] to play for safety.
    
    load_kernel:
        xor ax, ax         ; mov ax, 0  => function "reset"
        int 0x13         
        jc load_kernel     ; trouble? try again
    
        mov bx, 0x8000     ; set up start address of kernel
    
        ; set parameters for reading function
        ; 8-bit-wise for better overview
        mov dl,[bootdrive] ; select boot drive
        mov al, 63         ; read n sectors (max. 63!!!)
        mov ch,  0         ; cylinder = 0
        mov cl,  2         ; sector   = 2
        mov dh,  0         ; head     = 0
        mov ah,  2         ; function "read" 
        int 0x13         
        jc load_kernel     ; trouble? try again
    

    INT 13H (0x13)

    Function 02H (0x02)  Read sector

    Call with: AH = 02H
    AL = number of sectors
    CH = cylinder
    CL = sector
    DH = head
    DL = drive
    00H-7FH floppy disk
    80H-FFH fixed disk

    ES:BX = segment: offset of buffer

    Returns: If function successful
    Carry flag = clear
    AH = 00H
    AL = number of sectors transferred

    If function unsuccessful
    Carry flag = set
    AH = status

    Woher kommt die Beschränkung mit den 63 Sektoren in obigem Fall?
    Ist der Bootloader aus Sektor 1 im Weg (nachfolgend wird dieser vorher aus dem Weg geschafft)? Ein gewisser Linus Torvalds lädt hier z.B. deutlich mehr: 🙂

    http://stuff.mit.edu/afs/sipb/user/tlyu/tmp/memtest86/bootsect.S
    Code-Ausschnitt:

    load_setup:
    	xor	dx, dx			! drive 0, head 0
    	mov	cx,#0x0002		! sector 2, track 0
    	mov	bx,#0x0200		! address = 512, in INITSEG
    	mov	ax,#0x0200+SETUPSECS	! service 2, nr of sectors
    					! (assume all on head 0, track 0)
    	int	0x13			! read it
    	jnc	ok_load_setup		! ok - continue
    
    	push	ax			! dump error code
    	call	print_nl
    	mov	bp, sp
    	call	print_hex
    	pop	ax	
    
    	xor	dl, dl			! reset FDC
    	xor	ah, ah
    	int	0x13
    	jmp	load_setup
    
    ok_load_setup:
    


  • Naja, die BIOS Funktion hat seine ganzen Parameter ja nicht umsonst. Da musst du wohl die Funktion mehrfach aufrufen, mit jeweils aus der Logischen Block Adresse (LBA) berechneten Parametern für Zylinder, Kopf und Sektor.

    Die Formel für die Umrechnung ist an vielen Stellen zu finden. Unter anderem auch hier:
    http://lowlevel.brainsware.org/wiki/index.php/Logical_Block_Addressing
    Wikipedia hat dazu auch ein paar Haufen Beispiel-Zahlen: http://en.wikipedia.org/wiki/Cylinder-head-sector

    Dazu musst du allerdings die Parameter deines Laufwerks bzw. des Mediums wissen. Bei FAT stehen sie im Bootsektor in dieser netten Datenstruktur am Anfang. Oder du rechnest die selbst aus. Oder Nimmst diese Tabelle:
    http://www.pcguide.com/ref/fdd/mediaGeometry-c.html

    Geschickte Assemblerprogrammierer kommen natürlich beim inkrementellen lesen eines linearen Abbilds wie deinem Kernel ohne Multiplikationen und Divisionen aus.

    Du solltest da auf off-by-one-Probleme aufpassen, weil LBA bei 0 anfängt, während CHS Sektoren bei 1 anfängt zu zählen.



  • Erhard Henkes schrieb:

    Woher kommt die Beschränkung mit den 63 Sektoren in obigem Fall?

    6 Bit -> 64 Werte. Und ein paar gehen wohl aus technischen Gründen flöten. Willkommen im Jahr 1980.

    Ist der Bootloader aus Sektor 1 im Weg (nachfolgend wird dieser vorher aus dem Weg geschafft)? Ein gewisser Linus Torvalds lädt hier z.B. deutlich mehr: 🙂

    http://stuff.mit.edu/afs/sipb/user/tlyu/tmp/memtest86/bootsect.S
    Code-Ausschnitt:
    [code]load_setup:
    xor dx, dx ! drive 0, head 0
    mov cx,#0x0002 ! sector 2, track 0
    mov bx,#0x0200 ! address = 512, in INITSEG
    mov ax,#0x0200+SETUPSECS ! service 2, nr of sectors

    SETUPSECS hat den Wert 1. Er lädt nur einen Sektor. Siehe -> http://stuff.mit.edu/afs/sipb/user/tlyu/tmp/memtest86/mtest.h

    An anderer Stelle lädt er sicherlich noch weitere Sektoren. Aber nicht mit schwarzer Magie, sondern schön nacheinander.


  • Mod

    Jetzt muss ich doch noch mal blöd fragen:
    Floppy Disk (1,44 MB):

    Was kommt nach CHS = 0, 0, 63? Muss ich dann den Head-Wert von 0 auf 1 erhöhen (oder den Cylinder)?
    (Floppy hat ja nur 2 Heads) also CHS = 0, 1, 1 ... und wenn ich dann bei CHS = 0, 1, 63 anlange, geht es mit CHS = 1, 0, 1 weiter oder anders?

    Eigentlich gibt es doch nur CHS = (80)(2)(18) = 2880 Sektoren auf einer Floppy.
    Wieso kann ich da eigentlich bei den Sektoren über 18 gehen bis 63?

    So Lowlevel musste ich bisher nicht rumtigern, richtig lustig. 😃

    MitGRUBwärDasNichPassiert

    Das wird vermieden, solange es irgendwie geht!



  • Du kannst in diesem Fall mehr als 18 Sektoren am Stück lesen, wenn du ein relativ modernes BIOS hast, das die Berechnungen für dich übernimmt. Siehe auch http://www.ctyme.com/intr/rb-0607.htm

    Den Sektor 63 kannst du allerdings nicht adressieren, wenn er nicht existiert.

    Ob du Head oder Cylinder erhöhen musst ist theoretisch eine Frage der Konvention. Praktisch ist es durch LBA festgelegt, dass zuerst Head erhöht wird. Siehe auch die spannende, bereits verlinkte Tabelle: http://en.wikipedia.org/wiki/Cylinder-head-sector#Equivalent_values_for_CHS_and_LBA

    Du bist übrigens gar nicht mal so "Lowlevel". Du nutzt einfach nur einen veralteten Treiber, mit einem inkonsistenten Interface.


  • Mod

    Du kannst in diesem Fall mehr als 18 Sektoren am Stück lesen, wenn du ein relativ modernes BIOS hast, das die Berechnungen für dich übernimmt.

    Danke für den Hinweis.

    Ich habe bisher folgenden Code, der aber nicht zum Erfolg führt:

    mov [bootdrive], dl ; boot drive stored by BIOS in DL.
                            ; we save it to [bootdrive] to play for safety.
    
    load_kernel1:
        xor ax, ax         ; mov ax, 0  => function "reset"
        int 0x13         
        jc load_kernel1     ; trouble? try again
    
        mov bx, 0x8000     ; set up start address of kernel
        ; set parameters for reading function (8-bit-wise for better overview)
        mov dl,[bootdrive] ; select boot drive
        mov al, 63         ; read n sectors
        mov ch,  0         ; cylinder = 0
        mov cl,  2         ; sector   = 2
        mov dh,  0         ; head     = 0
        mov ah,  2         ; function "read" 
        int 0x13         
    	jc load_kernel1    ; trouble? try again
    
    load_kernel2:	
        xor ax, ax        ; mov ax, 0  => function "reset"
        int 0x13         
        jc load_kernel2   ; trouble? try again
    
    	mov bx, 0xFE00     ; 
    	mov dl,[bootdrive] ; select boot drive
    	mov al, 63         ; read n sectors
    	mov ch,  0         ; cylinder = 1
        mov cl, 11         ; sector   = 11
        mov dh,  1         ; head     = 1
        mov ah,  2         ; function "read" 
        int 0x13         
        jc load_kernel2    ; trouble? try again
    

    Ideen:

    C 0      0      1      1
    H 0      1      0      1 
    S 1...18 1...18 1...18 1...18
    

    0x8000 + (63*0x200) = 0xFE00 für zweite Runde Sektoren laden.

    Könnte das natürlich auch 18er weise machen.
    Muss ich jedes mal resetten?


  • Mod

    Den Sektor 63 kannst du allerdings nicht adressieren, wenn er nicht existiert.

    mov [bootdrive], dl ; boot drive stored by BIOS in DL.
                            ; we save it to [bootdrive] to play for safety.
    
    load_kernel1:
        xor ax, ax         ; mov ax, 0  => function "reset"
        int 0x13         
        jc load_kernel1     ; trouble? try again
    
        mov bx, 0x8000     ; set up start address of kernel
        ; set parameters for reading function (8-bit-wise for better overview)
        mov dl,[bootdrive] ; select boot drive
        mov al, 62         ; read n sectors
        mov ch,  0         ; cylinder 
        mov cl,  2         ; sector   
        mov dh,  0         ; head     
        mov ah,  2         ; function "read" 
        int 0x13         
    	jc load_kernel1    ; trouble? try again
    
    load_kernel2:	
        ;xor ax, ax        ; mov ax, 0  => function "reset"
        ;int 0x13         
        ;jc load_kernel2   ; trouble? try again
    
    	mov bx, 0xFC00     ; 
    	mov dl,[bootdrive] ; select boot drive
    	mov al, 52         ; read n sectors
    	mov ch,  1         ; cylinder 
        mov cl, 10         ; sector   
        mov dh,  1         ; head     
        mov ah,  2         ; function "read" 
        int 0x13         
        jc load_kernel2    ; trouble? try again
    

    geht auch nicht.


  • Mod

    mov [bootdrive], dl ; boot drive stored by BIOS in DL.
                            ; we save it to [bootdrive] to play for safety.
    
    load_kernel1:
        xor ax, ax         ; mov ax, 0  => function "reset"
        int 0x13         
        jc load_kernel1     ; trouble? try again
    
        mov bx, 0x8000     ; set up start address of kernel
        ; set parameters for reading function (8-bit-wise for better overview)
        mov dl,[bootdrive] ; select boot drive
        mov al, 17         ; read n sectors
        mov ch,  0         ; cylinder 
        mov cl,  2         ; sector   
        mov dh,  0         ; head     
        mov ah,  2         ; function "read" 
        int 0x13         
    	jc load_kernel1    ; trouble? try again
    
    load_kernel2:	
        ;xor ax, ax        ; mov ax, 0  => function "reset"
        ;int 0x13         
        ;jc load_kernel2   ; trouble? try again
    
    	mov bx, 0xA200     ; 
    	mov dl,[bootdrive] ; select boot drive
    	mov al, 63         ; read n sectors
    	mov ch,  0         ; cylinder 
        mov cl,  1         ; sector   
        mov dh,  1         ; head     
        mov ah,  2         ; function "read" 
        int 0x13         
        jc load_kernel2    ; trouble? try again
    

    ... auch nicht. Habe auch cyl/head 0 und 1 umgekehrt probiert.


  • Mod

    Most BIOSes support "multitrack" reads, where the value in AL exceeds the number of sectors remaining on the track, in which case any additional sectors are read beginning at sector 1 on the following head in the same cylinder

    Also wäre doch der Ansatz

    C 0      0      1      1
    H 0      1      0      1
    S 1...18 1...18 1...18 1...18
    

    nicht verkehrt. Offenbar geht das ja auch Cylinder-übergreifend, allerdings nur bis sector 63.



  • Evtl erst 62 Sektoren von CHS:0/0/2 nach 0x8000, dann 63 Sektoren von 0/1/1 nach 0xFC00?


  • Mod

    Das hatte ich im mittleren Versuch bereits probiert (mit 52 im zweiten Satz).

    mov bx, 0xFC00     ;
        mov dl,[bootdrive] ; select boot drive
        mov al, 52         ; read n sectors
        mov ch,  1         ; cylinder
        mov cl, 10         ; sector  
        mov dh,  1         ; head    
        mov ah,  2         ; function "read"
        int 0x13
    

    Ich teste allerdings mit Bochs, vielleicht verkehrt (das mit 63 in einem Ansatz hat bisher jedoch auf bochs funktioniert).

    Ein in Bochs lauffähiges OS lief nun nicht in real life auf einem PC, daher macht das jetzt auch keinen Sinn ein "schwieriges" OS so zu testen.

    Mist! Bin wohl langsam doch mürbe für Linux und GRUB. Man wird meistens nur an-/ausgelacht, wenn man sich mit eigenem Bootloader und Windows quält. 🙄

    Was ich benötige, ist ein Vorbild eines Bootloaders, der einen Kern größer 63 Sektoren laden muss. Habe dies bisher nichr gefunden. Pls help. 🙂


  • Mod

    Das Problem scheint eine harte Begrenzung bei 64K zu sein.
    http://groups.google.com/group/de.comp.lang.assembler.x86/browse_thread/thread/67307a357a9ca137/e62f588a36e8ad46?lnk=raot

    CHS habe ich im Griff:
    http://lowlevel.brainsware.org/forum/index.php?topic=2219.msg25073#msg25073

    Daher habe ich nun versucht, meinen Kernel nach 0x10000 zu verlagern, denn obwohl ich CHS richtig gesteuert habe, knallt es bei 0xFE00 einfach brutal und verlagern auf ein anderes Segment hilft nicht!

    Leider besteht hier mit der neuen Kernel-Adresse noch ein Problem - entweder mit dem Target-Puffer ES:BX oder mit dem far jump zum PM:

    ...
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Load GDT, switch to PM, and jump to kernel ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;		
    
        cli               ; clear interrupts
        lgdt [gdtr]       ; load GDT via GDTR (defined in file "gtd.inc")	
    
        mov eax, cr0      ; switch-over to Protected Mode
        or  eax, 1        ; set bit 0 of CR0 register
        mov cr0, eax      ;	
    
        jmp 08:0x10000    ; this is a 16-bit-FAR-Jmp, but CS is loaded with "index" 8 in GDT 
                          ; hence, the code will be interpreted as 32 bit from here on
    					  ; the address can be found in the linker script 
    
    ...
    

    (EDIT: gekürzt)

    kernel.ld:

    OUTPUT_FORMAT("binary")
    ENTRY(KernelStart)
    SECTIONS
    { 
      . = 0x00010000;
      .text   : { *(EXCLUDE_FILE(shared_pages.o).text) } 
      . = 0x00015000;
      .text1  : { shared_pages.o(.text)                } 
      . = 0x00016000;
      .data   : { *(.data)                             } 	
      .rodata : { *(.rodata)                           }
      .bss    : { *(.bss)                              }  
    }
    

    kernel.asm:

    [Bits 32]
    section .text          ; ld needs that for coff format
    
    [global KernelStart]
    KernelStart:
        mov    ax, 0x10
        mov    ds, ax      ; data descriptor --> data, stack and extra segment
        mov    ss, ax           
        mov    es, ax
        xor    eax, eax    ; null desriptor --> FS and GS
        mov    fs, ax
        mov    gs, ax
        mov    esp, 0x200000 ; set stack below 2 MB limit
    
    [extern _main]         ; entry point in ckernel.c
    	call _main ; ->-> C-Kernel
        jmp $
    

    Wenn ich den far jump auf
    jmp 0x1000:0x0000
    abändere, hilft das leider auch nicht.
    Bochs resettet in jedem Fall.
    Kann jemand helfen?

    PS: Kein Wunder, das alle GRUB einsetzen. Bootloader scheint aufgrund einiger schwer überwindbarer antiker Strukturen ein wirklich hartes Brot zu sein. 🙄



  • Bei deinem Sprung ("jmp 0x8:0x10000") gibt mein Assembler (NASM) sogar eine Warnung aus: warning: word data exceeds bounds

    Er assembliert das also zu jmp word 0x8:0x0000. Was ja auch klar ist, wenn man im Real Mode ist, weil Segment und Offset halt 16 Bit sind.

    Du solltest auf deine Ausgaben gucken, oder deinen Assembler updaten.

    Aber was für ein Glück, dass man das Offset auf 32 Bit erweitern kann: jmp dword 0x8:0x10000


  • Mod

    jmp dword 08:0x10000

    Thx! 👍
    Gibt man "jmp dword 08:0x10000" in Google ein, findet man nur uns hier:
    http://www.google.de/search?hl=de&q="jmp+dword+08%3A0x10000"&btnG=Suche&meta= 😕 (Ich liebe solche Unikate)

    Du solltest auf deine Ausgaben gucken

    werde ich beherzigen.


  • Mod

    Allerdings dürfte der nächste Schlag nun bei einem Kernel mit mehr als 128 Sektoren vorprogrammiert sein (EDIT: ich habe es ausprobiert: es stimmt!). Weiß da jemand etwas Genaues, wie man diese harte Begrenzung umgeht?



  • Wie in dem Verlinkten Thema steht, duerfte das an dem antike DMA-Controller fuer Floppys scheitern. Einen direkten Workarround bei diesen ISA-Fossilien kenne ich auch nicht.
    Es wird dir also wohl nichts anderes uebrig bleiben, als dich damit zu arrangieren. Moeglichkeiten gibt es schliesslich viele (von denen einige eigentlich auch vom BIOS selbst umgesetzt werden koennen sollten, aber egal...).
    Naheliegend waere es zB., in einen guenstig gelegenen Buffer einzulesen und von dort Sektor fuer Sektor an die gewuenschte Adresse zu kopieren oder eben selbst genau darauf zu achten, dass du dem DMA mit seinen 64K-Pages nicht in's Gehege kommst.


  • Mod

    Merkwürdigerweise finde ich zu diesem Thema keine Vorbilder. Bootloader für solche Anwendungen sind offenbar ein eher "verstecktes" Thema. In Foren wird man sofort auf GRUB zugesteuert.

    Naja, 64KB (128 Sektoren) ist schon eine Menge für einen Hobby-Kernel. 😃
    Immerhin wurde nun die durch 6 Bit bedingte 63-Sektoren-Grenze durch die eigene Berechnung der CHS geschafft. Immer step-by-step oder "festina lente" wie der Lateiner sagt.



  • Evtl hilft auch der offen liegende Code von GRUB weiter? Hab ihn mir interessehalber gerade auch mal angesehen, dies hier müsste der Boot-Code sein (ansonsten DL für alles hier).


  • Mod

    Danke für den Link zu GRUB!

    Ärgerlich finde ich, dass ich den Kernel nun ab 0x10000 beginnen musste. Wir hatten ihn ja extra nach 0x8000 gelegt, um den Speicher zu "schonen". 😉 Naja, immerhin versteht man nun besser, warum man typischerweise dort beginnt.

    MitGRUBwärDasNichPassiert

    In meinem Tutorial versuche ich GRUB solange zu vermeiden, bis es wirklich notwendig wird. Bisher wurde immer ein work-around gefunden. Man lernt mit eigenem Bootloader auch eindeutig mehr über die Basics (z.B. die harte Grenze durch ISA DMA oder die eigene CHS-Berechnung 0,0,1...18 | 0,1,1...18 | 1,0,1...18 | 1,1,1...18 | 2,0,1...18 | ... ). 🙂

    Damit kann man auch große Kernel nachladen:

    org 0x7C00  ; set up start address of bootloader
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; setup a stack and segment regs ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
        xor ax, ax
        mov ds, ax
        mov es, ax
        mov ss, ax
        mov sp, ax
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;   read kernel from floppy disk                           ;
    ;   starting from C(ylinder) H(ead) S(ector): 0 0 2        ;
    ;   17 + 18 + 18 + 18 + 57 = 128 (hard segment limit)      ; 
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
        mov si,loadmsg     ; show loading message
        call print_string
        call Waitingloop
        mov [bootdrive], dl ; boot drive stored by BIOS in DL.
                            ; we save it to [bootdrive] to play for safety.
    load_kernel:
        xor ax, ax         ; mov ax, 0  => function "reset"
        int 0x13         
        jc load_kernel     ; trouble? try again
    
    	mov bx, 0x1000     
    	mov es, bx         ; target in memory: segment
        mov bx, 0x0000     ; target in memory: offset 
        mov dl,[bootdrive] ; select boot drive
        mov al, 17         ; read n sectors
        mov ch,  0         ; cylinder = 0
        mov cl,  2         ; sector   = 2
        mov dh,  0         ; head     = 0
        mov ah,  2         ; function "read" 
        int 0x13         
        jc load_kernel     ; trouble? try again
    
    load_kernel2:
        xor ax, ax         ; mov ax, 0  => function "reset"
        int 0x13         
        jc load_kernel2    ; trouble? try again
    
        mov bx, 0x2200     ; target in memory: offset 
        mov dl,[bootdrive] ; select boot drive
        mov al, 18         ; read n sectors
        mov ch,  0         ; cylinder 
        mov cl,  1         ; sector   
        mov dh,  1         ; head     
        mov ah,  2         ; function "read" 
        int 0x13         
        jc load_kernel2    ; trouble? try again
    
    load_kernel3:
        xor ax, ax         ; mov ax, 0  => function "reset"
        int 0x13         
        jc load_kernel3    ; trouble? try again
    
        mov bx, 0x4600     ; target in memory: offset 
        mov dl,[bootdrive] ; select boot drive
        mov al, 18         ; read n sectors
        mov ch,  1         ; cylinder 
        mov cl,  1         ; sector   
        mov dh,  0         ; head     
        mov ah,  2         ; function "read" 
        int 0x13         
        jc load_kernel3     ; trouble? try again	
    
    load_kernel4:
        xor ax, ax         ; mov ax, 0  => function "reset"
        int 0x13         
        jc load_kernel4    ; trouble? try again
    
        mov bx, 0x6A00     ; target in memory: offset 
        mov dl,[bootdrive] ; select boot drive
        mov al,  18        ; read n sectors
        mov ch,  1         ; cylinder 
        mov cl,  1         ; sector   
        mov dh,  1         ; head     
        mov ah,  2         ; function "read" 
        int 0x13         
        jc load_kernel4     ; trouble? try again	
    
    load_kernel5:
        xor ax, ax         ; mov ax, 0  => function "reset"
        int 0x13         
        jc load_kernel5    ; trouble? try again
    
        mov bx, 0x8E00     ; target in memory: offset 
        mov dl,[bootdrive] ; select boot drive
        mov al,  57        ; read n sectors (max. 57!!! due to max. 128)
        mov ch,  2         ; cylinder 
        mov cl,  1         ; sector   
        mov dh,  0         ; head     
        mov ah,  2         ; function "read" 
        int 0x13         
        jc load_kernel5     ; trouble? try again	
    
    ; new segment
    load_kernel10:
        xor ax, ax         ; mov ax, 0  => function "reset"
        int 0x13         
        jc load_kernel10     ; trouble? try again
    
    	mov bx, 0x2000     ; next segment
    	mov es, bx         ; target in memory: segment
        mov bx, 0x0000     ; target in memory: offset 
        mov dl,[bootdrive] ; select boot drive
        mov al, 63         ; read n sectors
        mov ch,  3         ; cylinder 
        mov cl,  4         ; sector   
        mov dh,  1         ; head     
        mov ah,  2         ; function "read" 
        int 0x13         
        jc load_kernel10     ; trouble? try again	
    
    	call Waitingloop
    
    ;;;;;;;;;;;;;
    ; A20-Gate  ;
    ;;;;;;;;;;;;;	
    
        in  al, 0x92      ; switch A20 gate via fast A20 port 92
        cmp al, 0xff      ; if it reads 0xFF, nothing's implemented on this port
        je .no_fast_A20
    
        or  al, 2         ; set A20_Gate_Bit (bit 1)
        and al, ~1        ; clear INIT_NOW bit (don't reset pc...)
        out 0x92, al
        jmp .A20_done
    
    .no_fast_A20:         ; no fast shortcut -> use the slow kbc...
        call empty_8042 
    
        mov al, 0xD1      ; kbc command: write to output port
        out 0x64, al
        call empty_8042
    
        mov al, 0xDF      ; writing this to kbc output port enables A20
        out 0x60, al
        call empty_8042
    
    .A20_done:
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Load GDT, switch to PM, and jump to kernel ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;		
    
        cli               ; clear interrupts
        lgdt [gdtr]       ; load GDT via GDTR (defined in file "gtd.inc")	
    
        mov eax, cr0      ; switch-over to Protected Mode
        or  eax, 1        ; set bit 0 of CR0 register
        mov cr0, eax      ;	
    
        jmp dword 08:0x10000    ; this is a 16-bit-FAR-Jmp, but CS is loaded with "index" 8 in GDT 
                          ; hence, the code will be interpreted as 32 bit from here on
    					  ; the address can be found in the linker script (phys)
    
    ;;;;;;;;;
    ; Calls ;
    ;;;;;;;;;
    
    print_string:
        mov ah, 0x0E      ; VGA BIOS fnct. 0x0E: teletype
    .loop:   
        lodsb             ; grab a byte from SI
        test al, al       ; NUL?
        jz .done          ; if the result is zero, get out
        int 0x10          ; otherwise, print out the character!
        jmp .loop
    .done:
        ret
    
    empty_8042:
        call Waitingloop
        in al, 0x64
        cmp al, 0xff      ; ... no real kbc at all?
        je .done
    
        test al, 1        ; something in input buffer?
        jz .no_output
        call Waitingloop
        in al, 0x60       ; yes: read buffer
        jmp empty_8042    ; and try again
    
    .no_output:
        test al, 2        ; command buffer empty?
        jnz empty_8042    ; no: we can't send anything new till it's empty
    .done:
    ret
    
    Waitingloop:                   
        mov cx,0xFFFF
    .loop_start:
        dec cx     
        jnz .loop_start
        ret       
    
    ;;;;;;;;;;;;
    ; Includes ;
    ;;;;;;;;;;;;
    
    %include "gdt.inc"
    
    ;;;;;;;;
    ; data ;
    ;;;;;;;;
    
        bootdrive db 0    
        loadmsg db "bootloader message: loading kernel ...",13,10,0
    
        times 510-($-$$) hlt
        db 0x55
        db 0xAA
    

    Nun fehlt nur noch ein netter Algorithmus, der ES:BX und CHS sektorenweise berechnet, dann könnte man dies in einer Schleife für n Sektoren erledigen.

    Den Teil "LBA2CHS" findet man hier:
    http://en.wikipedia.org/wiki/CHS_conversion#Assembler_code_example

    GetCHS proc
                mov         AX, LBA
                xor         DX, DX
                mov         BX, SPT
                div         BX
                inc         DX
                mov         SECT, DL
                xor         DX, DX
                mov         BX, HPC
                div         BX
                mov         CYL, AL
                mov         HEAD, DL
                ret	
    GetCHS endp
    

  • Mod

    Nun fehlt nur noch ein netter Algorithmus, der ES:BX und CHS sektorenweise berechnet, dann könnte man dies in einer Schleife für n Sektoren erledigen.

    Read-Algorithmus wurde von PorkChicken (LowLevel-Forum) vorgeschlagen.

    org 0x7C00  ; set up start address of bootloader
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; setup a stack and segment regs ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
        xor ax, ax
        mov ds, ax
        mov es, ax
        mov ax, 0x7000   ; stack address 
        mov ss, ax       
        xor sp, sp       ; set stackpointer to 0 
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;   read kernel from floppy disk                           ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    read_kernel: 
        mov si,loadmsg     
        call print_string
        mov [bootdrive], dl ; boot drive stored by BIOS in DL.
    
        ; di: number of sectors, bx: segment, ch, dh, cl: cylinder, head, sector
        mov bx, 500
    	mov di, bx 
    	mov bx, 0x0800 ; kernel start address (cf. linker script)
    	mov ch, 0
    	mov dh, 0
    	mov cl, 2
        call read_sectors
    
    ;;;;;;;;;;;;;
    ; A20-Gate  ;
    ;;;;;;;;;;;;;	
    
    switch_a20:
        in  al, 0x92      ; switch A20 gate via fast A20 port 92
        cmp al, 0xff      ; if it reads 0xFF, nothing's implemented on this port
        je .no_fast_A20
    
        or  al, 2         ; set A20_Gate_Bit (bit 1)
        and al, ~1        ; clear INIT_NOW bit (don't reset pc...)
        out 0x92, al
        jmp .A20_done
    
    .no_fast_A20:         ; no fast shortcut -> use the slow kbc...
        call empty_8042 
    
        mov al, 0xD1      ; kbc command: write to output port
        out 0x64, al
        call empty_8042
    
        mov al, 0xDF      ; writing this to kbc output port enables A20
        out 0x60, al
        call empty_8042
    
    .A20_done:
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Load GDT, switch to PM, and jump to kernel ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    load_gdt:	
        cli               ; clear interrupts
        lgdt [gdtr]       ; load GDT via GDTR (defined in file "gtd.inc")	
    
    switch_to_PM:	
        mov eax, cr0      ; switch-over to Protected Mode
        or  eax, 1        ; set bit 0 of CR0 register
        mov cr0, eax      ;	
    
    jmp_to_PM:
        jmp dword 08:0x00008000    ; this is a 16-bit-FAR-Jmp, but CS is loaded with "index" 8 in GDT 
                          ; hence, the code will be interpreted as 32 bit from here on
    					  ; the address can be found in the linker script 
    ;;;;;;;;;
    ; Calls ;
    ;;;;;;;;;
    
    print_string:
        mov ah, 0x0E      ; VGA BIOS fnct. 0x0E: teletype
    .loop:   
        lodsb             ; grab a byte from SI
        test al, al       ; NUL?
        jz .done          ; if the result is zero, get out
        int 0x10          ; otherwise, print out the character!
        jmp .loop
    .done:
        ret
    
    empty_8042:
        call Waitingloop
        in al, 0x64
        cmp al, 0xff      ; ... no real kbc at all?
        je .done
    
        test al, 1        ; something in input buffer?
        jz .no_output
        call Waitingloop
        in al, 0x60       ; yes: read buffer
        jmp empty_8042    ; and try again
    
    .no_output:
        test al, 2        ; command buffer empty?
        jnz empty_8042    ; no: we can't send anything new till it's empty
    .done:
        ret
    
    Waitingloop:                   
        mov cx,0xFFFF
    .loop_start:
        dec cx     
        jnz .loop_start
        ret       
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;    read sectors from floppy disk to buffer ES:BX in memory    ;
    ;                                                               ; 
    ;    input:                                                     ;
    ;    di         - number of sectors                             ;
    ;    bx         - segment                                       ;
    ;    ch, dh, cl - cylinder, head, sector                        ; 
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    read_sectors:
    .next:
        mov es, bx
        xor bx, bx
    
    .again:
        mov dl, [bootdrive]
        mov ax, 0x0201
        int 0x13
        jc .again
    
        inc cl
        cmp cl, 19
        jl .loop
        mov cl, 1
    
        inc dh
        cmp dh, 2
        jl .loop
        mov dh, 0
    
        inc ch
        cmp ch, 80
        jae .error
    
    .loop:
        mov bx, es
        add bx, 512/16
        sub di, 1
    	jnz .next
    
    .done:
        ret	
    
    .error
        mov si, errormsg   ; show error message
        call print_string
    .end
        jmp .end	
    
    ;;;;;;;;;;;;
    ; Includes ;
    ;;;;;;;;;;;;
    
    %include "gdt.inc"
    
    ;;;;;;;;
    ; data ;
    ;;;;;;;;
    
        bootdrive db 0    
        loadmsg db "bootloader message: loading kernel ...",13,10,0
        errormsg db "bootloader message: sector read error ...",13,10,0
    
        times 510-($-$$) hlt
        db 0x55
        db 0xAA
    

    Stack wurde von 0x10000 nach 0x7000 verlegt, weil er im Weg war.


Anmelden zum Antworten