Eigener Bootloader klappt noch nicht perfekt


  • Mod

    Für den über 63 Sektoren (32256 Bytes) hinaus wachsenden Kernel von PrettyOS ... http://www.henkessoft.de/OS_Dev/OS_Dev3.htm und
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-236354-and-start-is-540.html
    ... benötigte ich einen Bootloader, der auch große Kernel nachladen kann.

    Folgende boot.asm funktioniert soweit, dennoch bitte ich um weitere Verbesserungsvorschläge, um GRUB noch eine Weile zu entgehen (passt einfach nicht zu MS Windows 😉 ).

    Hauptproblem: Laufwerkslampe erlischt nicht, hört also nicht auf zu laden (oder was auch immer?).

    Frage: Liegt der Stack bei 0x7000 brauchbar (er wurde dorthin verlagert, damit man ggf. den Kernel auch bei 0x8000 nachladen kann).

    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, 100
    	mov di, bx 
    	mov bx, 0x1000 ; 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:0x00010000    ; 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:
    
    .reset_floppy:
        xor ax, ax        ; mov ax, 0  => function "reset"
        int 0x13           
        jc .reset_floppy  ; trouble? try again 
    
    .next:
        mov si, progressmsg   ; show progress
        call print_string
    	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
    	progressmsg db "*",0
    
        times 510-($-$$) hlt
        db 0x55
        db 0xAA
    

  • Mod

    Die Lösung für das Ausschalten des Motors und Lichtes habe ich inzwischen gefunden:
    http://lowlevel.brainsware.org/forum/index.php?PHPSESSID=e0d117469cb94ea64b66af28231d57fc&action=printpage;topic=983.0

    .done:
        push dx
        mov dx,0x3F2      ; switch off floppy disk motor
        mov al,0x0C
        out dx,al 
        pop dx
        ret
    

    Gibt es noch weitere Ideen zur Optimierung?


  • Mod

    Das wirklich Schwierige bei einem eigenen Bootloader ist, dass man bei einem Fehler beim Ausführen des OS nicht sicher sein kann, dass es am OS-Code liegt, denn es kann auch an der Diskette selbst bzw. am Schreib- oder Lesevorgang liegen.

    Welches Schreibprogramm würdet ihr - gesteuert aus makefile - empfehlen? Es sollte verifizieren, ob die Daten komplett auf den Sektoren angekommen sind.



  • Hast du den Start eigentlich mal mit einem USB-Stick versucht? Vielleicht gibt das einige Probleme weniger und es gibt ja sowieso kaum noch Leute, die Diskettenlaufwerke an ihrem Heim-PC haben.


  • Mod

    Hast du den Start eigentlich mal mit einem USB-Stick versucht?

    Nein, sollte ich mir wirklich mal anschauen, denn der USB-Stick ist heute die "Diskette".


  • Mod

    Da ich dem neuen Bootloader noch nicht "traue" und es bei einigen PC zu Problemen mit PrettyOS kommt, wollte ich zum Vergleich den alten einfachen Bootloader testen. Er lädt auch brav mit Bochs von Disk, startet aber nicht auf "real PC". Was habe ich vergessen?

    boot.asm:

    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
    	mov ss, ax       
        xor sp, sp       ; set stackpointer to 0 
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; read kernel from floppy disk ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
       ; mov si,loadmsg      ; show loading message
       ; call print_string	
    
        ;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,  0         ; 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_kernel     ; trouble? try again
    
    ;;;;;;;;;;;;;
    ; 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 0x8: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 (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    ; boot drive
        ;loadmsg db "bootloader message: loading kernel ...",13,10,0
    
        times 510-($-$$) hlt
        db 0x55
        db 0xAA
    

    gdt.inc:

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Global Descriptor Table (GDT) ;;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    NULL_Desc:
        dd    0
        dd    0
    
    CODE_Desc:
        dw    0xFFFF	    ; segment length  bits 0-15 ("limit")    
        dw    0		        ; segment base    byte 0,1      
        db    0		        ; segment base    byte 2    
        db    10011010b     ; access rights
        db    11001111b     ; bit 7-4: 4 flag bits:  granularity, default operation size bit, 2 bits available for OS
                            ; bit 3-0: segment length bits 16-19 
        db    0             ; segment base    byte 3    
    
    DATA_Desc:
        dw    0xFFFF        ; segment length  bits 0-15
        dw    0             ; segment base    byte 0,1
        db    0             ; segment base    byte 2
        db    10010010b     ; access rights
        db    11001111b     ; bit 7-4: 4 flag bits:  granularity, big bit (0=USE16-Segm., 1=USE32-Segm.), 2 bits avail.
                            ; bit 3-0: segment length bits 16-19
        db    0             ; segment base    byte 3       
    
    gdtr:
    Limit    dw 24          ; length of GDT
    Base     dd NULL_Desc   ; base of GDT ( linear address: RM Offset + Seg<<4 )
    

    kernel.ld:

    OUTPUT_FORMAT("binary")
    ENTRY(KernelStart)
    SECTIONS
    { 
      . = 0x8000;
      .text   : { __code_start = .;   *(EXCLUDE_FILE(shared_pages.o data.o).text) } 
      . = 0xc000;
      .text1  : { shared_pages.o(.text)                                    } 
      . = 0xd000;
      .text2  : { data.o(.text)                                            }
      .data   : { __data_start = .;   *(.data)                             } 	
      .rodata : { __rodata_start = .; *(.rodata)                           }
      .bss    : { __bss_start = .;    *(.bss) *(COMMON)                    } 
      __end = .;
    }
    

    Ist wichtig, dass ich das auf real PC zum Laufen bekomme, um sicher sein zu können, dass es nicht am neuen Bootloader-Algo für größere Kernel liegt.

    EDIT: Inzwischen habe ich heraus gefunden, dass der Bootloader funktioniert. Ein gewisser Zweifel bleibt immer, aber momentan liegen Fehler eher im Kernel.



  • Dieser Thread wurde von Moderator/in Nobuo T aus dem Forum Assembler in das Forum Projekt: OS-Development verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.


Log in to reply